def test_headers_with_tracestate(self):
        """When there is a traceparent and tracestate header, data from
        both should be addded to the SpanContext.
        """
        traceparent_value = "00-{trace_id}-{span_id}-00".format(
            trace_id=format(self.TRACE_ID, "032x"),
            span_id=format(self.SPAN_ID, "016x"),
        )
        tracestate_value = "foo=1,bar=2,baz=3"
        span_context = get_span_from_context(
            FORMAT.extract(
                get_as_list,
                {
                    "traceparent": [traceparent_value],
                    "tracestate": [tracestate_value],
                },
            )).get_context()
        self.assertEqual(span_context.trace_id, self.TRACE_ID)
        self.assertEqual(span_context.span_id, self.SPAN_ID)
        self.assertEqual(span_context.trace_state, {
            "foo": "1",
            "bar": "2",
            "baz": "3"
        })
        self.assertTrue(span_context.is_remote)
        output = {}  # type:typing.Dict[str, str]
        span = trace.DefaultSpan(span_context)

        ctx = set_span_in_context(span)
        FORMAT.inject(dict.__setitem__, output, ctx)
        self.assertEqual(output["traceparent"], traceparent_value)
        for pair in ["foo=1", "bar=2", "baz=3"]:
            self.assertIn(pair, output["tracestate"])
        self.assertEqual(output["tracestate"].count(","), 2)
예제 #2
0
    def test_sampling_priority_auto_reject(self):
        """Test sampling priority rejected."""
        parent_context = get_span_from_context(
            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_REJECT),
                },
            )
        ).get_context()

        self.assertEqual(parent_context.trace_flags, constants.AUTO_REJECT)

        child = trace.Span(
            "child",
            trace_api.SpanContext(
                parent_context.trace_id,
                trace.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.SAMPLING_PRIORITY_KEY],
            str(constants.AUTO_REJECT),
        )
    def inject(
        self,
        set_in_carrier: httptextformat.Setter[httptextformat.HTTPTextFormatT],
        carrier: httptextformat.HTTPTextFormatT,
        context: typing.Optional[Context] = None,
    ) -> None:
        """Injects SpanContext into the carrier.

        See `opentelemetry.trace.propagation.httptextformat.HTTPTextFormat.inject`
        """
        span_context = get_span_from_context(context).get_context()

        if span_context == trace.INVALID_SPAN_CONTEXT:
            return
        traceparent_string = "00-{:032x}-{:016x}-{:02x}".format(
            span_context.trace_id,
            span_context.span_id,
            span_context.trace_flags,
        )
        set_in_carrier(carrier, self._TRACEPARENT_HEADER_NAME,
                       traceparent_string)
        if span_context.trace_state:
            tracestate_string = _format_tracestate(span_context.trace_state)
            set_in_carrier(carrier, self._TRACESTATE_HEADER_NAME,
                           tracestate_string)
    def test_invalid_parent_id(self):
        """If the parent id is invalid, we must ignore the full traceparent
        header.

        Also ignore any tracestate.

        RFC 3.2.2.3

        Vendors MUST ignore the traceparent when the parent-id is invalid (for
        example, if it contains non-lowercase hex characters).

        RFC 3.3

        If the vendor failed to parse traceparent, it MUST NOT attempt to parse
        tracestate.
        Note that the opposite is not true: failure to parse tracestate MUST
        NOT affect the parsing of traceparent.
        """
        span = get_span_from_context(
            FORMAT.extract(
                get_as_list,
                {
                    "traceparent": [
                        "00-00000000000000000000000000000000-0000000000000000-00"
                    ],
                    "tracestate": ["foo=1,bar=2,foo=3"],
                },
            ))
        self.assertEqual(span.get_context(), trace.INVALID_SPAN_CONTEXT)
    def test_invalid_trace_id(self):
        """If the trace id is invalid, we must ignore the full traceparent header,
        and return a random, valid trace.

        Also ignore any tracestate.

        RFC 3.2.2.3

        If the trace-id value is invalid (for example if it contains
        non-allowed characters or all zeros), vendors MUST ignore the
        traceparent.

        RFC 3.3

        If the vendor failed to parse traceparent, it MUST NOT attempt to
        parse tracestate.
        Note that the opposite is not true: failure to parse tracestate MUST
        NOT affect the parsing of traceparent.
        """
        span = get_span_from_context(
            FORMAT.extract(
                get_as_list,
                {
                    "traceparent": [
                        "00-00000000000000000000000000000000-1234567890123456-00"
                    ],
                    "tracestate": ["foo=1,bar=2,foo=3"],
                },
            ))
        self.assertEqual(span.get_context(), trace.INVALID_SPAN_CONTEXT)
