Skip to content
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

Send trace context with logs from web applications #3448

Merged
merged 49 commits into from
Jun 2, 2017
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
9a20be9
Send trace context with logs from web applications
liyanhui1228 May 22, 2017
cf283c4
Fix style
liyanhui1228 May 22, 2017
583d076
Improved code for web framework detection
liyanhui1228 May 23, 2017
5be368e
Fix year
liyanhui1228 May 23, 2017
28e3a6c
Drop module level variables
liyanhui1228 May 23, 2017
cdc96b4
Address Jon's comments and add some unit tests (not complete yet)
liyanhui1228 May 25, 2017
fb83ab6
Fix stuff
liyanhui1228 May 25, 2017
9a20c4e
Add unit test for flask and some refactor
liyanhui1228 May 25, 2017
f8b7e65
Add unit test for middleware
liyanhui1228 May 26, 2017
65efdcd
Add unit test for django
liyanhui1228 May 26, 2017
f78cc2e
Fix style
liyanhui1228 May 26, 2017
33b92dd
Address jon's comments
liyanhui1228 May 26, 2017
5cc798f
Address jon's comments
liyanhui1228 May 30, 2017
6ef3f49
Address all comments
liyanhui1228 Jun 2, 2017
14c2545
fix style
liyanhui1228 Jun 2, 2017
199ada0
Send trace context with logs from web applications
liyanhui1228 May 22, 2017
425c8ef
Fix style
liyanhui1228 May 22, 2017
1047fd5
Improved code for web framework detection
liyanhui1228 May 23, 2017
46012eb
Fix year
liyanhui1228 May 23, 2017
43148bf
Drop module level variables
liyanhui1228 May 23, 2017
de02a1d
Address Jon's comments and add some unit tests (not complete yet)
liyanhui1228 May 25, 2017
ffb57c4
Fix stuff
liyanhui1228 May 25, 2017
8fd8f31
Add unit test for flask and some refactor
liyanhui1228 May 25, 2017
4480027
Add unit test for middleware
liyanhui1228 May 26, 2017
ad00bdb
Add unit test for django
liyanhui1228 May 26, 2017
8ed81ad
Fix style
liyanhui1228 May 26, 2017
a6b25bb
Address jon's comments
liyanhui1228 May 26, 2017
6123764
Address jon's comments
liyanhui1228 May 30, 2017
3caccc7
Address all comments
liyanhui1228 Jun 2, 2017
4589a06
fix style
liyanhui1228 Jun 2, 2017
2889cfc
Tweaks for style / coverage / lint.
dhermes Jun 2, 2017
53738d7
Merge branch 'yanhuil/trace_id' of https://github.com/GoogleCloudPlat…
liyanhui1228 Jun 2, 2017
1957077
Send trace context with logs from web applications
liyanhui1228 May 22, 2017
bf683f7
Fix style
liyanhui1228 May 22, 2017
51cdb39
Improved code for web framework detection
liyanhui1228 May 23, 2017
2479d91
Fix year
liyanhui1228 May 23, 2017
6c04196
Drop module level variables
liyanhui1228 May 23, 2017
f70c84e
Address Jon's comments and add some unit tests (not complete yet)
liyanhui1228 May 25, 2017
6fc4b21
Fix stuff
liyanhui1228 May 25, 2017
2106bfc
Add unit test for flask and some refactor
liyanhui1228 May 25, 2017
c66fc31
Add unit test for middleware
liyanhui1228 May 26, 2017
035c4a7
Add unit test for django
liyanhui1228 May 26, 2017
bd8c1b4
Fix style
liyanhui1228 May 26, 2017
51bf193
Address jon's comments
liyanhui1228 May 26, 2017
6b90d1e
Address jon's comments
liyanhui1228 May 30, 2017
0c6c4f6
Address all comments
liyanhui1228 Jun 2, 2017
fe8a3d5
fix style
liyanhui1228 Jun 2, 2017
4dff904
Tweaks for style / coverage / lint.
dhermes Jun 2, 2017
9bac365
Merge branch 'yanhuil/trace_id' of https://github.com/GoogleCloudPlat…
liyanhui1228 Jun 2, 2017
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Send trace context with logs from web applications
  • Loading branch information
liyanhui1228 authored and Jon Wayne Parrott committed Jun 2, 2017
commit 195707739b43c11a259a8eb6f2aa32da3aba9857
39 changes: 39 additions & 0 deletions logging/google/cloud/logging/handlers/_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,23 @@
import math
import json

