Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 1f2b190

Browse filesBrowse files
fix: 16-bit hexadecimal formatting for XCTC span IDs (#946)
* fix: 16-bit hexadecimal formatting for XCTC span IDs * 🦉 Updates from OwlBot post-processor See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md * addressed nit * 🦉 Updates from OwlBot post-processor See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md * Fixed test + docstring --------- Co-authored-by: Owl Bot <gcf-owl-bot[bot]@users.noreply.github.com>
1 parent 6c753c2 commit 1f2b190
Copy full SHA for 1f2b190

File tree

Expand file treeCollapse file tree

4 files changed

+67
-22
lines changed
Filter options
Expand file treeCollapse file tree

4 files changed

+67
-22
lines changed

‎google/cloud/logging_v2/handlers/_helpers.py

Copy file name to clipboardExpand all lines: google/cloud/logging_v2/handlers/_helpers.py
+22-2Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -174,20 +174,40 @@ def _parse_xcloud_trace(header):
174174
Args:
175175
header (str): the string extracted from the X_CLOUD_TRACE header
176176
Returns:
177-
Tuple[Optional[dict], Optional[str], bool]:
177+
Tuple[Optional[str], Optional[str], bool]:
178178
The trace_id, span_id and trace_sampled extracted from the header
179179
Each field will be None if not found.
180180
"""
181181
trace_id = span_id = None
182182
trace_sampled = False
183-
# see https://cloud.google.com/trace/docs/setup for X-Cloud-Trace_Context format
183+
184+
# As per the format described at https://cloud.google.com/trace/docs/trace-context#legacy-http-header
185+
# "X-Cloud-Trace-Context: TRACE_ID[/SPAN_ID][;o=OPTIONS]"
186+
# for example:
187+
# "X-Cloud-Trace-Context: 105445aa7843bc8bf206b12000100000/1;o=1"
188+
#
189+
# We expect:
190+
# * trace_id (optional, 128-bit hex string): "105445aa7843bc8bf206b12000100000"
191+
# * span_id (optional, 16-bit hex string): "0000000000000001" (needs to be converted into 16 bit hex string)
192+
# * trace_sampled (optional, bool): true
184193
if header:
185194
try:
186195
regex = r"([\w-]+)?(\/?([\w-]+))?(;?o=(\d))?"
187196
match = re.match(regex, header)
188197
trace_id = match.group(1)
189198
span_id = match.group(3)
190199
trace_sampled = match.group(5) == "1"
200+
201+
# Convert the span ID to 16-bit hexadecimal instead of decimal
202+
try:
203+
span_id_int = int(span_id)
204+
if span_id_int > 0 and span_id_int < 2**64:
205+
span_id = f"{span_id_int:016x}"
206+
else:
207+
span_id = None
208+
except (ValueError, TypeError):
209+
span_id = None
210+
191211
except IndexError:
192212
pass
193213
return trace_id, span_id, trace_sampled

‎tests/unit/handlers/test__helpers.py

Copy file name to clipboardExpand all lines: tests/unit/handlers/test__helpers.py
+37-14Lines changed: 37 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,13 @@
2525

2626
_FLASK_TRACE_ID = "flask0id"
2727
_FLASK_SPAN_ID = "span0flask"
28+
_FLASK_SPAN_ID_XCTC_DEC = "12345"
29+
_FLASK_SPAN_ID_XCTC_HEX = "3039".zfill(16)
2830
_FLASK_HTTP_REQUEST = {"requestUrl": "https://flask.palletsprojects.com/en/1.1.x/"}
2931
_DJANGO_TRACE_ID = "django0id"
3032
_DJANGO_SPAN_ID = "span0django"
33+
_DJANGO_SPAN_ID_XCTC_DEC = "54321"
34+
_DJANGO_SPAN_ID_XCTC_HEX = "d431".zfill(16)
3135
_DJANGO_HTTP_REQUEST = {"requestUrl": "https://www.djangoproject.com/"}
3236

3337

@@ -64,8 +68,9 @@ def test_no_context_header(self):
6468
def test_xcloud_header(self):
6569
flask_trace_header = "X_CLOUD_TRACE_CONTEXT"
6670
expected_trace_id = _FLASK_TRACE_ID
67-
expected_span_id = _FLASK_SPAN_ID
68-
flask_trace_id = f"{expected_trace_id}/{expected_span_id};o=1"
71+
input_span_id = _FLASK_SPAN_ID_XCTC_DEC
72+
expected_span_id = _FLASK_SPAN_ID_XCTC_HEX
73+
flask_trace_id = f"{expected_trace_id}/{input_span_id};o=1"
6974

7075
app = self.create_app()
7176
context = app.test_request_context(
@@ -173,9 +178,10 @@ def test_xcloud_header(self):
173178
from google.cloud.logging_v2.handlers.middleware import request
174179

175180
django_trace_header = "HTTP_X_CLOUD_TRACE_CONTEXT"
176-
expected_span_id = _DJANGO_SPAN_ID
181+
input_span_id = _DJANGO_SPAN_ID_XCTC_DEC
182+
expected_span_id = _DJANGO_SPAN_ID_XCTC_HEX
177183
expected_trace_id = _DJANGO_TRACE_ID
178-
django_trace_id = f"{expected_trace_id}/{expected_span_id};o=1"
184+
django_trace_id = f"{expected_trace_id}/{input_span_id};o=1"
179185

180186
django_request = RequestFactory().get(
181187
"/", **{django_trace_header: django_trace_id}
@@ -501,43 +507,60 @@ def test_no_span(self):
501507
self.assertEqual(sampled, False)
502508

503509
def test_no_trace(self):
504-
header = "/12345"
510+
input_span = "12345"
511+
expected_span = "3039".zfill(16)
512+
header = f"/{input_span}"
505513
trace_id, span_id, sampled = self._call_fut(header)
506514
self.assertIsNone(trace_id)
507-
self.assertEqual(span_id, "12345")
515+
self.assertEqual(span_id, expected_span)
508516
self.assertEqual(sampled, False)
509517

510518
def test_with_span(self):
511519
expected_trace = "12345"
512-
expected_span = "67890"
513-
header = f"{expected_trace}/{expected_span}"
520+
input_span = "67890"
521+
expected_span = "10932".zfill(16)
522+
header = f"{expected_trace}/{input_span}"
514523
trace_id, span_id, sampled = self._call_fut(header)
515524
self.assertEqual(trace_id, expected_trace)
516525
self.assertEqual(span_id, expected_span)
517526
self.assertEqual(sampled, False)
518527

528+
def test_with_span_decimal_not_in_bounds(self):
529+
input_spans = ["0", "9" * 100]
530+
531+
for input_span in input_spans:
532+
expected_trace = "12345"
533+
header = f"{expected_trace}/{input_span}"
534+
trace_id, span_id, sampled = self._call_fut(header)
535+
self.assertEqual(trace_id, expected_trace)
536+
self.assertIsNone(span_id)
537+
self.assertEqual(sampled, False)
538+
519539
def test_with_extra_characters(self):
520540
expected_trace = "12345"
521-
expected_span = "67890"
522-
header = f"{expected_trace}/{expected_span};abc"
541+
input_span = "67890"
542+
expected_span = "10932".zfill(16)
543+
header = f"{expected_trace}/{input_span};abc"
523544
trace_id, span_id, sampled = self._call_fut(header)
524545
self.assertEqual(trace_id, expected_trace)
525546
self.assertEqual(span_id, expected_span)
526547
self.assertEqual(sampled, False)
527548

528549
def test_with_explicit_no_sampled(self):
529550
expected_trace = "12345"
530-
expected_span = "67890"
531-
header = f"{expected_trace}/{expected_span};o=0"
551+
input_span = "67890"
552+
expected_span = "10932".zfill(16)
553+
header = f"{expected_trace}/{input_span};o=0"
532554
trace_id, span_id, sampled = self._call_fut(header)
533555
self.assertEqual(trace_id, expected_trace)
534556
self.assertEqual(span_id, expected_span)
535557
self.assertEqual(sampled, False)
536558

537559
def test_with__sampled(self):
538560
expected_trace = "12345"
539-
expected_span = "67890"
540-
header = f"{expected_trace}/{expected_span};o=1"
561+
input_span = "67890"
562+
expected_span = "10932".zfill(16)
563+
header = f"{expected_trace}/{input_span};o=1"
541564
trace_id, span_id, sampled = self._call_fut(header)
542565
self.assertEqual(trace_id, expected_trace)
543566
self.assertEqual(span_id, expected_span)

‎tests/unit/handlers/test_handlers.py

Copy file name to clipboardExpand all lines: tests/unit/handlers/test_handlers.py
+4-3Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ def test_minimal_record(self):
140140
self.assertIsNone(record._labels)
141141
self.assertEqual(record._labels_str, "{}")
142142

143-
def test_record_with_request(self):
143+
def test_record_with_xctc_request(self):
144144
"""
145145
test filter adds http request data when available
146146
"""
@@ -161,8 +161,9 @@ def test_record_with_request(self):
161161
expected_path = "http://testserver/123"
162162
expected_agent = "Mozilla/5.0"
163163
expected_trace = "123"
164-
expected_span = "456"
165-
combined_trace = f"{expected_trace}/{expected_span};o=1"
164+
input_span = "456"
165+
expected_span = "1c8".zfill(16)
166+
combined_trace = f"{expected_trace}/{input_span};o=1"
166167
expected_request = {
167168
"requestMethod": "GET",
168169
"requestUrl": expected_path,

‎tests/unit/handlers/test_structured_log.py

Copy file name to clipboardExpand all lines: tests/unit/handlers/test_structured_log.py
+4-3Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -382,7 +382,7 @@ def test_format_with_arguments(self):
382382
result = handler.format(record)
383383
self.assertIn(expected_result, result)
384384

385-
def test_format_with_request(self):
385+
def test_format_with_xctc_request(self):
386386
import logging
387387
import json
388388

@@ -393,8 +393,9 @@ def test_format_with_request(self):
393393
expected_path = "http://testserver/123"
394394
expected_agent = "Mozilla/5.0"
395395
expected_trace = "123"
396-
expected_span = "456"
397-
trace_header = f"{expected_trace}/{expected_span};o=1"
396+
input_span = "456"
397+
expected_span = "1c8".zfill(16)
398+
trace_header = f"{expected_trace}/{input_span};o=1"
398399
expected_payload = {
399400
"logging.googleapis.com/trace": expected_trace,
400401
"logging.googleapis.com/spanId": expected_span,

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.