예제 #6
0
    def __init__(self, span=None, name="span", **kwargs):
        # type: (Optional[Span], Optional[str], Any) -> None
        current_tracer = self.get_current_tracer()

        ## kind
        value = kwargs.pop('kind', None)
        kind = (
            OpenTelemetrySpanKind.CLIENT if value == SpanKind.CLIENT else
            OpenTelemetrySpanKind.PRODUCER if value == SpanKind.PRODUCER else
            OpenTelemetrySpanKind.SERVER if value == SpanKind.SERVER else
            OpenTelemetrySpanKind.CONSUMER if value ==
            SpanKind.CONSUMER else OpenTelemetrySpanKind.INTERNAL if value ==
            SpanKind.INTERNAL else OpenTelemetrySpanKind.INTERNAL if value ==
            SpanKind.UNSPECIFIED else None)  # type: SpanKind
        if value and kind is None:
            raise ValueError(
                "Kind {} is not supported in OpenTelemetry".format(value))

        links = kwargs.pop('links', None)
        if links:
            try:
                ot_links = []
                for link in links:
                    ctx = extract(link.headers)
                    span_ctx = get_span_from_context(ctx).get_span_context()
                    ot_links.append(
                        OpenTelemetryLink(span_ctx, link.attributes))
                kwargs.setdefault('links', ot_links)
            except AttributeError:
                # we will just send the links as is if it's not ~azure.core.tracing.Link without any validation
                # assuming user knows what they are doing.
                kwargs.setdefault('links', links)
        self._span_instance = span or current_tracer.start_span(
            name=name, kind=kind, **kwargs)
        self._current_ctxt_manager = None
예제 #7
0
 def inject(
     self,
     set_in_carrier: Setter[HTTPTextFormatT],
     carrier: HTTPTextFormatT,
     context: typing.Optional[Context] = None,
 ) -> None:
     span = get_span_from_context(context=context)
     sampled = (trace.TraceFlags.SAMPLED & span.context.trace_flags) != 0
     set_in_carrier(
         carrier,
         self.TRACE_ID_KEY,
         format_trace_id(span.context.trace_id),
     )
     set_in_carrier(carrier, self.PARENT_ID_KEY,
                    format_span_id(span.context.span_id))
     set_in_carrier(
         carrier,
         self.SAMPLING_PRIORITY_KEY,
         str(constants.AUTO_KEEP if sampled else constants.AUTO_REJECT),
     )
     if constants.DD_ORIGIN in span.context.trace_state:
         set_in_carrier(
             carrier,
             self.ORIGIN_KEY,
             span.context.trace_state[constants.DD_ORIGIN],
         )
    def test_propagation(self):
        traceparent_value = "00-{trace_id}-{span_id}-00".format(
            trace_id=format(self.TRACE_ID, "032x"),
            span_id=format(self.SPAN_ID, "016x"),
        )
        tracestate_value = "foo=1,bar=2,baz=3"
        headers = {
            "otcorrelationcontext": ["key1=val1,key2=val2"],
            "traceparent": [traceparent_value],
            "tracestate": [tracestate_value],
        }
        ctx = extract(get_as_list, headers)
        correlations = correlationcontext.get_correlations(context=ctx)
        expected = {"key1": "val1", "key2": "val2"}
        self.assertEqual(correlations, expected)
        span_context = get_span_from_context(context=ctx).get_context()

        self.assertEqual(span_context.trace_id, self.TRACE_ID)
        self.assertEqual(span_context.span_id, self.SPAN_ID)

        span = trace.DefaultSpan(span_context)
        ctx = correlationcontext.set_correlation("key3", "val3")
        ctx = correlationcontext.set_correlation("key4", "val4", context=ctx)
        ctx = set_span_in_context(span, context=ctx)
        output = {}
        inject(dict.__setitem__, output, context=ctx)
        self.assertEqual(traceparent_value, output["traceparent"])
        self.assertIn("key3=val3", output["otcorrelationcontext"])
        self.assertIn("key4=val4", output["otcorrelationcontext"])
        self.assertIn("foo=1", output["tracestate"])
        self.assertIn("bar=2", output["tracestate"])
        self.assertIn("baz=3", output["tracestate"])
