Skip to content

Commit

Permalink
Update span ids to be 16 char hex strings (#147)
Browse files Browse the repository at this point in the history
  • Loading branch information
wkiser authored and liyanhui1228 committed Apr 2, 2018
1 parent 471939e commit 186c931
Show file tree
Hide file tree
Showing 19 changed files with 244 additions and 261 deletions.
31 changes: 17 additions & 14 deletions opencensus/trace/propagation/binary_format.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
import logging
import struct

from opencensus.trace.span_context import SpanContext
from opencensus.trace import span_context as span_context_module
from opencensus.trace.trace_options import TraceOptions

# Used for decoding hex bytes to hex string.
Expand Down Expand Up @@ -54,7 +54,7 @@
trace_id_field_id=UNSIGNED_CHAR,
trace_id='{}{}'.format(TRACE_ID_SIZE, CHAR_ARRAY_FORMAT),
span_id_field_id=UNSIGNED_CHAR,
span_id=UNSIGNED_LONG_LONG,
span_id='{}{}'.format(SPAN_ID_SIZE, CHAR_ARRAY_FORMAT),
trace_option_field_id=UNSIGNED_CHAR,
trace_option=UNSIGNED_CHAR)

Expand All @@ -80,15 +80,15 @@ class BinaryFormatPropagator(object):
[SpanContext]
trace_id: hex string with length 32.
e.g. 'a0b72ca15c1a4bd18962d0ac59dc90b9'
span_id: 64 bits integer.
e.g. 7433567179112518326
span_id: hex string with length 16.
e.g. 'a0b72ca15c1a4bd1'
enabled (trace option): bool.
e.g. True
[Binary Format]
trace_id: Bytes with length 16.
e.g. b'\xa0\xb7,\xa1\\\x1aK\xd1\x89b\xd0\xacY\xdc\x90\xb9'
span_id: Bytes with length 8.
e.g. b'\xb6\x12\x01\xf5\xf6U)g'
e.g. b'\x00\xf0g\xaa\x0b\xa9\x02\xb7'
trace_option: Byte with length 1.
e.g. b'\x01'
"""
Expand All @@ -106,25 +106,28 @@ def from_header(self, binary):
"""
# If no binary provided, generate a new SpanContext
if binary is None:
return SpanContext(from_header=False)
return span_context_module.SpanContext(from_header=False)

# If cannot parse, return a new SpanContext and ignore the context
# from binary.
try:
data = Header._make(struct.unpack(BINARY_FORMAT, binary))
except struct.error:
logging.warn('Cannot parse the incoming binary data {}, '
'wrong format. Total bytes length should be {}.'
.format(binary, FORMAT_LENGTH))
return SpanContext(from_header=False)
logging.warning(
'Cannot parse the incoming binary data {}, '
'wrong format. Total bytes length should be {}.'.format(
binary, FORMAT_LENGTH
)
)
return span_context_module.SpanContext(from_header=False)

# data.trace_id is in bytes with length 16, hexlify it to hex bytes
# with length 32, then decode it to hex string using utf-8.
trace_id = str(binascii.hexlify(data.trace_id).decode(UTF8))
span_id = data.span_id
span_id = str(binascii.hexlify(data.span_id).decode(UTF8))
trace_options = TraceOptions(data.trace_option)