try:
import django
except ImportError:
_USE_DJANGO = False
else:
_USE_DJANGO = True

try:
import flask
except ImportError:
_USE_FLASK = False
flask = None
else:
_USE_FLASK = True

This comment was marked as spam.

This comment was marked as spam.

This comment was marked as spam.

This comment was marked as spam.

This comment was marked as spam.

This comment was marked as spam.

This comment was marked as spam.

This comment was marked as spam.

from google.cloud.logging.handlers.middleware.request import get_request


def format_stackdriver_json(record, message):
"""Helper to format a LogRecord in in Stackdriver fluentd format.
Expand All @@ -37,3 +54,25 @@ def format_stackdriver_json(record, message):
}

return json.dumps(payload)


def get_trace_id_from_request_header():
"""Helper to get trace_id from web application request header.

:rtype: str
:returns: Trace_id in HTTP request headers.
"""
if _USE_FLASK:
try:
trace_id = flask.request.headers['X_CLOUD_TRACE_CONTEXT'].split('/')[0]
except Exception:
trace_id = None
elif _USE_DJANGO:
try:
request = get_request()
trace_id = request.META['HTTP_X_CLOUD_TRACE_CONTEXT'].split('/')[0]
except Exception:
trace_id = None
else:
trace_id = None
return trace_id

This comment was marked as spam.

This comment was marked as spam.

18 changes: 17 additions & 1 deletion logging/google/cloud/logging/handlers/app_engine.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@

import os

from google.cloud.logging.handlers._helpers import get_trace_id_from_request_header
from google.cloud.logging.handlers.handlers import CloudLoggingHandler
from google.cloud.logging.handlers.transports import BackgroundThreadTransport
from google.cloud.logging.resource import Resource
Expand Down Expand Up @@ -50,7 +51,8 @@ def __init__(self, client,
client,
name=_DEFAULT_GAE_LOGGER_NAME,
transport=transport,
resource=self.get_gae_resource())
resource=self.get_gae_resource(),
labels=self.get_gae_labels())

def get_gae_resource(self):
"""Return the GAE resource using the environment variables.
Expand All @@ -67,3 +69,17 @@ def get_gae_resource(self):
},
)
return gae_resource

def get_gae_labels(self):
"""Return the labels for GAE app which includes trace_id.

This comment was marked as spam.

This comment was marked as spam.

This comment was marked as spam.


:rtype: dict
:returns: Labels for GAE app.

This comment was marked as spam.

This comment was marked as spam.

This comment was marked as spam.

"""
trace_id = get_trace_id_from_request_header()
if trace_id is None:
trace_id = 'unknown'

This comment was marked as spam.

This comment was marked as spam.

This comment was marked as spam.

gae_labels = {

This comment was marked as spam.

This comment was marked as spam.

'appengine.googleapis.com/trace_id': trace_id,

This comment was marked as spam.

This comment was marked as spam.

}
return gae_labels
12 changes: 10 additions & 2 deletions logging/google/cloud/logging/handlers/handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,9 @@ class CloudLoggingHandler(logging.StreamHandler):
:param resource: (Optional) Monitored resource of the entry, defaults
to the global resource type.

:type labels: dict
:param labels: (Optional) Mapping of labels for the entry.

Example:

.. code-block:: python
Expand All @@ -79,12 +82,14 @@ class CloudLoggingHandler(logging.StreamHandler):
def __init__(self, client,
name=DEFAULT_LOGGER_NAME,
transport=BackgroundThreadTransport,
resource=_GLOBAL_RESOURCE):
resource=_GLOBAL_RESOURCE,
labels=None):
super(CloudLoggingHandler, self).__init__()
self.name = name
self.client = client
self.transport = transport(client, name)
self.resource = resource
self.labels = labels

def emit(self, record):
"""Actually log the specified logging record.
Expand All @@ -97,7 +102,10 @@ def emit(self, record):
:param record: The record to be logged.
"""
message = super(CloudLoggingHandler, self).format(record)
self.transport.send(record, message, resource=self.resource)
self.transport.send(record,

This comment was marked as spam.

This comment was marked as spam.

message,
resource=self.resource,
labels=self.labels)


