From ba94afb7d0a5371f2d2de4232de56df34e8a1f99 Mon Sep 17 00:00:00 2001 From: Daniel Sanche Date: Wed, 16 Dec 2020 15:21:09 -0800 Subject: [PATCH] feat: support http_request field (#120) --- google/cloud/logging_v2/handlers/_helpers.py | 88 ++++++--- .../cloud/logging_v2/handlers/app_engine.py | 19 +- .../handlers/transports/background_thread.py | 36 +--- .../logging_v2/handlers/transports/base.py | 8 +- .../logging_v2/handlers/transports/sync.py | 15 +- tests/unit/handlers/test__helpers.py | 175 +++++++++++++++--- tests/unit/handlers/test_app_engine.py | 62 ++++--- .../transports/test_background_thread.py | 31 +--- tests/unit/handlers/transports/test_sync.py | 3 + 9 files changed, 283 insertions(+), 154 deletions(-) diff --git a/google/cloud/logging_v2/handlers/_helpers.py b/google/cloud/logging_v2/handlers/_helpers.py index 3150e46c..9821e95a 100644 --- a/google/cloud/logging_v2/handlers/_helpers.py +++ b/google/cloud/logging_v2/handlers/_helpers.py @@ -23,9 +23,14 @@ flask = None from google.cloud.logging_v2.handlers.middleware.request import _get_django_request +from google.logging.type.http_request_pb2 import HttpRequest _DJANGO_TRACE_HEADER = "HTTP_X_CLOUD_TRACE_CONTEXT" +_DJANGO_USERAGENT_HEADER = "HTTP_USER_AGENT" +_DJANGO_REMOTE_ADDR_HEADER = "REMOTE_ADDR" +_DJANGO_REFERER_HEADER = "HTTP_REFERER" _FLASK_TRACE_HEADER = "X_CLOUD_TRACE_CONTEXT" +_PROTOCOL_HEADER = "SERVER_PROTOCOL" def format_stackdriver_json(record, message): @@ -46,59 +51,86 @@ def format_stackdriver_json(record, message): return json.dumps(payload) -def get_trace_id_from_flask(): - """Get trace_id from flask request headers. +def get_request_data_from_flask(): + """Get http_request and trace data from flask request headers. Returns: - str: TraceID in HTTP request headers. + Tuple[Optional[google.logging.type.http_request_pb2.HttpRequest], Optional[str]]: + Data related to the current http request and the trace_id for the + request. Both fields will be None if a flask request isn't found. """ if flask is None or not flask.request: - return None + return None, None + + # build http_request + http_request = HttpRequest( + request_method=flask.request.method, + request_url=flask.request.url, + request_size=flask.request.content_length, + user_agent=flask.request.user_agent.string, + remote_ip=flask.request.remote_addr, + referer=flask.request.referrer, + protocol=flask.request.environ.get(_PROTOCOL_HEADER), + ) + # find trace id + trace_id = None header = flask.request.headers.get(_FLASK_TRACE_HEADER) + if header: + trace_id = header.split("/", 1)[0] - if header is None: - return None - - trace_id = header.split("/", 1)[0] - - return trace_id + return http_request, trace_id -def get_trace_id_from_django(): - """Get trace_id from django request headers. +def get_request_data_from_django(): + """Get http_request and trace data from django request headers. Returns: - str: TraceID in HTTP request headers. + Tuple[Optional[google.logging.type.http_request_pb2.HttpRequest], Optional[str]]: + Data related to the current http request and the trace_id for the + request. Both fields will be None if a django request isn't found. """ request = _get_django_request() if request is None: - return None + return None, None + # build http_request + http_request = HttpRequest( + request_method=request.method, + request_url=request.build_absolute_uri(), + request_size=len(request.body), + user_agent=request.META.get(_DJANGO_USERAGENT_HEADER), + remote_ip=request.META.get(_DJANGO_REMOTE_ADDR_HEADER), + referer=request.META.get(_DJANGO_REFERER_HEADER), + protocol=request.META.get(_PROTOCOL_HEADER), + ) + # find trace id + trace_id = None header = request.META.get(_DJANGO_TRACE_HEADER) - if header is None: - return None - - trace_id = header.split("/", 1)[0] + if header: + trace_id = header.split("/", 1)[0] - return trace_id + return http_request, trace_id -def get_trace_id(): - """Helper to get trace_id from web application request header. +def get_request_data(): + """Helper to get http_request and trace data from supported web + frameworks (currently supported: Flask and Django). Returns: - str: TraceID in HTTP request headers. + Tuple[Optional[google.logging.type.http_request_pb2.HttpRequest], Optional[str]]: + Data related to the current http request and the trace_id for the + request. Both fields will be None if a supported web request isn't found. """ checkers = ( - get_trace_id_from_django, - get_trace_id_from_flask, + get_request_data_from_django, + get_request_data_from_flask, ) for checker in checkers: - trace_id = checker() - if trace_id is not None: - return trace_id + http_request, trace_id = checker() + if http_request is not None: + return http_request, trace_id - return None + return None, None diff --git a/google/cloud/logging_v2/handlers/app_engine.py b/google/cloud/logging_v2/handlers/app_engine.py index fed9bd20..4d1fe808 100644 --- a/google/cloud/logging_v2/handlers/app_engine.py +++ b/google/cloud/logging_v2/handlers/app_engine.py @@ -21,7 +21,7 @@ import logging import os -from google.cloud.logging_v2.handlers._helpers import get_trace_id +from google.cloud.logging_v2.handlers._helpers import get_request_data from google.cloud.logging_v2.handlers.transports import BackgroundThreadTransport from google.cloud.logging_v2.resource import Resource @@ -96,7 +96,7 @@ def get_gae_labels(self): """ gae_labels = {} - trace_id = get_trace_id() + _, trace_id = get_request_data() if trace_id is not None: gae_labels[_TRACE_ID_LABEL] = trace_id @@ -114,11 +114,14 @@ def emit(self, record): """ message = super(AppEngineHandler, self).format(record) gae_labels = self.get_gae_labels() - trace_id = ( - "projects/%s/traces/%s" % (self.project_id, gae_labels[_TRACE_ID_LABEL]) - if _TRACE_ID_LABEL in gae_labels - else None - ) + http_request, trace_id = get_request_data() + if trace_id is not None: + trace_id = f"projects/{self.project_id}/traces/{trace_id}" self.transport.send( - record, message, resource=self.resource, labels=gae_labels, trace=trace_id + record, + message, + resource=self.resource, + labels=gae_labels, + trace=trace_id, + http_request=http_request, ) diff --git a/google/cloud/logging_v2/handlers/transports/background_thread.py b/google/cloud/logging_v2/handlers/transports/background_thread.py index 873fa452..3d654dbd 100644 --- a/google/cloud/logging_v2/handlers/transports/background_thread.py +++ b/google/cloud/logging_v2/handlers/transports/background_thread.py @@ -222,31 +222,21 @@ def _main_thread_terminated(self): file=sys.stderr, ) - def enqueue( - self, record, message, *, resource=None, labels=None, trace=None, span_id=None - ): + def enqueue(self, record, message, **kwargs): """Queues a log entry to be written by the background thread. Args: record (logging.LogRecord): Python log record that the handler was called with. message (str): The message from the ``LogRecord`` after being formatted by the associated log formatters. - resource (Optional[google.cloud.logging_v2.resource.Resource]): - Monitored resource of the entry - labels (Optional[dict]): Mapping of labels for the entry. - trace (Optional[str]): TraceID to apply to the logging entry. - span_id (Optional[str]): Span_id within the trace for the log entry. - Specify the trace parameter if span_id is set. + kwargs: Additional optional arguments for the logger """ queue_entry = { "info": {"message": message, "python_logger": record.name}, "severity": _helpers._normalize_severity(record.levelno), - "resource": resource, - "labels": labels, - "trace": trace, - "span_id": span_id, "timestamp": datetime.datetime.utcfromtimestamp(record.created), } + queue_entry.update(kwargs) self._queue.put_nowait(queue_entry) def flush(self): @@ -291,30 +281,16 @@ def __init__( ) self.worker.start() - def send( - self, record, message, resource=None, labels=None, trace=None, span_id=None - ): + def send(self, record, message, **kwargs): """Overrides Transport.send(). Args: record (logging.LogRecord): Python log record that the handler was called with. message (str): The message from the ``LogRecord`` after being formatted by the associated log formatters. - resource (Optional[google.cloud.logging_v2.resource.Resource]): - Monitored resource of the entry. - labels (Optional[dict]): Mapping of labels for the entry. - trace (Optional[str]): TraceID to apply to the logging entry. - span_id (Optional[str]): span_id within the trace for the log entry. - Specify the trace parameter if span_id is set. + kwargs: Additional optional arguments for the logger """ - self.worker.enqueue( - record, - message, - resource=resource, - labels=labels, - trace=trace, - span_id=span_id, - ) + self.worker.enqueue(record, message, **kwargs) def flush(self): """Submit any pending log records.""" diff --git a/google/cloud/logging_v2/handlers/transports/base.py b/google/cloud/logging_v2/handlers/transports/base.py index c94c7ad7..d60a5a07 100644 --- a/google/cloud/logging_v2/handlers/transports/base.py +++ b/google/cloud/logging_v2/handlers/transports/base.py @@ -22,18 +22,14 @@ class Transport(object): client and name object, and must override :meth:`send`. """ - def send( - self, record, message, *, resource=None, labels=None, trace=None, span_id=None - ): + def send(self, record, message, **kwargs): """Transport send to be implemented by subclasses. Args: record (logging.LogRecord): Python log record that the handler was called with. message (str): The message from the ``LogRecord`` after being formatted by the associated log formatters. - resource (Optional[google.cloud.logging_v2.resource.Resource]): - Monitored resource of the entry. - labels (Optional[dict]): Mapping of labels for the entry. + kwargs: Additional optional arguments for the logger """ raise NotImplementedError diff --git a/google/cloud/logging_v2/handlers/transports/sync.py b/google/cloud/logging_v2/handlers/transports/sync.py index 550c2939..35ee73da 100644 --- a/google/cloud/logging_v2/handlers/transports/sync.py +++ b/google/cloud/logging_v2/handlers/transports/sync.py @@ -30,9 +30,7 @@ class SyncTransport(Transport): def __init__(self, client, name): self.logger = client.logger(name) - def send( - self, record, message, *, resource=None, labels=None, trace=None, span_id=None - ): + def send(self, record, message, **kwargs): """Overrides transport.send(). Args: @@ -40,16 +38,9 @@ def send( Python log record that the handler was called with. message (str): The message from the ``LogRecord`` after being formatted by the associated log formatters. - resource (Optional[~logging_v2.resource.Resource]): - Monitored resource of the entry. - labels (Optional[dict]): Mapping of labels for the entry. + kwargs: Additional optional arguments for the logger """ info = {"message": message, "python_logger": record.name} self.logger.log_struct( - info, - severity=_helpers._normalize_severity(record.levelno), - resource=resource, - labels=labels, - trace=trace, - span_id=span_id, + info, severity=_helpers._normalize_severity(record.levelno), **kwargs, ) diff --git a/tests/unit/handlers/test__helpers.py b/tests/unit/handlers/test__helpers.py index 1fbf6c86..8fb37305 100644 --- a/tests/unit/handlers/test__helpers.py +++ b/tests/unit/handlers/test__helpers.py @@ -16,13 +16,18 @@ import mock +_FLASK_TRACE_ID = "flask-id" +_FLASK_HTTP_REQUEST = {"request_url": "https://flask.palletsprojects.com/en/1.1.x/"} +_DJANGO_TRACE_ID = "django-id" +_DJANGO_HTTP_REQUEST = {"request_url": "https://www.djangoproject.com/"} -class Test_get_trace_id_from_flask(unittest.TestCase): + +class Test_get_request_data_from_flask(unittest.TestCase): @staticmethod def _call_fut(): from google.cloud.logging_v2.handlers import _helpers - return _helpers.get_trace_id_from_flask() + return _helpers.get_request_data_from_flask() @staticmethod def create_app(): @@ -39,13 +44,14 @@ def index(): def test_no_context_header(self): app = self.create_app() with app.test_request_context(path="/", headers={}): - trace_id = self._call_fut() + http_request, trace_id = self._call_fut() self.assertIsNone(trace_id) + self.assertEqual(http_request.request_method, "GET") def test_valid_context_header(self): flask_trace_header = "X_CLOUD_TRACE_CONTEXT" - expected_trace_id = "testtraceidflask" + expected_trace_id = _FLASK_TRACE_ID flask_trace_id = expected_trace_id + "/testspanid" app = self.create_app() @@ -54,17 +60,57 @@ def test_valid_context_header(self): ) with context: - trace_id = self._call_fut() + http_request, trace_id = self._call_fut() self.assertEqual(trace_id, expected_trace_id) + self.assertEqual(http_request.request_method, "GET") + + def test_http_request_populated(self): + expected_path = "http://testserver/123" + expected_agent = "Mozilla/5.0" + expected_referrer = "self" + expected_ip = "10.1.2.3" + body_content = "test" + headers = { + "User-Agent": expected_agent, + "Referer": expected_referrer, + } + app = self.create_app() + with app.test_client() as c: + c.put( + path=expected_path, + data=body_content, + environ_base={"REMOTE_ADDR": expected_ip}, + headers=headers, + ) + http_request, trace_id = self._call_fut() + + self.assertEqual(http_request.request_method, "PUT") + self.assertEqual(http_request.request_url, expected_path) + self.assertEqual(http_request.user_agent, expected_agent) + self.assertEqual(http_request.referer, expected_referrer) + self.assertEqual(http_request.remote_ip, expected_ip) + self.assertEqual(http_request.request_size, len(body_content)) + self.assertEqual(http_request.protocol, "HTTP/1.1") + + def test_http_request_sparse(self): + expected_path = "http://testserver/123" + app = self.create_app() + with app.test_client() as c: + c.put(path=expected_path) + http_request, trace_id = self._call_fut() + self.assertEqual(http_request.request_method, "PUT") + self.assertEqual(http_request.request_url, expected_path) + self.assertEqual(http_request.protocol, "HTTP/1.1") -class Test_get_trace_id_from_django(unittest.TestCase): + +class Test_get_request_data_from_django(unittest.TestCase): @staticmethod def _call_fut(): from google.cloud.logging_v2.handlers import _helpers - return _helpers.get_trace_id_from_django() + return _helpers.get_request_data_from_django() def setUp(self): from django.conf import settings @@ -89,7 +135,8 @@ def test_no_context_header(self): middleware = request.RequestMiddleware(None) middleware.process_request(django_request) - trace_id = self._call_fut() + http_request, trace_id = self._call_fut() + self.assertEqual(http_request.request_method, "GET") self.assertIsNone(trace_id) def test_valid_context_header(self): @@ -106,61 +153,137 @@ def test_valid_context_header(self): middleware = request.RequestMiddleware(None) middleware.process_request(django_request) - trace_id = self._call_fut() + http_request, trace_id = self._call_fut() self.assertEqual(trace_id, expected_trace_id) + self.assertEqual(http_request.request_method, "GET") + + def test_http_request_populated(self): + from django.test import RequestFactory + from google.cloud.logging_v2.handlers.middleware import request + + expected_path = "http://testserver/123" + expected_agent = "Mozilla/5.0" + expected_referrer = "self" + body_content = "test" + django_request = RequestFactory().put( + expected_path, + data=body_content, + HTTP_USER_AGENT=expected_agent, + HTTP_REFERER=expected_referrer, + ) + + middleware = request.RequestMiddleware(None) + middleware.process_request(django_request) + http_request, trace_id = self._call_fut() + self.assertEqual(http_request.request_method, "PUT") + self.assertEqual(http_request.request_url, expected_path) + self.assertEqual(http_request.user_agent, expected_agent) + self.assertEqual(http_request.referer, expected_referrer) + self.assertEqual(http_request.remote_ip, "127.0.0.1") + self.assertEqual(http_request.request_size, len(body_content)) + self.assertEqual(http_request.protocol, "HTTP/1.1") + + def test_http_request_sparse(self): + from django.test import RequestFactory + from google.cloud.logging_v2.handlers.middleware import request + + expected_path = "http://testserver/123" + django_request = RequestFactory().put(expected_path) + middleware = request.RequestMiddleware(None) + middleware.process_request(django_request) + http_request, trace_id = self._call_fut() + self.assertEqual(http_request.request_method, "PUT") + self.assertEqual(http_request.request_url, expected_path) + self.assertEqual(http_request.remote_ip, "127.0.0.1") + self.assertEqual(http_request.protocol, "HTTP/1.1") -class Test_get_trace_id(unittest.TestCase): +class Test_get_request_data(unittest.TestCase): @staticmethod def _call_fut(): from google.cloud.logging_v2.handlers import _helpers - return _helpers.get_trace_id() + return _helpers.get_request_data() def _helper(self, django_return, flask_return): django_patch = mock.patch( - "google.cloud.logging_v2.handlers._helpers.get_trace_id_from_django", + "google.cloud.logging_v2.handlers._helpers.get_request_data_from_django", return_value=django_return, ) flask_patch = mock.patch( - "google.cloud.logging_v2.handlers._helpers.get_trace_id_from_flask", + "google.cloud.logging_v2.handlers._helpers.get_request_data_from_flask", return_value=flask_return, ) with django_patch as django_mock: with flask_patch as flask_mock: - trace_id = self._call_fut() + result = self._call_fut() - return django_mock, flask_mock, trace_id + return django_mock, flask_mock, result def test_from_django(self): - django_mock, flask_mock, trace_id = self._helper("test-django-trace-id", None) - self.assertEqual(trace_id, django_mock.return_value) + django_expected = (_DJANGO_HTTP_REQUEST, _DJANGO_TRACE_ID) + flask_expected = (None, None) + django_mock, flask_mock, output = self._helper(django_expected, flask_expected) + self.assertEqual(output, django_expected) django_mock.assert_called_once_with() flask_mock.assert_not_called() def test_from_flask(self): - django_mock, flask_mock, trace_id = self._helper(None, "test-flask-trace-id") - self.assertEqual(trace_id, flask_mock.return_value) + django_expected = (None, None) + flask_expected = (_FLASK_HTTP_REQUEST, _FLASK_TRACE_ID) + + django_mock, flask_mock, output = self._helper(django_expected, flask_expected) + self.assertEqual(output, flask_expected) django_mock.assert_called_once_with() flask_mock.assert_called_once_with() def test_from_django_and_flask(self): - django_mock, flask_mock, trace_id = self._helper( - "test-django-trace-id", "test-flask-trace-id" - ) + django_expected = (_DJANGO_HTTP_REQUEST, _DJANGO_TRACE_ID) + flask_expected = (_FLASK_HTTP_REQUEST, _FLASK_TRACE_ID) + + django_mock, flask_mock, output = self._helper(django_expected, flask_expected) + # Django wins. - self.assertEqual(trace_id, django_mock.return_value) + self.assertEqual(output, django_expected) django_mock.assert_called_once_with() flask_mock.assert_not_called() - def test_missing(self): - django_mock, flask_mock, trace_id = self._helper(None, None) - self.assertIsNone(trace_id) + def test_missing_http_request(self): + flask_expected = (None, _FLASK_TRACE_ID) + django_expected = (None, _DJANGO_TRACE_ID) + django_mock, flask_mock, output = self._helper(django_expected, flask_expected) + + # function only returns trace if http_request data is present + self.assertEqual(output, (None, None)) + + django_mock.assert_called_once_with() + flask_mock.assert_called_once_with() + + def test_missing_trace_id(self): + flask_expected = (_FLASK_HTTP_REQUEST, None) + django_expected = (None, _DJANGO_TRACE_ID) + django_mock, flask_mock, output = self._helper(django_expected, flask_expected) + + # trace_id is optional + self.assertEqual(output, flask_expected) + + django_mock.assert_called_once_with() + flask_mock.assert_called_once_with() + + def test_missing_both(self): + flask_expected = (None, None) + django_expected = (None, None) + django_mock, flask_mock, output = self._helper(django_expected, flask_expected) + self.assertEqual(output, (None, None)) django_mock.assert_called_once_with() flask_mock.assert_called_once_with() + + def test_wo_libraries(self): + output = self._call_fut() + self.assertEqual(output, (None, None)) diff --git a/tests/unit/handlers/test_app_engine.py b/tests/unit/handlers/test_app_engine.py index ea16e3c8..71672fa6 100644 --- a/tests/unit/handlers/test_app_engine.py +++ b/tests/unit/handlers/test_app_engine.py @@ -87,36 +87,54 @@ def test_constructor_w_gae_flex_env(self): self.assertIs(handler.stream, stream) def test_emit(self): - client = mock.Mock(project=self.PROJECT, spec=["project"]) - handler = self._make_one(client, transport=_Transport) - gae_resource = handler.get_gae_resource() - gae_labels = handler.get_gae_labels() - trace = None - logname = "app" - message = "hello world" - record = logging.LogRecord(logname, logging, None, None, message, None, None) - handler.emit(record) - - self.assertIs(handler.transport.client, client) - self.assertEqual(handler.transport.name, logname) - self.assertEqual( - handler.transport.send_called_with, - (record, message, gae_resource, gae_labels, trace), + expected_http_request = {"request_url": "test"} + trace_id = "trace-test" + expected_trace_id = f"projects/{self.PROJECT}/traces/{trace_id}" + get_request_patch = mock.patch( + "google.cloud.logging_v2.handlers.app_engine.get_request_data", + return_value=(expected_http_request, trace_id), ) + with get_request_patch: + # library integrations mocked to return test data + client = mock.Mock(project=self.PROJECT, spec=["project"]) + handler = self._make_one(client, transport=_Transport) + gae_resource = handler.get_gae_resource() + gae_labels = handler.get_gae_labels() + logname = "app" + message = "hello world" + record = logging.LogRecord( + logname, logging, None, None, message, None, None + ) + handler.project_id = self.PROJECT + handler.emit(record) + + self.assertIs(handler.transport.client, client) + self.assertEqual(handler.transport.name, logname) + self.assertEqual( + handler.transport.send_called_with, + ( + record, + message, + gae_resource, + gae_labels, + expected_trace_id, + expected_http_request, + ), + ) def _get_gae_labels_helper(self, trace_id): - get_trace_patch = mock.patch( - "google.cloud.logging_v2.handlers.app_engine.get_trace_id", - return_value=trace_id, + get_request_patch = mock.patch( + "google.cloud.logging_v2.handlers.app_engine.get_request_data", + return_value=(None, trace_id), ) client = mock.Mock(project=self.PROJECT, spec=["project"]) # The handler actually calls ``get_gae_labels()``. - with get_trace_patch as mock_get_trace: + with get_request_patch as mock_get_request: handler = self._make_one(client, transport=_Transport) gae_labels = handler.get_gae_labels() - self.assertEqual(mock_get_trace.mock_calls, [mock.call()]) + self.assertEqual(mock_get_request.mock_calls, [mock.call()]) return gae_labels @@ -138,5 +156,5 @@ def __init__(self, client, name): self.client = client self.name = name - def send(self, record, message, resource, labels, trace): - self.send_called_with = (record, message, resource, labels, trace) + def send(self, record, message, resource, labels, trace, http_request): + self.send_called_with = (record, message, resource, labels, trace, http_request) diff --git a/tests/unit/handlers/transports/test_background_thread.py b/tests/unit/handlers/transports/test_background_thread.py index e9626a75..5410c5f1 100644 --- a/tests/unit/handlers/transports/test_background_thread.py +++ b/tests/unit/handlers/transports/test_background_thread.py @@ -64,12 +64,7 @@ def test_send(self): transport.send(record, message, resource=_GLOBAL_RESOURCE) transport.worker.enqueue.assert_called_once_with( - record, - message, - resource=_GLOBAL_RESOURCE, - labels=None, - trace=None, - span_id=None, + record, message, resource=_GLOBAL_RESOURCE, ) def test_trace_send(self): @@ -91,12 +86,7 @@ def test_trace_send(self): transport.send(record, message, resource=_GLOBAL_RESOURCE, trace=trace) transport.worker.enqueue.assert_called_once_with( - record, - message, - resource=_GLOBAL_RESOURCE, - labels=None, - trace=trace, - span_id=None, + record, message, resource=_GLOBAL_RESOURCE, trace=trace, ) def test_span_send(self): @@ -118,12 +108,7 @@ def test_span_send(self): transport.send(record, message, resource=_GLOBAL_RESOURCE, span_id=span_id) transport.worker.enqueue.assert_called_once_with( - record, - message, - resource=_GLOBAL_RESOURCE, - labels=None, - trace=None, - span_id=span_id, + record, message, resource=_GLOBAL_RESOURCE, span_id=span_id, ) def test_flush(self): @@ -297,11 +282,12 @@ def test_enqueue_defaults(self): expected_info = {"message": message, "python_logger": "testing"} self.assertEqual(entry["info"], expected_info) self.assertEqual(entry["severity"], LogSeverity.INFO) - self.assertIsNone(entry["resource"]) - self.assertIsNone(entry["labels"]) - self.assertIsNone(entry["trace"]) - self.assertIsNone(entry["span_id"]) self.assertIsInstance(entry["timestamp"], datetime.datetime) + self.assertNotIn("resource", entry.keys()) + self.assertNotIn("labels", entry.keys()) + self.assertNotIn("trace", entry.keys()) + self.assertNotIn("span_id", entry.keys()) + self.assertNotIn("http_request", entry.keys()) def test_enqueue_explicit(self): import datetime @@ -503,6 +489,7 @@ def log_struct( trace=None, span_id=None, timestamp=None, + http_request=None, ): from google.cloud.logging_v2.logger import _GLOBAL_RESOURCE diff --git a/tests/unit/handlers/transports/test_sync.py b/tests/unit/handlers/transports/test_sync.py index 0ee6db22..9f064275 100644 --- a/tests/unit/handlers/transports/test_sync.py +++ b/tests/unit/handlers/transports/test_sync.py @@ -58,6 +58,7 @@ def test_send(self): None, None, None, + None, ) self.assertEqual(transport.logger.log_struct_called_with, EXPECTED_SENT) @@ -76,6 +77,7 @@ def log_struct( labels=None, trace=None, span_id=None, + http_request=None, ): self.log_struct_called_with = ( message, @@ -84,6 +86,7 @@ def log_struct( labels, trace, span_id, + http_request, )