Skip to content

Commit e24ae67

Browse files
csilverslukesneeringer
authored andcommitted
Add support for logging the trace-id in webapp2 apps. (#3593)
1 parent ef03ab6 commit e24ae67

File tree

3 files changed

+106
-11
lines changed

3 files changed

+106
-11
lines changed

packages/google-cloud-logging/google/cloud/logging/handlers/_helpers.py

Lines changed: 44 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,21 @@
2222
except ImportError: # pragma: NO COVER
2323
flask = None
2424

25+
try:
26+
import webapp2
27+
except (ImportError, SyntaxError): # pragma: NO COVER
28+
# If you try to import webapp2 under python3, you'll get a syntax
29+
# error (since it hasn't been ported yet). We just pretend it
30+
# doesn't exist. This is unlikely to hit in real life but does
31+
# in the tests.
32+
webapp2 = None
33+
2534
from google.cloud.logging.handlers.middleware.request import (
2635
_get_django_request)
2736

28-
_FLASK_TRACE_HEADER = 'X_CLOUD_TRACE_CONTEXT'
2937
_DJANGO_TRACE_HEADER = 'HTTP_X_CLOUD_TRACE_CONTEXT'
38+
_FLASK_TRACE_HEADER = 'X_CLOUD_TRACE_CONTEXT'
39+
_WEBAPP2_TRACE_HEADER = 'X-CLOUD-TRACE-CONTEXT'
3040

3141

3242
def format_stackdriver_json(record, message):
@@ -54,7 +64,7 @@ def get_trace_id_from_flask():
5464
"""Get trace_id from flask request headers.
5565
5666
:rtype: str
57-
:return: Trace_id in HTTP request headers.
67+
:returns: TraceID in HTTP request headers.
5868
"""
5969
if flask is None or not flask.request:
6070
return None
@@ -69,11 +79,38 @@ def get_trace_id_from_flask():
6979
return trace_id
7080

7181

82+
def get_trace_id_from_webapp2():
83+
"""Get trace_id from webapp2 request headers.
84+
85+
:rtype: str
86+
:returns: TraceID in HTTP request headers.
87+
"""
88+
if webapp2 is None:
89+
return None
90+
91+
try:
92+
# get_request() succeeds if we're in the middle of a webapp2
93+
# request, or raises an assertion error otherwise:
94+
# "Request global variable is not set".
95+
req = webapp2.get_request()
96+
except AssertionError:
97+
return None
98+
99+
header = req.headers.get(_WEBAPP2_TRACE_HEADER)
100+
101+
if header is None:
102+
return None
103+
104+
trace_id = header.split('/', 1)[0]
105+
106+
return trace_id
107+
108+
72109
def get_trace_id_from_django():
73110
"""Get trace_id from django request headers.
74111
75112
:rtype: str
76-
:return: Trace_id in HTTP request headers.
113+
:returns: TraceID in HTTP request headers.
77114
"""
78115
request = _get_django_request()
79116

@@ -93,9 +130,11 @@ def get_trace_id():
93130
"""Helper to get trace_id from web application request header.
94131
95132
:rtype: str
96-
:returns: Trace_id in HTTP request headers.
133+
:returns: TraceID in HTTP request headers.
97134
"""
98-
checkers = (get_trace_id_from_django, get_trace_id_from_flask)
135+
checkers = (get_trace_id_from_django,
136+
get_trace_id_from_flask,
137+
get_trace_id_from_webapp2)
99138

100139
for checker in checkers:
101140
trace_id = checker()

packages/google-cloud-logging/nox.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ def unit_tests(session, python_version):
3636
# Install all test dependencies, then install this package in-place.
3737
session.install(
3838
'mock', 'pytest', 'pytest-cov',
39-
'flask', 'django', *LOCAL_DEPS)
39+
'flask', 'webapp2', 'webob', 'django', *LOCAL_DEPS)
4040
session.install('-e', '.')
4141

4242
# Run py.test against the unit tests.

packages/google-cloud-logging/tests/unit/handlers/test__helpers.py

Lines changed: 61 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,18 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414

15+
import json
1516
import unittest
1617

1718
import mock
19+
import six
20+
21+
try:
22+
from webapp2 import RequestHandler
23+
except SyntaxError:
24+
# webapp2 has not been ported to python3, so it will give a syntax
25+
# error if we try. We'll just skip the webapp2 tests in that case.
26+
RequestHandler = object
1827

1928

2029
class Test_get_trace_id_from_flask(unittest.TestCase):
@@ -37,11 +46,9 @@ def index():
3746

3847
return app
3948

40-
def setUp(self):
41-
self.app = self.create_app()
42-
4349
def test_no_context_header(self):
44-
with self.app.test_request_context(
50+
app = self.create_app()
51+
with app.test_request_context(
4552
path='/',
4653
headers={}):
4754
trace_id = self._call_fut()
@@ -53,7 +60,8 @@ def test_valid_context_header(self):
5360
expected_trace_id = 'testtraceidflask'
5461
flask_trace_id = expected_trace_id + '/testspanid'
5562

56-
context = self.app.test_request_context(
63+
app = self.create_app()
64+
context = app.test_request_context(
5765
path='/',
5866
headers={flask_trace_header: flask_trace_id})
5967

@@ -63,6 +71,54 @@ def test_valid_context_header(self):
6371
self.assertEqual(trace_id, expected_trace_id)
6472

6573

74+
class _GetTraceId(RequestHandler):
75+
def get(self):
76+
from google.cloud.logging.handlers import _helpers
77+
78+
trace_id = _helpers.get_trace_id_from_webapp2()
79+
self.response.content_type = 'application/json'
80+
self.response.out.write(json.dumps(trace_id))
81+
82+
83+
84+
@unittest.skipIf(six.PY3, 'webapp2 is Python 2 only')
85+
class Test_get_trace_id_from_webapp2(unittest.TestCase):
86+
87+
@staticmethod
88+
def create_app():
89+
import webapp2
90+
91+
app = webapp2.WSGIApplication([
92+
('/', _GetTraceId),
93+
])
94+
95+
return app
96+
97+
def test_no_context_header(self):
98+
import webob
99+
100+
req = webob.BaseRequest.blank('/')
101+
response = req.get_response(self.create_app())
102+
trace_id = json.loads(response.body)
103+
104+
self.assertEquals(None, trace_id)
105+
106+
def test_valid_context_header(self):
107+
import webob
108+
109+
webapp2_trace_header = 'X-Cloud-Trace-Context'
110+
expected_trace_id = 'testtraceidwebapp2'
111+
webapp2_trace_id = expected_trace_id + '/testspanid'
112+
113+
req = webob.BaseRequest.blank(
114+
'/',
115+
headers={webapp2_trace_header: webapp2_trace_id})
116+
response = req.get_response(self.create_app())
117+
trace_id = json.loads(response.body)
118+
119+
self.assertEqual(trace_id, expected_trace_id)
120+
121+
66122
class Test_get_trace_id_from_django(unittest.TestCase):
67123

68124
@staticmethod

0 commit comments

Comments
 (0)