예제 #9
0
 def test_invalid_single_header(self):
     """If an invalid single header is passed, return an
     invalid SpanContext.
     """
     carrier = {FORMAT.SINGLE_HEADER_KEY: "0-1-2-3-4-5-6-7"}
     ctx = FORMAT.extract(get_as_list, carrier)
     span_context = get_span_from_context(ctx).get_context()
     self.assertEqual(span_context.trace_id, trace_api.INVALID_TRACE_ID)
     self.assertEqual(span_context.span_id, trace_api.INVALID_SPAN_ID)
예제 #10
0
    def test_missing_parent_id(self):
        """If a parent id is missing, populate an invalid trace id."""
        carrier = {
            FORMAT.TRACE_ID_KEY: self.serialized_trace_id,
        }

        ctx = FORMAT.extract(get_as_list, carrier)
        span_context = get_span_from_context(ctx).get_context()
        self.assertEqual(span_context.span_id, trace_api.INVALID_SPAN_ID)
예제 #11
0
    def test_context_propagation(self):
        """Test the propagation of Datadog headers."""
        parent_context = get_span_from_context(
            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.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
        )
예제 #12
0
 def inject(
     self,
     set_in_carrier: Setter[HTTPTextFormatT],
     carrier: HTTPTextFormatT,
     context: typing.Optional[Context] = None,
 ) -> None:
     span = get_span_from_context(context)
     set_in_carrier(carrier, self.TRACE_ID_KEY,
                    str(span.get_context().trace_id))
     set_in_carrier(carrier, self.SPAN_ID_KEY,
                    str(span.get_context().span_id))
    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 = get_span_from_context(FORMAT.extract(get_as_list, output))
        self.assertIsInstance(span.get_context(), trace.SpanContext)
예제 #14
0
    def link_from_headers(cls, headers, attributes=None):
        # type: (Dict[str, str], Attributes) -> None
        """
        Given a dictionary, extracts the context and links the context to the current tracer.

        :param headers: A key value pair dictionary
        :type headers: dict
        """
        ctx = extract(_get_headers_from_http_request_headers, headers)
        span_ctx = get_span_from_context(ctx).get_context()
        current_span = cls.get_current_span()
        current_span.links.append(Link(span_ctx, attributes))
 def test_tracestate_header_with_trailing_comma(self):
     """Do not propagate invalid trace context.
     """
     span = get_span_from_context(
         FORMAT.extract(
             get_as_list,
             {
                 "traceparent": [
                     "00-12345678901234567890123456789012-1234567890123456-00"
                 ],
                 "tracestate": ["foo=1,"],
             },
         ))
     self.assertEqual(span.get_context().trace_state["foo"], "1")
 def test_tracestate_empty_header(self):
     """Test tracestate with an additional empty header (should be ignored)
     """
     span = get_span_from_context(
         FORMAT.extract(
             get_as_list,
             {
                 "traceparent": [
                     "00-12345678901234567890123456789012-1234567890123456-00"
                 ],
                 "tracestate": ["foo=1", ""],
             },
         ))
     self.assertEqual(span.get_context().trace_state["foo"], "1")
예제 #17
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_span_from_context(
            FORMAT.extract(
                get_as_list,
                {
                    malformed_trace_id_key: self.serialized_trace_id,
                    malformed_parent_id_key: self.serialized_parent_id,
                },
            )
        ).get_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)
