8000 feat: support AuditLog and RequestLog protos (#274) · googleapis/python-logging@5d91be9 · GitHub
[go: up one dir, main page]

8000 Skip to content

Commit 5d91be9

Browse files
feat: support AuditLog and RequestLog protos (#274)
1 parent cda1e61 commit 5d91be9

File tree

4 files changed

+138
-23
lines changed

4 files changed

+138
-23
lines changed

google/cloud/logging_v2/entries.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@
2727
from google.cloud._helpers import _rfc3339_nanos_to_datetime
2828
from google.cloud._helpers import _datetime_to_rfc3339
2929

30+
# import officially supported proto definitions
31+
import google.cloud.audit.audit_log_pb2 # noqa: F401
32+
import google.cloud.appengine_logging # noqa: F401
3033

3134
_GLOBAL_RESOURCE = Resource(type="global", labels={})
3235

@@ -316,13 +319,18 @@ def payload_pb(self):
316319

317320
@property
318321
def payload_json(self):
319-
if not isinstance(self.payload, Any):
322+
if isinstance(self.payload, collections.abc.Mapping):
320323
return self.payload
321324

322325
def to_api_repr(self):
323326
"""API repr (JSON format) for entry."""
324327
info = super(ProtobufEntry, self).to_api_repr()
325-
info["protoPayload"] = MessageToDict(self.payload)
328+
proto_payload = None
329+
if self.payload_json:
330+
proto_payload = dict(self.payload_json)
331+
elif self.payload_pb:
332+
proto_payload = MessageToDict(self.payload_pb)
333+
info["protoPayload"] = proto_payload
326334
return info
327335

328336
def parse_message(self, message):

setup.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@
3030
release_status = "Development Status :: 5 - Production/Stable"
3131
dependencies = [
3232
"google-api-core[grpc] >= 1.22.2, < 2.0.0dev",
33+
"google-cloud-appengine-logging >= 0.1.0, < 1.0.0dev",
34+
"google-cloud-audit-log >= 0.1.0, < 1.0.0dev",
3335
"google-cloud-core >= 1.4.1, < 2.0dev",
3436
"proto-plus >= 1.11.0",
3537
"packaging >= 14.3",

tests/system/test_system.py

Lines changed: 112 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,11 @@
1616
from datetime import timedelta
1717
from datetime import timezone
1818
import logging
19+
import numbers
1920
import os
2021
import pytest
2122
import unittest
23+
import uuid
2224

2325
from google.api_core.exceptions import BadGateway
2426
from google.api_core.exceptions import Conflict
@@ -36,6 +38,8 @@
3638
from google.cloud.logging_v2 import client
3739
from google.cloud.logging_v2.resource import Resource
3840

41+
from google.protobuf.struct_pb2 import Struct, Value, ListValue, NullValue
42+
3943
from test_utils.retry import RetryErrors
4044
from test_utils.retry import RetryResult
4145
from test_utils.system import unique_resource_id
@@ -142,32 +146,119 @@ def tearDown(self):
142146
def _logger_name(prefix):
143147
return prefix + unique_resource_id("-")
144148

145-
def test_list_entry_with_unregistered(self):
146-
from google.protobuf import any_pb2
149+
@staticmethod
150+
def _to_value(data):
151+
if data is None:
152+
return Value(null_value=NullValue.NULL_VALUE)
153+
elif isinstance(data, numbers.Number):
154+
return Value(number_value=data)
155+
elif isinstance(data, str):
156+
return Value(string_value=data)
157+
elif isinstance(data, bool):
158+
return Value(bool_value=data)
159+
elif isinstance(data, (list, tuple, set)):
160+
return Value(
161+
list_value=ListValue(values=(TestLogging._to_value(e) for e in data))
162+
)
163+
elif isinstance(data, dict):
164+
return Value(struct_value=TestLogging._dict_to_struct(data))
165+
else:
166+
raise TypeError("Unknown data type: %r" % type(data))
167+
168+
@staticmethod
169+
def _dict_to_struct(data):
170+
return Struct(fields={k: TestLogging._to_value(v) for k, v in data.items()})
171+
172+
def test_list_entry_with_auditlog(self):
173+
"""
174+
Test emitting and listing logs containing a google.cloud.audit.AuditLog proto message
175+
"""
147176
from google.protobuf import descriptor_pool
148177
from google.cloud.logging_v2 import entries
149178

150179
pool = descriptor_pool.Default()
151180
type_name = "google.cloud.audit.AuditLog"
152-
# Make sure the descriptor is not known in the registry.
153-
with self.assertRaises(KeyError):
154-
pool.FindMessageTypeByName(type_name)
155-
156181
type_url = "type.googleapis.com/" + type_name
157-
filter_ = self.TYPE_FILTER.format(type_url) + f" AND {_time_filter}"
158-
entry_iter = iter(Config.CLIENT.list_entries(page_size=1, filter_=filter_))
182+
# Make sure the descriptor is known in the registry.
183+
# Raises KeyError if unknown
184+
pool.FindMessageTypeByName(type_name)
185+
186+
# create log
187+
audit_dict = {
188+
"@type": type_url,
189+
"methodName": "test",
190+
"requestMetadata": {"callerIp": "::1", "callerSuppliedUserAgent": "test"},
191+
"resourceName": "test",
192+
"serviceName": "test",
193+
"status": {"code": 0},
194+
}
195+
audit_struct = self._dict_to_struct(audit_dict)
196+
197+
logger = Config.CLIENT.logger(f"audit-proto-{uuid.uuid1()}")
198+
logger.log_proto(audit_struct)
199+
200+
# retrieve log
201+
retry = RetryErrors((TooManyRequests, StopIteration), max_tries=8)
202+
protobuf_entry = retry(lambda: next(logger.list_entries()))()
159203

160-
retry = RetryErrors(TooManyRequests)
161-
protobuf_entry = retry(lambda: next(entry_iter))()
204+
self.assertIsInstance(protobuf_entry, entries.ProtobufEntry)
205+
self.assertIsNone(protobuf_entry.payload_pb)
206+
self.assertIsInstance(protobuf_entry.payload_json, dict)
207+
self.assertEqual(protobuf_entry.payload_json["@type"], type_url)
208+
self.assertEqual(
209+
protobuf_entry.payload_json["methodName"], audit_dict["methodName"]
210+
)
211+
self.assertEqual(
212+
protobuf_entry.to_api_repr()["protoPayload"]["@type"], type_url
213+
)
214+
self.assertEqual(
215+
protobuf_entry.to_api_repr()["protoPayload"]["methodName"],
216+
audit_dict["methodName"],
217+
)
218+
219+
def test_list_entry_with_requestlog(self):
220+
"""
221+
Test emitting and listing logs containing a google.appengine.logging.v1.RequestLog proto message
222+
"""
223+
from google.protobuf import descriptor_pool
224+
from google.cloud.logging_v2 import entries
225+
226+
pool = descriptor_pool.Default()
227+
type_name = "google.appengine.logging.v1.RequestLog"
228+
type_url = "type.googleapis.com/" + type_name
229+
# Make sure the descriptor is known in the registry.
230+
# Raises KeyError if unknown
231+
pool.FindMessageTypeByName(type_name)
232+
233+
# create log
234+
req_dict = {
235+
"@type": type_url,
236+
"ip": "0.0.0.0",
237+
"appId": "test",
238+
"versionId": "test",
239+
"requestId": "12345",
240+
"latency": "500.0s",
241+
"method": "GET",
242+
"status": 500,
243+
"resource": "test",
244+
"httpVersion": "HTTP/1.1",
245+
}
246+
req_struct = self._dict_to_struct(req_dict)
247+
248+
logger = Config.CLIENT.logger(f"req-proto-{uuid.uuid1()}")
249+
logger.log_proto(req_struct)
250+
251+
# retrieve log
252+
retry = RetryErrors((TooManyRequests, StopIteration), max_tries=8)
253+
protobuf_entry = retry(lambda: next(logger.list_entries()))()
162254

163255
self.assertIsInstance(protobuf_entry, entries.ProtobufEntry)
164-
if Config.CLIENT._use_grpc:
165-
self.assertIsNone(protobuf_entry.payload_json)
166-
self.assertIsInstance(protobuf_entry.payload_pb, any_pb2.Any)
167-
self.assertEqual(protobuf_entry.payload_pb.type_url, type_url)
168-
else:
169-
self.assertIsNone(protobuf_entry.payload_pb)
170-
self.assertEqual(protobuf_entry.payload_json["@type"], type_url)
256+
self.assertIsNone(protobuf_entry.payload_pb)
257+
self.assertIsInstance(protobuf_entry.payload_json, dict)
258+
self.assertEqual(protobuf_entry.payload_json["@type"], type_url)
259+
self.assertEqual(
260+
protobuf_entry.to_api_repr()["protoPayload"]["@type"], type_url
261+
)
171262

172263
def test_log_text(self):
173264
TEXT_PAYLOAD = "System test: test_log_text"
@@ -288,7 +379,7 @@ def test_log_handler_async(self):
288379

289380
cloud_logger = logging.getLogger(handler.name)
290381
cloud_logger.addHandler(handler)
291-
cloud_logger.warn(LOG_MESSAGE)
382+
cloud_logger.warning(LOG_MESSAGE)
292383
handler.flush()
293384
entries = _list_entries(logger)
294385
expected_payload = {"message": LOG_MESSAGE, "python_logger": handler.name}
@@ -310,7 +401,7 @@ def test_log_handler_sync(self):
310401
LOGGER_NAME = "mylogger"
311402
cloud_logger = logging.getLogger(LOGGER_NAME)
312403
cloud_logger.addHandler(handler)
313-
cloud_logger.warn(LOG_MESSAGE)
404+
cloud_logger.warning(LOG_MESSAGE)
314405

315406
entries = _list_entries(logger)
316407
expected_payload = {"message": LOG_MESSAGE, "python_logger": LOGGER_NAME}
@@ -342,7 +433,7 @@ def test_handlers_w_extras(self):
342433
"resource": Resource(type="cloudiot_device", labels={}),
343434
"labels": {"test-label": "manual"},
344435
}
345-
cloud_logger.warn(LOG_MESSAGE, extra=extra)
436+
cloud_logger.warning(LOG_MESSAGE, extra=extra)
346437

347438
entries = _list_entries(logger)
348439
self.assertEqual(len(entries), 1)
@@ -363,7 +454,7 @@ def test_log_root_handler(self):
363454
self.to_delete.append(logger)
364455

365456
google.cloud.logging.handlers.handlers.setup_logging(handler)
366-
logging.warn(LOG_MESSAGE)
457+
logging.warning(LOG_MESSAGE)
367458

368459
entries = _list_entries(logger)
369460
expected_payload = {"message": LOG_MESSAGE, "python_logger": "root"}

tests/unit/test_entries.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -503,6 +503,20 @@ def test_to_api_repr_defaults(self):
503503
}
504504
self.assertEqual(entry.to_api_repr(), expected)
505505

506+
def test_to_api_repr_struct(self):
507+
from google.protobuf.struct_pb2 import Struct, Value
508+
from google.cloud.logging_v2.logger import _GLOBAL_RESOURCE
509+
510+
LOG_NAME = "struct.log"
511+
message = Struct(fields={"foo": Value(bool_value=True)})
512+
entry = self._make_one(log_name=LOG_NAME, payload=message)
513+
expected = {
514+
"logName": LOG_NAME,
515+
"jsonPayload": message,
516+
"resource": _GLOBAL_RESOURCE._to_dict(),
517+
}
518+
self.assertEqual(entry.to_api_repr(), expected)
519+
506520
def test_to_api_repr_explicit(self):
507521
import datetime
508522
from google.cloud.logging import Resource

0 commit comments

Comments
 (0)
0