/
snippets.py
434 lines (332 loc) · 12.4 KB
/
snippets.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
# Copyright 2016 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Testable usage examples for Cloud Logging API wrapper
Each example function takes a ``client`` argument (which must be an instance
of :class:`google.cloud.logging.client.Client`) and uses it to perform a task
with the API.
To facilitate running the examples as system tests, each example is also passed
a ``to_delete`` list; the function adds to the list any objects created which
need to be deleted during teardown.
"""
import time
from google.cloud.logging.client import Client
def snippet(func):
"""Mark ``func`` as a snippet example function."""
func._snippet = True
return func
def _millis():
return time.time() * 1000
def do_something_with(item): # pylint: disable=unused-argument
pass
# pylint: disable=reimported,unused-variable,unused-argument
@snippet
def instantiate_client(_unused_client, _unused_to_delete):
"""Instantiate client."""
# [START client_create_default]
from google.cloud import logging
client = logging.Client()
# [END client_create_default]
credentials = object()
# [START client_create_explicit]
from google.cloud import logging
client = logging.Client(project="my-project", credentials=credentials)
# [END client_create_explicit]
# pylint: enable=reimported,unused-variable,unused-argument
@snippet
def client_list_entries(client, to_delete): # pylint: disable=unused-argument
"""List entries via client."""
# [START client_list_entries_default]
for entry in client.list_entries(): # API call(s)
do_something_with(entry)
# [END client_list_entries_default]
# [START client_list_entries_filter]
FILTER = "logName:log_name AND textPayload:simple"
for entry in client.list_entries(filter_=FILTER): # API call(s)
do_something_with(entry)
# [END client_list_entries_filter]
# [START client_list_entries_order_by]
from google.cloud.logging import DESCENDING
for entry in client.list_entries(order_by=DESCENDING): # API call(s)
do_something_with(entry)
# [END client_list_entries_order_by]
# [START client_list_entries_paged]
iterator = client.list_entries()
pages = iterator.pages
page1 = next(pages) # API call
for entry in page1:
do_something_with(entry)
page2 = next(pages) # API call
for entry in page2:
do_something_with(entry)
# [END client_list_entries_paged]
# @snippet Commented because we need real project IDs to test
def client_list_entries_multi_project(
client, to_delete
): # pylint: disable=unused-argument
"""List entries via client across multiple projects."""
# [START client_list_entries_multi_project]
PROJECT_IDS = ["one-project", "another-project"]
for entry in client.list_entries(projects=PROJECT_IDS): # API call(s)
do_something_with(entry)
# [END client_list_entries_multi_project]
@snippet
def logger_usage(client, to_delete):
"""Logger usage."""
LOG_NAME = "logger_usage_%d" % (_millis())
# [START logger_create]
logger = client.logger(LOG_NAME)
# [END logger_create]
to_delete.append(logger)
# [START logger_log_text]
logger.log_text("A simple entry") # API call
# [END logger_log_text]
# [START logger_log_struct]
logger.log_struct(
{"message": "My second entry", "weather": "partly cloudy"}
) # API call
# [END logger_log_struct]
# [START logger_log_resource_text]
from google.cloud.logging.resource import Resource
res = Resource(
type="generic_node",
labels={
"location": "us-central1-a",
"namespace": "default",
"node_id": "10.10.10.1",
},
)
logger.log_struct(
{"message": "My first entry", "weather": "partly cloudy"}, resource=res
)
# [END logger_log_resource_text]
# [START logger_list_entries]
from google.cloud.logging import DESCENDING
for entry in logger.list_entries(order_by=DESCENDING): # API call(s)
do_something_with(entry)
# [END logger_list_entries]
def _logger_delete():
# [START logger_delete]
logger.delete() # API call
# [END logger_delete]
_backoff_not_found(_logger_delete)
to_delete.remove(logger)
@snippet
def metric_crud(client, to_delete):
"""Metric CRUD."""
METRIC_NAME = "robots-%d" % (_millis(),)
DESCRIPTION = "Robots all up in your server"
FILTER = "logName:apache-access AND textPayload:robot"
UPDATED_FILTER = "textPayload:robot"
UPDATED_DESCRIPTION = "Danger, Will Robinson!"
# [START client_list_metrics]
for metric in client.list_metrics(): # API call(s)
do_something_with(metric)
# [END client_list_metrics]
# [START metric_create]
metric = client.metric(METRIC_NAME, filter_=FILTER, description=DESCRIPTION)
assert not metric.exists() # API call
metric.create() # API call
assert metric.exists() # API call
# [END metric_create]
to_delete.append(metric)
# [START metric_reload]
existing_metric = client.metric(METRIC_NAME)
existing_metric.reload() # API call
# [END metric_reload]
assert existing_metric.filter_ == FILTER
assert existing_metric.description == DESCRIPTION
# [START metric_update]
existing_metric.filter_ = UPDATED_FILTER
existing_metric.description = UPDATED_DESCRIPTION
existing_metric.update() # API call
# [END metric_update]
existing_metric.reload()
assert existing_metric.filter_ == UPDATED_FILTER
assert existing_metric.description == UPDATED_DESCRIPTION
def _metric_delete():
# [START metric_delete]
metric.delete()
# [END metric_delete]
_backoff_not_found(_metric_delete)
to_delete.remove(metric)
def _sink_storage_setup(client):
from google.cloud import storage
BUCKET_NAME = "sink-storage-%d" % (_millis(),)
client = storage.Client()
bucket = client.bucket(BUCKET_NAME)
bucket.create()
# [START sink_bucket_permissions]
bucket.acl.reload() # API call
logs_group = bucket.acl.group("cloud-logs@google.com")
logs_group.grant_owner()
bucket.acl.add_entity(logs_group)
bucket.acl.save() # API call
# [END sink_bucket_permissions]
return bucket
@snippet
def sink_storage(client, to_delete):
"""Sink log entries to storage."""
bucket = _sink_storage_setup(client)
to_delete.append(bucket)
SINK_NAME = "robots-storage-%d" % (_millis(),)
FILTER = "textPayload:robot"
# [START sink_storage_create]
DESTINATION = "storage.googleapis.com/%s" % (bucket.name,)
sink = client.sink(SINK_NAME, filter_=FILTER, destination=DESTINATION)
assert not sink.exists() # API call
sink.create() # API call
assert sink.exists() # API call
# [END sink_storage_create]
to_delete.insert(0, sink) # delete sink before bucket
def _sink_bigquery_setup(client):
from google.cloud import bigquery
DATASET_NAME = "sink_bigquery_%d" % (_millis(),)
client = bigquery.Client()
dataset = client.dataset(DATASET_NAME)
dataset.create()
dataset.reload()
# [START sink_dataset_permissions]
from google.cloud.bigquery.dataset import AccessGrant
grants = dataset.access_grants
grants.append(AccessGrant("WRITER", "groupByEmail", "cloud-logs@google.com"))
dataset.access_grants = grants
dataset.update() # API call
# [END sink_dataset_permissions]
return dataset
@snippet
def sink_bigquery(client, to_delete):
"""Sink log entries to bigquery."""
dataset = _sink_bigquery_setup(client)
to_delete.append(dataset)
SINK_NAME = "robots-bigquery-%d" % (_millis(),)
FILTER = "textPayload:robot"
# [START sink_bigquery_create]
DESTINATION = "bigquery.googleapis.com%s" % (dataset.path,)
sink = client.sink(SINK_NAME, filter_=FILTER, destination=DESTINATION)
assert not sink.exists() # API call
sink.create() # API call
assert sink.exists() # API call
# [END sink_bigquery_create]
to_delete.insert(0, sink) # delete sink before dataset
def _sink_pubsub_setup(client):
from google.cloud import pubsub
TOPIC_NAME = "sink-pubsub-%d" % (_millis(),)
client = pubsub.Client()
topic = client.topic(TOPIC_NAME)
topic.create()
# [START sink_topic_permissions]
policy = topic.get_iam_policy() # API call
policy.owners.add(policy.group("cloud-logs@google.com"))
topic.set_iam_policy(policy) # API call
# [END sink_topic_permissions]
return topic
@snippet
def sink_pubsub(client, to_delete):
"""Sink log entries to pubsub."""
topic = _sink_pubsub_setup(client)
to_delete.append(topic)
SINK_NAME = "robots-pubsub-%d" % (_millis(),)
FILTER = "logName:apache-access AND textPayload:robot"
UPDATED_FILTER = "textPayload:robot"
# [START sink_pubsub_create]
DESTINATION = "pubsub.googleapis.com/%s" % (topic.full_name,)
sink = client.sink(SINK_NAME, filter_=FILTER, destination=DESTINATION)
assert not sink.exists() # API call
sink.create() # API call
assert sink.exists() # API call
# [END sink_pubsub_create]
to_delete.insert(0, sink) # delete sink before topic
# [START client_list_sinks]
for sink in client.list_sinks(): # API call(s)
do_something_with(sink)
# [END client_list_sinks]
# [START sink_reload]
existing_sink = client.sink(SINK_NAME)
existing_sink.reload()
# [END sink_reload]
assert existing_sink.filter_ == FILTER
assert existing_sink.destination == DESTINATION
# [START sink_update]
existing_sink.filter_ = UPDATED_FILTER
existing_sink.update()
# [END sink_update]
existing_sink.reload()
assert existing_sink.filter_ == UPDATED_FILTER
# [START sink_delete]
sink.delete()
# [END sink_delete]
to_delete.pop(0)
@snippet
def logging_handler(client):
# [START create_default_handler]
import logging
handler = client.get_default_handler()
cloud_logger = logging.getLogger("cloudLogger")
cloud_logger.setLevel(logging.INFO)
cloud_logger.addHandler(handler)
cloud_logger.error("bad news")
# [END create_default_handler]
# [START create_cloud_handler]
from google.cloud.logging.handlers import CloudLoggingHandler
handler = CloudLoggingHandler(client)
cloud_logger = logging.getLogger("cloudLogger")
cloud_logger.setLevel(logging.INFO)
cloud_logger.addHandler(handler)
cloud_logger.error("bad news")
# [END create_cloud_handler]
# [START create_named_handler]
handler = CloudLoggingHandler(client, name="mycustomlog")
# [END create_named_handler]
@snippet
def setup_logging(client):
import logging
# [START setup_logging]
client.setup_logging(log_level=logging.INFO)
# [END setup_logging]
# [START setup_logging_excludes]
client.setup_logging(log_level=logging.INFO, excluded_loggers=("werkzeug",))
# [END setup_logging_excludes]
def _line_no(func):
return func.__code__.co_firstlineno
def _find_examples():
funcs = [obj for obj in globals().values() if getattr(obj, "_snippet", False)]
for func in sorted(funcs, key=_line_no):
yield func
def _name_and_doc(func):
return func.__name__, func.__doc__
def _backoff_not_found(deleter):
from google.cloud.exceptions import NotFound
timeouts = [1, 2, 4, 8, 16]
while timeouts:
try:
deleter()
except NotFound:
time.sleep(timeouts.pop(0))
else:
break
def main():
client = Client()
for example in _find_examples():
to_delete = []
print("%-25s: %s" % _name_and_doc(example))
try:
example(client, to_delete)
except AssertionError as failure:
print(" FAIL: %s" % (failure,))
except Exception as error: # pylint: disable=broad-except
print(" ERROR: %r" % (error,))
for item in to_delete:
_backoff_not_found(item.delete)
if __name__ == "__main__":
main()