span_context = SpanContext(
span_context = span_context_module.SpanContext(
trace_id=trace_id,
span_id=span_id,
trace_options=trace_options,
Expand All @@ -149,7 +152,7 @@ def to_header(self, span_context):
# If there is no span_id in this context, set it to 0, which is
# considered invalid and won't be set as the downstream parent span_id.
if span_id is None:
span_id = 0
span_id = span_context_module.INVALID_SPAN_ID

# Convert trace_id to bytes with length 16, treat span_id as 64 bit
# integer which is unsigned long long type and convert it to bytes with
Expand All @@ -160,6 +163,6 @@ def to_header(self, span_context):
TRACE_ID_FIELD_ID,
binascii.unhexlify(trace_id),
SPAN_ID_FIELD_ID,
span_id,
binascii.unhexlify(span_id),
TRACE_OPTION_FIELD_ID,
trace_options)
2 changes: 1 addition & 1 deletion opencensus/trace/propagation/google_cloud_format.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
from opencensus.trace.span_context import SpanContext
from opencensus.trace.trace_options import TraceOptions

_TRACE_CONTEXT_HEADER_FORMAT = '([0-9a-f]{32})(\/(\d+))?(;o=(\d+))?'
_TRACE_CONTEXT_HEADER_FORMAT = '([0-9a-f]{32})(\/([0-9a-f]{16}))?(;o=(\d+))?'
_TRACE_CONTEXT_HEADER_RE = re.compile(_TRACE_CONTEXT_HEADER_FORMAT)
_TRACE_ID_DELIMETER = '/'
_SPAN_ID_DELIMETER = ';'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ def from_header(self, header):
# Need to convert span_id from hex string to int
span_context = SpanContext(
trace_id=trace_id,
span_id=int(span_id, 16),
span_id=span_id,
trace_options=TraceOptions(trace_options),
from_header=True)
return span_context
Expand Down Expand Up @@ -90,10 +90,6 @@ def to_header(self, span_context):
span_id = span_context.span_id
trace_options = span_context.trace_options.enabled

# Need to convert span_id from int to hex string
span_id_hex = hex(span_id)
span_id = span_id_hex[2:].zfill(16)

# Convert the trace options
trace_options = '01' if trace_options else '00'

Expand Down
83 changes: 42 additions & 41 deletions opencensus/trace/span_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,17 @@
"""SpanContext encapsulates the current context within the request's trace."""

import logging
import random
import re
import uuid

from opencensus.trace import trace_options

_INVALID_TRACE_ID = '0' * 32
_INVALID_SPAN_ID = 0
INVALID_SPAN_ID = '0' * 16
_TRACE_HEADER_KEY = 'X_CLOUD_TRACE_CONTEXT'
_TRACE_ID_FORMAT = '[0-9a-f]{32}?'

TRACE_ID_PATTERN = re.compile('[0-9a-f]{32}?')
SPAN_ID_PATTERN = re.compile('[0-9a-f]{16}?')

# Default options, enable tracing
DEFAULT_OPTIONS = 1
Expand All @@ -42,8 +43,9 @@ class SpanContext(object):
:param trace_id: (Optional) Trace_id is a 32 digits uuid for the trace.
If not given, will generate one automatically.
:type span_id: int
:type span_id: str
:param span_id: (Optional) Identifier for the span, unique within a trace.
If not given, will generate one automatically.
:type trace_options: :class: `~opencensus.trace.trace_options.TraceOptions`
:param trace_options: (Optional) TraceOptions indicates 8 trace options.
Expand All @@ -64,10 +66,10 @@ def __init__(
if trace_options is None:
trace_options = DEFAULT

self.trace_id = self.check_trace_id(trace_id)
self.span_id = self.check_span_id(span_id)
self.trace_options = trace_options
self.from_header = from_header
self.trace_id = self._check_trace_id(trace_id)
self.span_id = self._check_span_id(span_id)
self.trace_options = trace_options

def __str__(self):
"""Returns a string form of the SpanContext. This is the format of
Expand All @@ -84,41 +86,42 @@ def __str__(self):
int(enabled))
return header

def check_span_id(self, span_id):
"""Check the type of span_id to ensure it is int. If it is not int,
first try to convert it to int, if failed to convert, then log a
warning message and set the span_id to None.
def _check_span_id(self, span_id):
"""Check the format of the span_id to ensure it is 16-character hex
value representing a 64-bit number. If span_id is invalid, logs a
warning message and returns None
:type span_id: int
:param span_id: Identifier for the span, unique within a trace.
:type span_id: str
:param span_id: Identifier for the span, unique within a span.
:rtype: int
:rtype: str
:returns: Span_id for the current span.
"""
if span_id is None:
return None
assert isinstance(span_id, str)

if span_id == 0:
if span_id is INVALID_SPAN_ID:
logging.warning(
'Span_id {} is invalid, cannot be zero.'.format(span_id))
'Span_id {} is invalid (cannot be all zero)'.format(span_id))
self.from_header = False
return None

if not isinstance(span_id, int):
try:
span_id = int(span_id)
except (TypeError, ValueError):
logging.warning(
'The type of span_id should be int, got {}.'.format(
span_id.__class__.__name__))
self.from_header = False
span_id = None
match = SPAN_ID_PATTERN.match(span_id)

return span_id
if match:
return span_id
else:
logging.warning(
'Span_id {} does not the match the '
'required format'.format(span_id))
self.from_header = False
return None

def check_trace_id(self, trace_id):
def _check_trace_id(self, trace_id):
"""Check the format of the trace_id to ensure it is 32-character hex
value representing a 128-bit number. Also the trace_id cannot be zero.
value representing a 128-bit number. If trace_id is invalid, returns a
randomly generated trace id
:type trace_id: str
:param trace_id:
Expand All @@ -131,41 +134,39 @@ def check_trace_id(self, trace_id):
if trace_id is _INVALID_TRACE_ID:
logging.warning(
'Trace_id {} is invalid (cannot be all zero), '
'generate a new one.'.format(trace_id))
'generating a new one.'.format(trace_id))
self.from_header = False
return generate_trace_id()

trace_id_pattern = re.compile(_TRACE_ID_FORMAT)

match = trace_id_pattern.match(trace_id)
match = TRACE_ID_PATTERN.match(trace_id)

if match:
return trace_id
else:
logging.warning(
'Trace_id {} does not the match the required format,'
'generate a new one instead.'.format(trace_id))
'generating a new one instead.'.format(trace_id))
self.from_header = False
return generate_trace_id()


def generate_span_id():
"""Return the random generated span ID for a span. Must be 16 digits
as Stackdriver Trace V2 API only accepts 16 digits span ID.
"""Return the random generated span ID for a span. Must be a 16 character
hexadecimal encoded string
:rtype: int
:returns: Identifier for the span. Must be a 64-bit integer other
than 0 and unique within a trace.
:rtype: str
:returns: 16 digit randomly generated hex trace id.
"""
span_id = random.randint(10**15, 10**16 - 1)
span_id = uuid.uuid4().hex[:16]
return span_id


def generate_trace_id():
"""Generate a trace_id randomly.
"""Generate a trace_id randomly. Must be a 32 character
hexadecimal encoded string
:rtype: str
:returns: 32 digit randomly generated trace ID.
:returns: 32 digit randomly generated hex trace id.
"""
trace_id = uuid.uuid4().hex
return trace_id
2 changes: 1 addition & 1 deletion tests/system/trace/basic_trace/basic_trace_system_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ def test_tracer(self):
from opencensus.trace.propagation import google_cloud_format

trace_id = 'f8739df974a4481f98748cd92b27177d'
span_id = '16971691944144156899'
span_id = '6e0c63257de34c92'
trace_option = 1

trace_header = '{}/{};o={}'.format(trace_id, span_id, trace_option)
Expand Down
2 changes: 1 addition & 1 deletion tests/system/trace/django/django_system_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ def wait_app_to_start():
def generate_header():
"""Generate a trace header."""
trace_id = uuid.uuid4().hex
span_id = random.randint(10**15, 10**16 - 1)
span_id = uuid.uuid4().hex[:16]
trace_option = 1

header = '{}/{};o={}'.format(trace_id, span_id, trace_option)
Expand Down
2 changes: 1 addition & 1 deletion tests/system/trace/flask/flask_system_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ def wait_app_to_start():
def generate_header():
"""Generate a trace header."""
trace_id = uuid.uuid4().hex
span_id = random.randint(10**15, 10**16 - 1)
span_id = uuid.uuid4().hex[:16]
trace_option = 1

header = '{}/{};o={}'.format(trace_id, span_id, trace_option)
Expand Down
4 changes: 2 additions & 2 deletions tests/unit/trace/exporters/test_stackdriver_exporter.py
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ def test_translate_to_stackdriver(self):
project_id = 'PROJECT'
trace_id = '6e0c63257de34c92bf9efcd03927272e'
span_name = 'test span'
span_id = 1234
span_id = '6e0c63257de34c92'
attributes = {
'attributeMap': {
'key': {
Expand All @@ -155,7 +155,7 @@ def test_translate_to_stackdriver(self):
}
}
}
parent_span_id = 1111
parent_span_id = '6e0c63257de34c93'
start_time = 'test start time'
end_time = 'test end time'
trace = {
Expand Down
20 changes: 10 additions & 10 deletions tests/unit/trace/exporters/test_zipkin_exporter.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,8 +98,8 @@ def test_translate_to_zipkin_span_kind_none(self):
span_data_module.SpanData(
name='child_span',
context=span_context.SpanContext(trace_id=trace_id),
span_id=1234567890,
parent_span_id=1111111111,
span_id='6e0c63257de34c92',
parent_span_id='6e0c63257de34c93',
attributes={'test_key': 'test_value'},
start_time='2017-08-15T18:02:26.071158Z',
end_time='2017-08-15T18:02:36.071158Z',
Expand All @@ -114,8 +114,8 @@ def test_translate_to_zipkin_span_kind_none(self):
span_data_module.SpanData(
name='child_span',
context=span_context.SpanContext(trace_id=trace_id),
span_id=1234567890,
parent_span_id=1111111111,
span_id='6e0c63257de34c92',
parent_span_id='6e0c63257de34c93',
attributes={'test_key': 1},
start_time='2017-08-15T18:02:26.071158Z',
end_time='2017-08-15T18:02:36.071158Z',
Expand All @@ -134,7 +134,7 @@ def test_translate_to_zipkin_span_kind_none(self):
span_data_module.SpanData(
name='child_span',
context=span_context.SpanContext(trace_id=trace_id),
span_id=1234567890,
span_id='6e0c63257de34c92',
parent_span_id=None,
attributes={
'test_key': False,
Expand Down Expand Up @@ -172,8 +172,8 @@ def test_translate_to_zipkin_span_kind_none(self):
expected_zipkin_spans_ipv4 = [
{
'traceId': '6e0c63257de34c92bf9efcd03927272e',
'id': '1234567890',
'parentId': '1111111111',
'id': '6e0c63257de34c92',
'parentId': '6e0c63257de34c93',
'name': 'child_span',
'timestamp': 1502820146000000,
'duration': 10000000,
Expand All @@ -182,8 +182,8 @@ def test_translate_to_zipkin_span_kind_none(self):
},
{
'traceId': '6e0c63257de34c92bf9efcd03927272e',
'id': '1234567890',
'parentId': '1111111111',
'id': '6e0c63257de34c92',
'parentId': '6e0c63257de34c93',
'name': 'child_span',
'timestamp': 1502820146000000,
'duration': 10000000,
Expand All @@ -195,7 +195,7 @@ def test_translate_to_zipkin_span_kind_none(self):
expected_zipkin_spans_ipv6 = [
{
'traceId': '6e0c63257de34c92bf9efcd03927272e',
'id': '1234567890',
'id': '6e0c63257de34c92',
'name': 'child_span',
'timestamp': 1502820146000000,
'duration': 10000000,
Expand Down
Loading

0 comments on commit 186c931

Please sign in to comment.