def setup_logging(handler, excluded_loggers=EXCLUDED_LOGGER_DEFAULTS,
Expand Down
17 changes: 17 additions & 0 deletions logging/google/cloud/logging/handlers/middleware/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Copyright 2016 Google Inc. All Rights Reserved.

This comment was marked as spam.

This comment was marked as spam.

#
# 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.

from google.cloud.logging.handlers.middleware.request import RequestMiddleware

__all__ = ['RequestMiddleware']
40 changes: 40 additions & 0 deletions logging/google/cloud/logging/handlers/middleware/request.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# Copyright 2016 Google Inc. All Rights Reserved.
#
# 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.

try:
from threading import local

This comment was marked as spam.

This comment was marked as spam.

except ImportError:
from django.utils._threading_local import local

This comment was marked as spam.

This comment was marked as spam.


_thread_locals = local()

This comment was marked as spam.

This comment was marked as spam.



def get_request():

This comment was marked as spam.

This comment was marked as spam.

This comment was marked as spam.

This comment was marked as spam.

This comment was marked as spam.

"""Get Django request from thread local.

:rtype: str
:returns: Django request.
"""
return getattr(_thread_locals, 'request', None)

class RequestMiddleware(object):
"""Saves the request in thread local"""

def process_request(self, request):
"""Called on each request, before Django decides which view to execute.

This comment was marked as spam.

This comment was marked as spam.

This comment was marked as spam.


:type request: :class:`~django.http.request.HttpRequest`
:param request: Django http request.
"""
_thread_locals.request = request

This comment was marked as spam.

This comment was marked as spam.

This comment was marked as spam.

This comment was marked as spam.

Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ def _main_thread_terminated(self):
else:
print('Failed to send %d pending logs.' % (self._queue.qsize(),))

def enqueue(self, record, message, resource=None):
def enqueue(self, record, message, resource=None, labels=None):
"""Queues a log entry to be written by the background thread.

:type record: :class:`logging.LogRecord`
Expand All @@ -215,6 +215,9 @@ def enqueue(self, record, message, resource=None):

:type resource: :class:`~google.cloud.logging.resource.Resource`
:param resource: (Optional) Monitored resource of the entry

:type labels: dict
:param labels: (Optional) Mapping of labels for the entry.
"""
self._queue.put_nowait({
'info': {
Expand All @@ -223,6 +226,7 @@ def enqueue(self, record, message, resource=None):
},
'severity': record.levelname,
'resource': resource,
'labels': labels,
})

def flush(self):
Expand Down Expand Up @@ -257,7 +261,7 @@ def __init__(self, client, name, grace_period=_DEFAULT_GRACE_PERIOD,
self.worker = _Worker(logger)
self.worker.start()

def send(self, record, message, resource=None):
def send(self, record, message, resource=None, labels=None):
"""Overrides Transport.send().

:type record: :class:`logging.LogRecord`
Expand All @@ -269,8 +273,11 @@ def send(self, record, message, resource=None):

:type resource: :class:`~google.cloud.logging.resource.Resource`
:param resource: (Optional) Monitored resource of the entry.

:type labels: dict
:param labels: (Optional) Mapping of labels for the entry.
"""
self.worker.enqueue(record, message, resource=resource)
self.worker.enqueue(record, message, resource=resource, labels=labels)

def flush(self):
"""Submit any pending log records."""
Expand Down
5 changes: 4 additions & 1 deletion logging/google/cloud/logging/handlers/transports/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ class Transport(object):
client and name object, and must override :meth:`send`.
"""

def send(self, record, message, resource=None):
def send(self, record, message, resource=None, labels=None):
"""Transport send to be implemented by subclasses.

:type record: :class:`logging.LogRecord`
Expand All @@ -34,6 +34,9 @@ def send(self, record, message, resource=None):

:type resource: :class:`~google.cloud.logging.resource.Resource`
:param resource: (Optional) Monitored resource of the entry.

:type labels: dict
:param labels: (Optional) Mapping of labels for the entry.
"""
raise NotImplementedError

Expand Down
11 changes: 9 additions & 2 deletions logging/google/cloud/logging/handlers/transports/sync.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ class SyncTransport(Transport):
def __init__(self, client, name):
self.logger = client.logger(name)

def send(self, record, message, resource=None):
def send(self, record, message, resource=None, labels=None):
"""Overrides transport.send().

:type record: :class:`logging.LogRecord`
Expand All @@ -38,8 +38,15 @@ def send(self, record, message, resource=None):
:type message: str
:param message: The message from the ``LogRecord`` after being
formatted by the associated log formatters.

:type resource: :class:`~google.cloud.logging.resource.Resource`
:param resource: (Optional) Monitored resource of the entry.

:type labels: dict
:param labels: (Optional) Mapping of labels for the entry.
"""
info = {'message': message, 'python_logger': record.name}
self.logger.log_struct(info,
severity=record.levelname,
resource=resource)
resource=resource,
labels=labels)