예제 #18
0
    def link_from_headers(cls, headers, attributes=None):
        # type: (Dict[str, str], Attributes) -> None
        """
        Given a dictionary, extracts the context and links the context to the current tracer.

        :param headers: A key value pair dictionary
        :type headers: dict
        """
        ctx = extract(headers)
        span_ctx = get_span_from_context(ctx).get_span_context()
        current_span = cls.get_current_span()
        try:
            current_span._links.append(OpenTelemetryLink(span_ctx, attributes))  # pylint: disable=protected-access
        except AttributeError:
            warnings.warn(
                """Link must be added while creating the span for Opentelemetry. It might be possible
                that one of the packages you are using doesn't follow the latest Opentelemtry Spec.
                Try updating the azure packages to the latest versions.""")
    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 = get_span_from_context(
            FORMAT.extract(
                get_as_list,
                {
                    "traceparent": [
                        "00-12345678901234567890123456789012-"
                        "1234567890123456-00-residue"
                    ],
                    "tracestate": ["foo=1,bar=2,foo=3"],
                },
            ))
        self.assertEqual(span.get_context(), trace.INVALID_SPAN_CONTEXT)
예제 #20
0
    def extract(self, format, carrier):
        """Implements the ``extract`` method from the base class."""

        # TODO: Finish documentation.
        # pylint: disable=redefined-builtin
        # This implementation does not perform the extracing by itself but
        # uses the configured propagators in opentelemetry.propagators.
        # TODO: Support Format.BINARY once it is supported in
        # opentelemetry-python.
        if format not in self._supported_formats:
            raise opentracing.UnsupportedFormatException

        def get_as_list(dict_object, key):
            value = dict_object.get(key)
            return [value] if value is not None else []

        propagator = propagators.get_global_httptextformat()
        ctx = propagator.extract(get_as_list, carrier)
        otel_context = get_span_from_context(ctx).get_context()

        return SpanContextShim(otel_context)
def get_child_parent_new_carrier(old_carrier):

    ctx = FORMAT.extract(get_as_list, old_carrier)
    parent_context = get_span_from_context(ctx).get_context()

    parent = trace.Span("parent", parent_context)
    child = trace.Span(
        "child",
        trace_api.SpanContext(
            parent_context.trace_id,
            trace.generate_span_id(),
            trace_flags=parent_context.trace_flags,
            trace_state=parent_context.trace_state,
        ),
        parent=parent,
    )

    new_carrier = {}
    ctx = set_span_in_context(child)
    FORMAT.inject(dict.__setitem__, new_carrier, context=ctx)

    return child, parent, new_carrier
예제 #22
0
 def inject(
     self,
     set_in_carrier: Setter[HTTPTextFormatT],
     carrier: HTTPTextFormatT,
     context: typing.Optional[Context] = None,
 ) -> None:
     span = get_span_from_context(context=context)
     sampled = (trace.TraceFlags.SAMPLED & span.context.trace_flags) != 0
     set_in_carrier(
         carrier,
         self.TRACE_ID_KEY,
         format_trace_id(span.context.trace_id),
     )
     set_in_carrier(carrier, self.SPAN_ID_KEY,
                    format_span_id(span.context.span_id))
     if span.parent is not None:
         set_in_carrier(
             carrier,
             self.PARENT_SPAN_ID_KEY,
             format_span_id(span.parent.context.span_id),
         )
     set_in_carrier(carrier, self.SAMPLED_KEY, "1" if sampled else "0")
 def test_tracestate_keys(self):
     """Test for valid key patterns in the tracestate
     """
     tracestate_value = ",".join([
         "1a-2f@foo=bar1",
         "1a-_*/2b@foo=bar2",
         "foo=bar3",
         "foo-_*/bar=bar4",
     ])
     span = get_span_from_context(
         FORMAT.extract(
             get_as_list,
             {
                 "traceparent": [
                     "00-12345678901234567890123456789012-1234567890123456-00"
                 ],
                 "tracestate": [tracestate_value],
             },
         ))
     self.assertEqual(span.get_context().trace_state["1a-2f@foo"], "bar1")
     self.assertEqual(span.get_context().trace_state["1a-_*/2b@foo"],
                      "bar2")
     self.assertEqual(span.get_context().trace_state["foo"], "bar3")
     self.assertEqual(span.get_context().trace_state["foo-_*/bar"], "bar4")
예제 #24
0
 def get_current_span(cls):
     # type: () -> Span
     """
     Get the current span from the execution context. Return None otherwise.
     """
     return get_span_from_context()