def __call__(self, logger, method_name, event_dict): # An example of adding datadog formatted trace context to logs # from: https://github.com/open-telemetry/opentelemetry-python-contrib/blob/b53b9a012f76c4fc883c3c245fddc29142706d0d/exporter/opentelemetry-exporter-datadog/src/opentelemetry/exporter/datadog/propagator.py#L122-L129 current_span = trace.get_current_span() if current_span is not None: event_dict['dd.trace_id'] = str(current_span.context.trace_id & 0xFFFFFFFFFFFFFFFF) event_dict['dd.span_id'] = str(current_span.context.span_id) return event_dict
def inject( self, carrier: CarrierT, context: typing.Optional[Context] = None, setter: Setter = default_setter, ) -> None: span = trace.get_current_span(context) setter.set(carrier, self.TRACE_ID_KEY, str(span.get_span_context().trace_id)) setter.set(carrier, self.SPAN_ID_KEY, str(span.get_span_context().span_id))
def test_tracestate_header_with_trailing_comma(self): """Do not propagate invalid trace context.""" span = trace.get_current_span( FORMAT.extract( { "traceparent": [ "00-12345678901234567890123456789012-1234567890123456-00" ], "tracestate": ["foo=1,"], }, )) self.assertEqual(span.get_span_context().trace_state["foo"], "1")
def test_tracestate_empty_header(self): """Test tracestate with an additional empty header (should be ignored)""" span = trace.get_current_span( FORMAT.extract( { "traceparent": [ "00-12345678901234567890123456789012-1234567890123456-00" ], "tracestate": ["foo=1", ""], }, )) self.assertEqual(span.get_span_context().trace_state["foo"], "1")
def test_extract_invalid_span_id(self): old_carrier = { "uber-trace-id": "000000000000000000000000deadbeef:0000000000000000:00:00", "uberctx-key1": "value1", } formatted_baggage = {"key1": "value1"} context = FORMAT.extract(carrier_getter, old_carrier) span_context = trace_api.get_current_span(context).get_span_context() self.assertEqual(span_context.span_id, trace_api.INVALID_SPAN_ID) self.assertDictEqual(formatted_baggage, context["baggage"])
def inject( self, set_in_carrier: Setter[TextMapPropagatorT], carrier: TextMapPropagatorT, context: typing.Optional[Context] = None, ) -> None: span = trace.get_current_span(context) set_in_carrier(carrier, self.TRACE_ID_KEY, str(span.get_span_context().trace_id)) set_in_carrier(carrier, self.SPAN_ID_KEY, str(span.get_span_context().span_id))
def inject( self, set_in_carrier: Setter[TextMapPropagatorT], carrier: TextMapPropagatorT, context: typing.Optional[Context] = None, ) -> None: if not carrier: raise ValueError( "Could not extract from carrier: %s".format(carrier) ) span = trace.get_current_span(context=context) span_context = span.get_context() if not span_context.is_valid: return otel_trace_id = "{:032x}".format(span_context.trace_id) xray_trace_id = ( self.TRACE_ID_VERSION + self.TRACE_ID_DELIMITER + otel_trace_id[: self.TRACE_ID_FIRST_PART_LENGTH] + self.TRACE_ID_DELIMITER + otel_trace_id[self.TRACE_ID_FIRST_PART_LENGTH :] ) parent_id = "{:016x}".format(span_context.span_id) sampling_flag = ( self.IS_SAMPLED if span_context.trace_flags & trace.TraceFlags.SAMPLED else self.NOT_SAMPLED ) # TODO: Add OT trace state to the X-Ray trace header trace_header = ( self.TRACE_ID_KEY + self.KEY_AND_VALUE_DELIMITER + xray_trace_id + self.KV_PAIR_DELIMITER + self.PARENT_ID_KEY + self.KEY_AND_VALUE_DELIMITER + parent_id + self.KV_PAIR_DELIMITER + self.SAMPLED_FLAG_KEY + self.KEY_AND_VALUE_DELIMITER + sampling_flag ) set_in_carrier( carrier, self.TRACE_HEADER_KEY, trace_header, )
def setup( args: P.args, kwargs: P.kwargs ) -> Tuple[Span, str, types.Attributes]: # type: ignore[name-defined] event_name = name if name else func.__qualname__ # Ex: MyObj.method func_inspect = FunctionInspector(func, args, kwargs) _attrs = func_inspect.wrangle_otel_attributes( all_args, these, attributes) if span: _span = func_inspect.get_span(span) else: if not get_current_span().is_recording(): raise RuntimeError( "There is no currently recording span context.") _span = get_current_span() LOGGER.info( f"Recorded event `{event_name}` for span `{_span.name}` with: " # type: ignore[attr-defined] f"attributes={list(_attrs.keys()) if _attrs else []}") return _span, event_name, _attrs
def test_no_traceparent_header(self): """When tracecontext headers are not present, a new SpanContext should be created. RFC 4.2.2: If no traceparent header is received, the vendor creates a new trace-id and parent-id that represents the current request. """ output = {} # type:typing.Dict[str, typing.List[str]] span = trace.get_current_span(FORMAT.extract(output)) self.assertIsInstance(span.get_span_context(), trace.SpanContext)
def test_start_span_implicit(self): tracer = new_tracer() self.assertEqual(trace_api.get_current_span(), trace_api.INVALID_SPAN) root = tracer.start_span("root") self.assertIsNotNone(root.start_time) self.assertIsNone(root.end_time) self.assertEqual(root.kind, trace_api.SpanKind.INTERNAL) with tracer.use_span(root, True): self.assertIs(trace_api.get_current_span(), root) with tracer.start_span("child", kind=trace_api.SpanKind.CLIENT) as child: self.assertIs(child.parent, root.get_span_context()) self.assertEqual(child.kind, trace_api.SpanKind.CLIENT) self.assertIsNotNone(child.start_time) self.assertIsNone(child.end_time) # The new child span should inherit the parent's context but # get a new span ID. root_context = root.get_span_context() child_context = child.get_span_context() self.assertEqual(root_context.trace_id, child_context.trace_id) self.assertNotEqual(root_context.span_id, child_context.span_id) self.assertEqual(root_context.trace_state, child_context.trace_state) self.assertEqual(root_context.trace_flags, child_context.trace_flags) # Verify start_span() did not set the current span. self.assertIs(trace_api.get_current_span(), root) self.assertIsNotNone(child.end_time) self.assertEqual(trace_api.get_current_span(), trace_api.INVALID_SPAN) self.assertIsNotNone(root.end_time)
def start_active_span( self, operation_name: str, child_of: Union[SpanShim, SpanContextShim] = None, references: list = None, tags: Attributes = None, start_time: float = None, ignore_active_span: bool = False, finish_on_close: bool = True, ) -> "ScopeShim": """Starts and activates a span. In terms of functionality, this method behaves exactly like the same method on a "regular" OpenTracing tracer. See :meth:`opentracing.Tracer.start_active_span` for more details. Args: operation_name: Name of the operation represented by the new span from the perspective of the current service. child_of: A :class:`SpanShim` or :class:`SpanContextShim` representing the parent in a "child of" reference. If specified, the *references* parameter must be omitted. references: A list of :class:`opentracing.Reference` objects that identify one or more parents of type :class:`SpanContextShim`. tags: A dictionary of tags. start_time: An explicit start time expressed as the number of seconds since the epoch as returned by :func:`time.time()`. ignore_active_span: Ignore the currently-active span in the OpenTelemetry tracer and make the created span the root span of a new trace. finish_on_close: Determines whether the created span should end automatically when closing the returned :class:`ScopeShim`. Returns: A :class:`ScopeShim` that is already activated by the :class:`ScopeManagerShim`. """ current_span = get_current_span() if (child_of is None and current_span.get_span_context() is not INVALID_SPAN_CONTEXT): child_of = SpanShim(None, None, current_span) span = self.start_span( operation_name=operation_name, child_of=child_of, references=references, tags=tags, start_time=start_time, ignore_active_span=ignore_active_span, ) return self._scope_manager.activate(span, finish_on_close)
def test_start_as_current_span_explicit(self): tracer = new_tracer() other_parent = trace._Span( "name", trace_api.SpanContext( trace_id=0x000000000000000000000000DEADBEEF, span_id=0x00000000DEADBEF0, is_remote=False, trace_flags=trace_api.TraceFlags(trace_api.TraceFlags.SAMPLED), ), ) other_parent_ctx = trace_api.set_span_in_context(other_parent) self.assertEqual(trace_api.get_current_span(), trace_api.INVALID_SPAN) # Test with the implicit root span with tracer.start_as_current_span("root") as root: self.assertIs(trace_api.get_current_span(), root) self.assertIsNotNone(root.start_time) self.assertIsNone(root.end_time) with tracer.start_as_current_span( "stepchild", other_parent_ctx ) as child: # The child should become the current span as usual, but its # parent should be the one passed in, not the # previously-current span. self.assertIs(trace_api.get_current_span(), child) self.assertNotEqual(child.parent, root) self.assertIs(child.parent, other_parent.get_span_context()) # After exiting the child's scope the last span on the stack should # become current, not the child's parent. self.assertNotEqual(trace_api.get_current_span(), other_parent) self.assertIs(trace_api.get_current_span(), root) self.assertIsNotNone(child.end_time)
def test_malformed_headers(self): """Test with no Datadog headers""" malformed_trace_id_key = FORMAT.TRACE_ID_KEY + "-x" malformed_parent_id_key = FORMAT.PARENT_ID_KEY + "-x" context = get_current_span( FORMAT.extract( { malformed_trace_id_key: self.serialized_trace_id, malformed_parent_id_key: self.serialized_parent_id, }, )).get_span_context() self.assertNotEqual(context.trace_id, int(self.serialized_trace_id)) self.assertNotEqual(context.span_id, int(self.serialized_parent_id)) self.assertFalse(context.is_remote)
def poll(self, *args, **kwargs): self._self_release_context() msg = self._self_instance.poll(*args, **kwargs) if msg: ctx = propagators.extract(get_from_messge, msg) self._self_span = trace.get_tracer(__name__).start_span( name="confluent_kafka.poll", kind=trace.SpanKind.CONSUMER, parent=trace.get_current_span(ctx), ) set_span_from_message(self._self_span, msg) msg = MessageProxy(msg) weakref.finalize(msg, self._self_release_context) return msg
def test_invalid_event_attributes(self): self.assertEqual(trace_api.get_current_span(), trace_api.INVALID_SPAN) with self.tracer.start_as_current_span("root") as root: root.add_event("event0", {"attr1": True, "attr2": ["hi", False]}) root.add_event("event0", {"attr1": dict()}) root.add_event("event0", {"attr1": [[True]]}) root.add_event("event0", {"attr1": [dict()], "attr2": [1, 2]}) self.assertEqual(len(root.events), 4) self.assertEqual(root.events[0].attributes, {"attr1": True}) self.assertEqual(root.events[1].attributes, {}) self.assertEqual(root.events[2].attributes, {}) self.assertEqual(root.events[3].attributes, {"attr2": (1, 2)})
def add_span_attributes(attributes: Dict[str, Any], span: Union[trace.Span, None] = None): """Add attributes to a span. Parameters ---------- attributes : dict Arbitrary number of span attributes. span : opentelemetry.trace.Span """ span = span or trace.get_current_span() for attribute, value in attributes.items(): span.set_attribute(attribute, value)
def roll_sum(sides, rolls): span = trace.get_current_span() sum = 0 for r in range(0, rolls): result = randint(1, sides) span.add_event( "log", { "roll.sides": sides, "roll.result": result, }, ) sum += result return str(sum)
def test_context_propagation(self): """Test the propagation of Datadog headers.""" parent_context = get_current_span( FORMAT.extract( get_as_list, { FORMAT.TRACE_ID_KEY: self.serialized_trace_id, FORMAT.PARENT_ID_KEY: self.serialized_parent_id, FORMAT.SAMPLING_PRIORITY_KEY: str(constants.AUTO_KEEP), FORMAT.ORIGIN_KEY: self.serialized_origin, }, )).get_context() self.assertEqual(parent_context.trace_id, int(self.serialized_trace_id)) self.assertEqual(parent_context.span_id, int(self.serialized_parent_id)) self.assertEqual(parent_context.trace_flags, constants.AUTO_KEEP) self.assertEqual( parent_context.trace_state.get(constants.DD_ORIGIN), self.serialized_origin, ) self.assertTrue(parent_context.is_remote) child = trace.Span( "child", trace_api.SpanContext( parent_context.trace_id, trace_api.RandomIdsGenerator().generate_span_id(), is_remote=False, trace_flags=parent_context.trace_flags, trace_state=parent_context.trace_state, ), parent=parent_context, ) child_carrier = {} child_context = set_span_in_context(child) FORMAT.inject(dict.__setitem__, child_carrier, context=child_context) self.assertEqual(child_carrier[FORMAT.TRACE_ID_KEY], self.serialized_trace_id) self.assertEqual(child_carrier[FORMAT.PARENT_ID_KEY], str(child.context.span_id)) self.assertEqual( child_carrier[FORMAT.SAMPLING_PRIORITY_KEY], str(constants.AUTO_KEEP), ) self.assertEqual(child_carrier.get(FORMAT.ORIGIN_KEY), self.serialized_origin)
def record_factory(*args, **kwargs): record = old_factory(*args, **kwargs) record.otelSpanID = "0" record.otelTraceID = "0" record.otelServiceName = service_name span = get_current_span() if span != INVALID_SPAN: ctx = span.get_span_context() if ctx != INVALID_SPAN_CONTEXT: record.otelSpanID = format(ctx.span_id, "016x") record.otelTraceID = format(ctx.trace_id, "032x") return record
def _handle_exception(self, exception: Optional[Exception]) -> "status.Status": if exception is None: return status.Status(status.StatusCode.OK) exc_hash = hash(exception) if exc_hash not in self.known_exceptions: span = trace.get_current_span() span.record_exception(exception) self.known_exceptions.add(exc_hash) return status.Status( description=f"{type(exception).__name__}: {exception}", status_code=status.StatusCode.ERROR, )
def test_span_lifetime(self): """Check that the span is active for the duration of the call.""" interceptor = server_interceptor() # To capture the current span at the time the handler is called active_span_in_handler = None def handler(request, context): nonlocal active_span_in_handler active_span_in_handler = trace.get_current_span() return b"" with futures.ThreadPoolExecutor(max_workers=1) as executor: server = grpc.server( executor, options=(("grpc.so_reuseport", 0), ), interceptors=[interceptor], ) server.add_generic_rpc_handlers((UnaryUnaryRpcHandler(handler), )) port = server.add_insecure_port("[::]:0") channel = grpc.insecure_channel("localhost:{:d}".format(port)) active_span_before_call = trace.get_current_span() try: server.start() channel.unary_unary("TestServicer/handler")(b"") finally: server.stop(None) active_span_after_call = trace.get_current_span() self.assertEqual(active_span_before_call, trace.INVALID_SPAN) self.assertEqual(active_span_after_call, trace.INVALID_SPAN) self.assertIsInstance(active_span_in_handler, trace_sdk.Span) self.assertIsNone(active_span_in_handler.parent)
def _translate(self, record: logging.LogRecord) -> LogRecord: timestamp = int(record.created * 1e9) span_context = get_current_span().get_span_context() attributes = self._get_attributes(record) severity_number = std_to_otlp(record.levelno) return LogRecord( timestamp=timestamp, trace_id=span_context.trace_id, span_id=span_context.span_id, trace_flags=span_context.trace_flags, severity_text=record.levelname, severity_number=severity_number, body=record.getMessage(), resource=self._log_emitter.resource, attributes=attributes, )
def test_mixed_case_header_key(self): header_value = "{}/{};o=1".format( get_hexadecimal_trace_id(self.valid_trace_id), self.valid_span_id) for header_key in ( "X-Cloud-Trace-Context", "X-ClOuD-tRace-ConTeXt", "X-CLOUD-TRACE-CONTEXT", ): header_map = {header_key: [header_value]} new_context = self.propagator.extract(dict_getter, header_map) new_span_context = trace.get_current_span( new_context).get_span_context() self.assertEqual(new_span_context.trace_id, self.valid_trace_id) self.assertEqual(new_span_context.span_id, self.valid_span_id) self.assertEqual(new_span_context.trace_flags, TraceFlags(1)) self.assertTrue(new_span_context.is_remote)
def inject( self, set_in_carrier: httptextformat.Setter[httptextformat.HTTPTextFormatT], carrier: httptextformat.HTTPTextFormatT, context: typing.Optional[Context] = None, ) -> None: span = trace.get_current_span(context) span_context = span.get_context() if span_context == trace.INVALID_SPAN_CONTEXT: return header = "{}/{};o={}".format( get_hexadecimal_trace_id(span_context.trace_id), span_context.span_id, int(span_context.trace_flags.sampled), ) set_in_carrier(carrier, _TRACE_CONTEXT_HEADER_NAME, header)
def inject( self, carrier: textmap.CarrierT, context: typing.Optional[Context] = None, setter: textmap.Setter = textmap.default_setter, ) -> None: span = trace.get_current_span(context) span_context = span.get_span_context() if span_context == trace.INVALID_SPAN_CONTEXT: return header = "{}/{};o={}".format( format_trace_id(span_context.trace_id), span_context.span_id, int(span_context.trace_flags.sampled), ) setter.set(carrier, _TRACE_CONTEXT_HEADER_NAME, header)
def inject( self, carrier: CarrierT, context: Optional[Context] = None, setter: Setter = default_setter, ) -> None: span_context = get_current_span(context).get_span_context() if span_context.trace_id == INVALID_TRACE_ID: return setter.set( carrier, OT_TRACE_ID_HEADER, hex(span_context.trace_id)[2:][-16:] ) setter.set( carrier, OT_SPAN_ID_HEADER, hex(span_context.span_id)[2:][-16:], ) if span_context.trace_flags == TraceFlags.SAMPLED: traceflags = "true" else: traceflags = "false" setter.set(carrier, OT_SAMPLED_HEADER, traceflags) baggage = get_all(context) if not baggage: return for header_name, header_value in baggage.items(): if ( _valid_header_name.fullmatch(header_name) is None or _valid_header_value.fullmatch(header_value) is None ): continue setter.set( carrier, "".join([OT_BAGGAGE_PREFIX, header_name]), header_value, )
def test_events(self): self.assertEqual(trace_api.get_current_span(), trace_api.INVALID_SPAN) with self.tracer.start_as_current_span("root") as root: # only event name root.add_event("event0") # event name and attributes now = time_ns() root.add_event("event1", { "name": "pluto", "some_bools": [True, False] }) # event name, attributes and timestamp now = time_ns() root.add_event("event2", {"name": ["birthday"]}, now) mutable_list = ["original_contents"] root.add_event("event3", {"name": mutable_list}) self.assertEqual(len(root.events), 4) self.assertEqual(root.events[0].name, "event0") self.assertEqual(root.events[0].attributes, {}) self.assertEqual(root.events[1].name, "event1") self.assertEqual( root.events[1].attributes, { "name": "pluto", "some_bools": (True, False) }, ) self.assertEqual(root.events[2].name, "event2") self.assertEqual(root.events[2].attributes, {"name": ("birthday", )}) self.assertEqual(root.events[2].timestamp, now) self.assertEqual(root.events[3].name, "event3") self.assertEqual(root.events[3].attributes, {"name": ("original_contents", )}) mutable_list = ["new_contents"] self.assertEqual(root.events[3].attributes, {"name": ("original_contents", )})
def inject( self, set_in_carrier: Setter[TextMapPropagatorT], carrier: TextMapPropagatorT, context: typing.Optional[Context] = None, ) -> None: span = trace.get_current_span(context=context) span_context = span.get_span_context() if not span_context.is_valid: return otel_trace_id = "{:032x}".format(span_context.trace_id) xray_trace_id = TRACE_ID_DELIMITER.join( [ TRACE_ID_VERSION, otel_trace_id[:TRACE_ID_FIRST_PART_LENGTH], otel_trace_id[TRACE_ID_FIRST_PART_LENGTH:], ] ) parent_id = "{:016x}".format(span_context.span_id) sampling_flag = ( IS_SAMPLED if span_context.trace_flags & trace.TraceFlags.SAMPLED else NOT_SAMPLED ) # TODO: Add OT trace state to the X-Ray trace header trace_header = KV_PAIR_DELIMITER.join( [ KEY_AND_VALUE_DELIMITER.join([key, value]) for key, value in [ (TRACE_ID_KEY, xray_trace_id), (PARENT_ID_KEY, parent_id), (SAMPLED_FLAG_KEY, sampling_flag), ] ] ) set_in_carrier( carrier, TRACE_HEADER_KEY, trace_header, )
def test_invalid_span_id(self, mock_generate_span_id, mock_generate_trace_id): """If a span id is invalid, generate a trace id.""" mock_generate_trace_id.configure_mock(return_value=1) mock_generate_span_id.configure_mock(return_value=2) carrier = { FORMAT.TRACE_ID_KEY: self.serialized_trace_id, FORMAT.SPAN_ID_KEY: "abc123", FORMAT.FLAGS_KEY: "1", } ctx = FORMAT.extract(carrier_getter, carrier) span_context = trace_api.get_current_span(ctx).get_span_context() self.assertEqual(span_context.trace_id, 1) self.assertEqual(span_context.span_id, 2)
def test_format_not_supported(self): """If the traceparent does not adhere to the supported format, discard it and create a new tracecontext. RFC 4.3 If the version cannot be parsed, return an invalid trace header. """ span = trace.get_current_span( FORMAT.extract( { "traceparent": [ "00-12345678901234567890123456789012-" "1234567890123456-00-residue" ], "tracestate": ["foo=1,bar=2,foo=3"], }, )) self.assertEqual(span.get_span_context(), trace.INVALID_SPAN_CONTEXT)