Skip to content

Commit ba94afb

Browse files
feat: support http_request field (#120)
1 parent 2a36af6 commit ba94afb

File tree

9 files changed

+283
-154
lines changed

9 files changed

+283
-154
lines changed

google/cloud/logging_v2/handlers/_helpers.py

+60-28
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,14 @@
2323
flask = None
2424

2525
from google.cloud.logging_v2.handlers.middleware.request import _get_django_request
26+
from google.logging.type.http_request_pb2 import HttpRequest
2627

2728
_DJANGO_TRACE_HEADER = "HTTP_X_CLOUD_TRACE_CONTEXT"
29+
_DJANGO_USERAGENT_HEADER = "HTTP_USER_AGENT"
30+
_DJANGO_REMOTE_ADDR_HEADER = "REMOTE_ADDR"
31+
_DJANGO_REFERER_HEADER = "HTTP_REFERER"
2832
_FLASK_TRACE_HEADER = "X_CLOUD_TRACE_CONTEXT"
33+
_PROTOCOL_HEADER = "SERVER_PROTOCOL"
2934

3035

3136
def format_stackdriver_json(record, message):
@@ -46,59 +51,86 @@ def format_stackdriver_json(record, message):
4651
return json.dumps(payload)
4752

4853

49-
def get_trace_id_from_flask():
50-
"""Get trace_id from flask request headers.
54+
def get_request_data_from_flask():
55+
"""Get http_request and trace data from flask request headers.
5156
5257
Returns:
53-
str: TraceID in HTTP request headers.
58+
Tuple[Optional[google.logging.type.http_request_pb2.HttpRequest], Optional[str]]:
59+
Data related to the current http request and the trace_id for the
60+
request. Both fields will be None if a flask request isn't found.
5461
"""
5562
if flask is None or not flask.request:
56-
return None
63+
return None, None
64+
65+
# build http_request
66+
http_request = HttpRequest(
67+
request_method=flask.request.method,
68+
request_url=flask.request.url,
69+
request_size=flask.request.content_length,
70+
user_agent=flask.request.user_agent.string,
71+
remote_ip=flask.request.remote_addr,
72+
referer=flask.request.referrer,
73+
protocol=flask.request.environ.get(_PROTOCOL_HEADER),
74+
)
5775

76+
# find trace id
77+
trace_id = None
5878
header = flask.request.headers.get(_FLASK_TRACE_HEADER)
79+
if header:
80+
trace_id = header.split("/", 1)[0]
5981

60-
if header is None:
61-
return None
62-
63-
trace_id = header.split("/", 1)[0]
64-
65-
return trace_id
82+
return http_request, trace_id
6683

6784

68-
def get_trace_id_from_django():
69-
"""Get trace_id from django request headers.
85+
def get_request_data_from_django():
86+
"""Get http_request and trace data from django request headers.
7087
7188
Returns:
72-
str: TraceID in HTTP request headers.
89+
Tuple[Optional[google.logging.type.http_request_pb2.HttpRequest], Optional[str]]:
90+
Data related to the current http request and the trace_id for the
91+
request. Both fields will be None if a django request isn't found.
7392
"""
7493
request = _get_django_request()
7594

7695
if request is None:
77-
return None
96+
return None, None
97+
# build http_request
98+
http_request = HttpRequest(
99+
request_method=request.method,
100+
request_url=request.build_absolute_uri(),
101+
request_size=len(request.body),
102+
user_agent=request.META.get(_DJANGO_USERAGENT_HEADER),
103+
remote_ip=request.META.get(_DJANGO_REMOTE_ADDR_HEADER),
104+
referer=request.META.get(_DJANGO_REFERER_HEADER),
105+
protocol=request.META.get(_PROTOCOL_HEADER),
106+
)
78107

108+
# find trace id
109+
trace_id = None
79110
header = request.META.get(_DJANGO_TRACE_HEADER)
80-
if header is None:
81-
return None
82-
83-
trace_id = header.split("/", 1)[0]
111+
if header:
112+
trace_id = header.split("/", 1)[0]
84113

85-
return trace_id
114+
return http_request, trace_id
86115

87116

88-
def get_trace_id():
89-
"""Helper to get trace_id from web application request header.
117+
def get_request_data():
118+
"""Helper to get http_request and trace data from supported web
119+
frameworks (currently supported: Flask and Django).
90120
91121
Returns:
92-
str: TraceID in HTTP request headers.
122+
Tuple[Optional[google.logging.type.http_request_pb2.HttpRequest], Optional[str]]:
123+
Data related to the current http request and the trace_id for the
124+
request. Both fields will be None if a supported web request isn't found.
93125
"""
94126
checkers = (
95-
get_trace_id_from_django,
96-
get_trace_id_from_flask,
127+
get_request_data_from_django,
128+
get_request_data_from_flask,
97129
)
98130

99131
for checker in checkers:
100-
trace_id = checker()
101-
if trace_id is not None:
102-
return trace_id
132+
http_request, trace_id = checker()
133+
if http_request is not None:
134+
return http_request, trace_id
103135

104-
return None
136+
return None, None

google/cloud/logging_v2/handlers/app_engine.py

+11-8
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
import logging
2222
import os
2323

24-
from google.cloud.logging_v2.handlers._helpers import get_trace_id
24+
from google.cloud.logging_v2.handlers._helpers import get_request_data
2525
from google.cloud.logging_v2.handlers.transports import BackgroundThreadTransport
2626
from google.cloud.logging_v2.resource import Resource
2727

@@ -96,7 +96,7 @@ def get_gae_labels(self):
9696
"""
9797
gae_labels = {}
9898

99-
trace_id = get_trace_id()
99+
_, trace_id = get_request_data()
100100
if trace_id is not None:
101101
gae_labels[_TRACE_ID_LABEL] = trace_id
102102

@@ -114,11 +114,14 @@ def emit(self, record):
114114
"""
115115
message = super(AppEngineHandler, self).format(record)
116116
gae_labels = self.get_gae_labels()
117-
trace_id = (
118-
"projects/%s/traces/%s" % (self.project_id, gae_labels[_TRACE_ID_LABEL])
119-
if _TRACE_ID_LABEL in gae_labels
120-
else None
121-
)
117+
http_request, trace_id = get_request_data()
118+
if trace_id is not None:
119+
trace_id = f"projects/{self.project_id}/traces/{trace_id}"
122120
self.transport.send(
123-
record, message, resource=self.resource, labels=gae_labels, trace=trace_id
121+
record,
122+
message,
123+
resource=self.resource,
124+
labels=gae_labels,
125+
trace=trace_id,
126+
http_request=http_request,
124127
)

google/cloud/logging_v2/handlers/transports/background_thread.py

+6-30
Original file line numberDiff line numberDiff line change
@@ -222,31 +222,21 @@ def _main_thread_terminated(self):
222222
file=sys.stderr,
223223
)
224224

225-
def enqueue(
226-
self, record, message, *, resource=None, labels=None, trace=None, span_id=None
227-
):
225+
def enqueue(self, record, message, **kwargs):
228226
"""Queues a log entry to be written by the background thread.
229227
230228
Args:
231229
record (logging.LogRecord): Python log record that the handler was called with.
232230
message (str): The message from the ``LogRecord`` after being
233231
formatted by the associated log formatters.
234-
resource (Optional[google.cloud.logging_v2.resource.Resource]):
235-
Monitored resource of the entry
236-
labels (Optional[dict]): Mapping of labels for the entry.
237-
trace (Optional[str]): TraceID to apply to the logging entry.
238-
span_id (Optional[str]): Span_id within the trace for the log entry.
239-
Specify the trace parameter if span_id is set.
232+
kwargs: Additional optional arguments for the logger
240233
"""
241234
queue_entry = {
242235
"info": {"message": message, "python_logger": record.name},
243236
"severity": _helpers._normalize_severity(record.levelno),
244-
"resource": resource,
245-
"labels": labels,
246-
"trace": trace,
247-
"span_id": span_id,
248237
"timestamp": datetime.datetime.utcfromtimestamp(record.created),
249238
}
239+
queue_entry.update(kwargs)
250240
self._queue.put_nowait(queue_entry)
251241

252242
def flush(self):
@@ -291,30 +281,16 @@ def __init__(
291281
)
292282
self.worker.start()
293283

294-
def send(
295-
self, record, message, resource=None, labels=None, trace=None, span_id=None
296-
):
284+
def send(self, record, message, **kwargs):
297285
"""Overrides Transport.send().
298286
299287
Args:
300288
record (logging.LogRecord): Python log record that the handler was called with.
301289
message (str): The message from the ``LogRecord`` after being
302290
formatted by the associated log formatters.
303-
resource (Optional[google.cloud.logging_v2.resource.Resource]):
304-
Monitored resource of the entry.
305-
labels (Optional[dict]): Mapping of labels for the entry.
306-
trace (Optional[str]): TraceID to apply to the logging entry.
307-
span_id (Optional[str]): span_id within the trace for the log entry.
308-
Specify the trace parameter if span_id is set.
291+
kwargs: Additional optional arguments for the logger
309292
"""
310-
self.worker.enqueue(
311-
record,
312-
message,
313-
resource=resource,
314-
labels=labels,
315-
trace=trace,
316-
span_id=span_id,
317-
)
293+
self.worker.enqueue(record, message, **kwargs)
318294

319295
def flush(self):
320296
"""Submit any pending log records."""

google/cloud/logging_v2/handlers/transports/base.py

+2-6
Original file line numberDiff line numberDiff line change
@@ -22,18 +22,14 @@ class Transport(object):
2222
client and name object, and must override :meth:`send`.
2323
"""
2424

25-
def send(
26-
self, record, message, *, resource=None, labels=None, trace=None, span_id=None
27-
):
25+
def send(self, record, message, **kwargs):
2826
"""Transport send to be implemented by subclasses.
2927
3028
Args:
3129
record (logging.LogRecord): Python log record that the handler was called with.
3230
message (str): The message from the ``LogRecord`` after being
3331
formatted by the associated log formatters.
34-
resource (Optional[google.cloud.logging_v2.resource.Resource]):
35-
Monitored resource of the entry.
36-
labels (Optional[dict]): Mapping of labels for the entry.
32+
kwargs: Additional optional arguments for the logger
3733
"""
3834
raise NotImplementedError
3935

google/cloud/logging_v2/handlers/transports/sync.py

+3-12
Original file line numberDiff line numberDiff line change
@@ -30,26 +30,17 @@ class SyncTransport(Transport):
3030
def __init__(self, client, name):
3131
self.logger = client.logger(name)
3232

33-
def send(
34-
self, record, message, *, resource=None, labels=None, trace=None, span_id=None
35-
):
33+
def send(self, record, message, **kwargs):
3634
"""Overrides transport.send().
3735
3836
Args:
3937
record (logging.LogRecord):
4038
Python log record that the handler was called with.
4139
message (str): The message from the ``LogRecord`` after being
4240
formatted by the associated log formatters.
43-
resource (Optional[~logging_v2.resource.Resource]):
44-
Monitored resource of the entry.
45-
labels (Optional[dict]): Mapping of labels for the entry.
41+
kwargs: Additional optional arguments for the logger
4642
"""
4743
info = {"message": message, "python_logger": record.name}
4844
self.logger.log_struct(
49-
info,
50-
severity=_helpers._normalize_severity(record.levelno),
51-
resource=resource,
52-
labels=labels,
53-
trace=trace,
54-
span_id=span_id,
45+
info, severity=_helpers._normalize_severity(record.levelno), **kwargs,
5546
)

0 commit comments

Comments
 (0)