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
Beispiel #2
0
 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))
Beispiel #3
0
 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")
Beispiel #4
0
 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")
Beispiel #5
0
 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))
Beispiel #7
0
    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,
        )
Beispiel #8
0
        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
Beispiel #9
0
    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)
Beispiel #11
0
    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)
Beispiel #13
0
    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)
Beispiel #19
0
        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
Beispiel #20
0
    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,
        )
Beispiel #21
0
    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)
Beispiel #26
0
    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,
            )
Beispiel #27
0
    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)
Beispiel #30
0
    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)