Source code for opencensus.trace.propagation.binary_format

# Copyright 2017, OpenCensus Authors
#
# 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.

import binascii
import collections
import logging
import struct

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.
UTF8 = 'utf-8'

VERSION_ID = 0
TRACE_ID_FIELD_ID = 0
SPAN_ID_FIELD_ID = 1
TRACE_OPTION_FIELD_ID = 2

# Sizes are number of bytes.
ID_SIZE = 1
TRACE_ID_SIZE = 16
SPAN_ID_SIZE = 8
TRACE_OPTION_SIZE = 1

FORMAT_LENGTH = 4 * ID_SIZE + TRACE_ID_SIZE + SPAN_ID_SIZE + TRACE_OPTION_SIZE

# See: https://docs.python.org/3/library/struct.html#format-characters
BIG_ENDIAN = '>'
CHAR_ARRAY_FORMAT = 's'
UNSIGNED_CHAR = 'B'
UNSIGNED_LONG_LONG = 'Q'

# Adding big endian indicator at the beginning to avoid auto padding. This is
# for ensuring the length of binary is not changed when propagating.
BINARY_FORMAT = '{big_endian}{version_id}' \
    '{trace_id_field_id}{trace_id}' \
    '{span_id_field_id}{span_id}' \
    '{trace_option_field_id}{trace_option}'\
    .format(
        big_endian=BIG_ENDIAN,
        version_id=UNSIGNED_CHAR,
        trace_id_field_id=UNSIGNED_CHAR,
        trace_id='{}{}'.format(TRACE_ID_SIZE, CHAR_ARRAY_FORMAT),
        span_id_field_id=UNSIGNED_CHAR,
        span_id='{}{}'.format(SPAN_ID_SIZE, CHAR_ARRAY_FORMAT),
        trace_option_field_id=UNSIGNED_CHAR,
        trace_option=UNSIGNED_CHAR)

Header = collections.namedtuple(
    'Header',
    'version_id '
    'trace_id_field_id '
    'trace_id '
    'span_id_field_id '
    'span_id '
    'trace_option_field_id '
    'trace_option')


[docs]class BinaryFormatPropagator(object): """This propagator contains the method for serializing and deserializing SpanContext using a binary format. See: https://github.com/census-instrumentation/opencensus-specs/blob/ master/encodings/BinaryEncoding.md Example: [SpanContext] trace_id: hex string with length 32. e.g. 'a0b72ca15c1a4bd18962d0ac59dc90b9' 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'\x00\xf0g\xaa\x0b\xa9\x02\xb7' trace_option: Byte with length 1. e.g. b'\x01' """
[docs] def from_header(self, binary): """Generate a SpanContext object using the trace context header. The value of enabled parsed from header is int. Need to convert to bool. :type binary: bytes :param binary: Trace context header which was extracted from the request headers. :rtype: :class:`~opencensus.trace.span_context.SpanContext` :returns: SpanContext generated from the trace context header. """ # If no binary provided, generate a new SpanContext if binary is None: 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.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 = str(binascii.hexlify(data.span_id).decode(UTF8)) trace_options = TraceOptions(data.trace_option) span_context = span_context_module.SpanContext( trace_id=trace_id, span_id=span_id, trace_options=trace_options, from_header=True) return span_context
[docs] def to_header(self, span_context): """Convert a SpanContext object to header in binary format. :type span_context: :class:`~opencensus.trace.span_context.SpanContext` :param span_context: SpanContext object. :rtype: bytes :returns: A trace context header in binary format. """ trace_id = span_context.trace_id span_id = span_context.span_id trace_options = int(span_context.trace_options.trace_options_byte) # 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 = 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 # length 8, trace_option is integer with length 1. return struct.pack( BINARY_FORMAT, VERSION_ID, TRACE_ID_FIELD_ID, binascii.unhexlify(trace_id), SPAN_ID_FIELD_ID, binascii.unhexlify(span_id), TRACE_OPTION_FIELD_ID, trace_options)