-
Notifications
You must be signed in to change notification settings - Fork 56
feat: support http_request field #120
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 14 commits
55383da
3c67803
4be2e19
61115d1
f7aa0e0
1459f7e
96cf896
a8d54c8
841e68a
a3bd27c
e8733f4
2f73049
e6ccd30
b653b22
6ca954e
16e7888
36a71ab
6b549bd
e91548c
5f9d932
69c0d39
5bb9e51
6d1974d
f0f5334
fe9104c
95ceb66
399cbf7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -25,6 +25,10 @@ | |
from google.cloud.logging_v2.handlers.middleware.request import _get_django_request | ||
|
||
_DJANGO_TRACE_HEADER = "HTTP_X_CLOUD_TRACE_CONTEXT" | ||
_DJANGO_LENGTH_HEADER = "CONTENT_LENGTH" | ||
_DJANGO_USERAGENT_HEADER = "HTTP_USER_AGENT" | ||
_DJANGO_REMOTE_ADDR_HEADER = "REMOTE_ADDR" | ||
_DJANGO_REFERER_HEADER = "HTTP_REFERER" | ||
_FLASK_TRACE_HEADER = "X_CLOUD_TRACE_CONTEXT" | ||
|
||
|
||
|
@@ -46,59 +50,82 @@ 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 trace_id and http_request data from flask request headers. | ||
|
||
Returns: | ||
str: TraceID in HTTP request headers. | ||
dict: data about the associated http request. | ||
""" | ||
if flask is None or not flask.request: | ||
return None | ||
return None, None | ||
|
||
# build http_request | ||
http_request = { | ||
"request_method": flask.request.method, | ||
"request_url": flask.request.url, | ||
"request_size": str(flask.request.content_length), | ||
"user_agent": flask.request.user_agent.string, | ||
"remote_ip": flask.request.remote_addr, | ||
"referer": flask.request.referrer, | ||
} | ||
|
||
# find trace id | ||
trace_id = None | ||
header = flask.request.headers.get(_FLASK_TRACE_HEADER) | ||
if header: | ||
trace_id = header.split("/", 1)[0] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Future edge case to take care of: Given user input is Seems super niche edge case, but I actually got a similar ticket for this in Go. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hmm I'm not sure I understand. Can you have a span without a trace? Do you have a ticket or doc I can look over? Either way, I'm not a huge fan of this parsing logic. I'm hoping to fix it up when I add support for spans soon There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It shouldn't be possible to have a trace as null but span as non-null. Curious how this was a case in Go? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The edge cases I'm referring to here is just that the So the user could manually write anything here. e.g. This was the original Go issue. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Where we tell users they can manually write this header: https://cloud.google.com/trace/docs/setup#force-trace There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hmm good to keep in mind, yeah it might be helpful to throw an exception or something if the header doesn't match the expected format I'll probably fix that in a later PR when I add support for spans though. That's when proper parsing will be more important. For now, this is the logic that was already in place |
||
|
||
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 trace_id and http_request data from django request headers. | ||
|
||
Returns: | ||
str: TraceID in HTTP request headers. | ||
dict: data about the associated http request. | ||
""" | ||
request = _get_django_request() | ||
|
||
if request is None: | ||
return None | ||
return None, None | ||
|
||
# build http_request | ||
http_request = { | ||
"request_method": request.method, | ||
"request_url": request.build_absolute_uri(), | ||
"request_size": request.META.get(_DJANGO_LENGTH_HEADER), | ||
"user_agent": request.META.get(_DJANGO_USERAGENT_HEADER), | ||
"remote_ip": request.META.get(_DJANGO_REMOTE_ADDR_HEADER), | ||
"referer": request.META.get(_DJANGO_REFERER_HEADER), | ||
daniel-sanche marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
|
||
# 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 trace_id and http_request data from supported web | ||
frameworks. | ||
daniel-sanche marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
Returns: | ||
str: TraceID in HTTP request headers. | ||
dict: data about the associated http request. | ||
""" | ||
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 |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -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, | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I see you're using
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I believe it should. This is the type used by the underlying LogEntry proto, so I assumed it would be preferred:
But I'm still getting used to working with gapic code. Do you prefer to just use dictionaries for these kinds of fields? |
||||||
) |
Uh oh!
There was an error while loading. Please reload this page.