def test_websocket_traceresponse_header(self):
        """Test a traceresponse header is set for websocket messages"""

        orig = get_global_response_propagator()
        set_global_response_propagator(TraceResponsePropagator())

        self.scope = {
            "type": "websocket",
            "http_version": "1.1",
            "scheme": "ws",
            "path": "/",
            "query_string": b"",
            "headers": [],
            "client": ("127.0.0.1", 32767),
            "server": ("127.0.0.1", 80),
        }
        app = otel_asgi.OpenTelemetryMiddleware(simple_asgi)
        self.seed_app(app)
        self.send_input({"type": "websocket.connect"})
        self.send_input({"type": "websocket.receive", "text": "ping"})
        self.send_input({"type": "websocket.disconnect"})
        _, socket_send, *_ = self.get_all_output()

        span = self.memory_exporter.get_finished_spans()[-1]
        self.assertEqual(trace_api.SpanKind.SERVER, span.kind)

        traceresponse = "00-{0}-{1}-01".format(
            format_trace_id(span.get_span_context().trace_id),
            format_span_id(span.get_span_context().span_id),
        )
        self.assertListEqual(
            socket_send["headers"],
            [
                [b"traceresponse", f"{traceresponse}".encode()],
                [b"access-control-expose-headers", b"traceresponse"],
            ],
        )

        set_global_response_propagator(orig)
Example #2
0
    def inject(
        self,
        carrier: CarrierT,
        context: typing.Optional[Context] = None,
        setter: Setter = default_setter,
    ) -> None:
        span = trace.get_current_span(context=context)

        span_context = span.get_span_context()
        if span_context == trace.INVALID_SPAN_CONTEXT:
            return

        sampled = (trace.TraceFlags.SAMPLED & span_context.trace_flags) != 0
        setter.set(
            carrier,
            self.TRACE_ID_KEY,
            format_trace_id(span_context.trace_id),
        )
        setter.set(
            carrier, self.SPAN_ID_KEY, format_span_id(span_context.span_id)
        )
        setter.set(carrier, self.SAMPLED_KEY, "1" if sampled else "0")
Example #3
0
    async def test_trace_response_headers(self):
        response = await self.async_client.get("/span_name/1234/")

        self.assertFalse(response.has_header("Server-Timing"))
        self.memory_exporter.clear()

        set_global_response_propagator(TraceResponsePropagator())

        response = await self.async_client.get("/span_name/1234/")
        span = self.memory_exporter.get_finished_spans()[0]

        self.assertTrue(response.has_header("traceresponse"))
        self.assertEqual(
            response["Access-Control-Expose-Headers"],
            "traceresponse",
        )
        trace_id = format_trace_id(span.get_span_context().trace_id)
        span_id = format_span_id(span.get_span_context().span_id)
        self.assertEqual(
            response["traceresponse"],
            f"00-{trace_id}-{span_id}-01",
        )
        self.memory_exporter.clear()
    def test_trace_response(self):
        orig = get_global_response_propagator()

        set_global_response_propagator(TraceResponsePropagator())
        response = self.client.get("/hello/123")
        headers = response.headers

        span_list = self.memory_exporter.get_finished_spans()
        self.assertEqual(len(span_list), 1)
        span = span_list[0]

        self.assertIn("traceresponse", headers)
        self.assertEqual(
            headers["access-control-expose-headers"], "traceresponse",
        )
        self.assertEqual(
            headers["traceresponse"],
            "00-{0}-{1}-01".format(
                trace.format_trace_id(span.get_span_context().trace_id),
                trace.format_span_id(span.get_span_context().span_id),
            ),
        )

        set_global_response_propagator(orig)
Example #5
0
    async def test_trace_response_headers(self):
        response = await self.async_client.get("/span_name/1234/")

        self.assertNotIn("Server-Timing", response.headers)
        self.memory_exporter.clear()

        set_global_response_propagator(TraceResponsePropagator())

        response = await self.async_client.get("/span_name/1234/")
        span = self.memory_exporter.get_finished_spans()[0]

        self.assertIn("traceresponse", response.headers)
        self.assertEqual(
            response.headers["Access-Control-Expose-Headers"],
            "traceresponse",
        )
        self.assertEqual(
            response.headers["traceresponse"],
            "00-{0}-{1}-01".format(
                format_trace_id(span.get_span_context().trace_id),
                format_span_id(span.get_span_context().span_id),
            ),
        )
        self.memory_exporter.clear()
    def inject(
        self,
        carrier: textmap.CarrierT,
        context: typing.Optional[Context] = None,
        setter: textmap.Setter = textmap.default_setter,
    ) -> None:
        """Injects SpanContext into the carrier.

        See `opentelemetry.propagators.textmap.TextMapPropagator.inject`
        """
        span = trace.get_current_span(context)
        span_context = span.get_span_context()
        if span_context == trace.INVALID_SPAN_CONTEXT:
            return
        traceparent_string = "00-{trace_id}-{span_id}-{:02x}".format(
            span_context.trace_flags,
            trace_id=format_trace_id(span_context.trace_id),
            span_id=format_span_id(span_context.span_id),
        )
        setter.set(carrier, self._TRACEPARENT_HEADER_NAME, traceparent_string)
        if span_context.trace_state:
            tracestate_string = span_context.trace_state.to_header()
            setter.set(carrier, self._TRACESTATE_HEADER_NAME,
                       tracestate_string)
def _extract_logs_from_span(span):
    logs = []

    ctx = span.get_context()
    trace_id = ctx.trace_id
    p_span_id = ctx.span_id
    for event in span.events:
        l = {
            'start_time':
            datetime.datetime.utcfromtimestamp(event.timestamp / float(1e9)),
            'duration_ms':
            0,
            'name':
            event.name,
            'trace.trace_id':
            trace_api.format_trace_id(trace_id)[2:],
            'trace.parent_id':
            trace_api.format_span_id(p_span_id)[2:],
            'meta.span_type':
            'span_event',
        }
        l.update(event.attributes)
        logs.append(l)
    return logs
 def _format_context(context):
     x_ctx = OrderedDict()
     x_ctx["trace_id"] = trace_api.format_trace_id(context.trace_id)
     x_ctx["span_id"] = trace_api.format_span_id(context.span_id)
     x_ctx["trace_state"] = repr(context.trace_state)
     return x_ctx
Example #9
0
 def _encode_span_id(span_id: int) -> str:
     return format_span_id(span_id)
    def _translate_to_cloud_trace(
        self, spans: Sequence[ReadableSpan]
    ) -> List[Dict[str, Any]]:
        """Translate the spans to Cloud Trace format.

        Args:
            spans: Sequence of spans to convert
        """

        cloud_trace_spans = []

        for span in spans:
            ctx = span.get_span_context()
            trace_id = format_trace_id(ctx.trace_id)
            span_id = format_span_id(ctx.span_id)
            span_name = "projects/{}/traces/{}/spans/{}".format(
                self.project_id, trace_id, span_id
            )

            parent_id = None
            if span.parent:
                parent_id = format_span_id(span.parent.span_id)

            start_time = _get_time_from_ns(span.start_time)
            end_time = _get_time_from_ns(span.end_time)

            if span.attributes and len(span.attributes) > MAX_SPAN_ATTRS:
                logger.warning(
                    "Span has more then %s attributes, some will be truncated",
                    MAX_SPAN_ATTRS,
                )

            # Span does not support a MonitoredResource object. We put the
            # information into attributes instead.
            resources_and_attrs = {
                **(span.attributes or {}),
                **_extract_resources(span.resource),
            }

            cloud_trace_spans.append(
                {
                    "name": span_name,
                    "span_id": span_id,
                    "display_name": _get_truncatable_str_object(
                        span.name, 128
                    ),
                    "start_time": start_time,
                    "end_time": end_time,
                    "parent_span_id": parent_id,
                    "attributes": _extract_attributes(
                        resources_and_attrs,
                        MAX_SPAN_ATTRS,
                        add_agent_attr=True,
                    ),
                    "links": _extract_links(span.links),  # type: ignore[has-type]
                    "status": _extract_status(span.status),  # type: ignore[arg-type]
                    "time_events": _extract_events(span.events),
                    "span_kind": _extract_span_kind(span.kind),
                }
            )
            # TODO: Leverage more of the Cloud Trace API, e.g.
            #  same_process_as_parent_span and child_span_count

        return cloud_trace_spans
Example #11
0
    def extract(
        self,
        getter: Getter[TextMapPropagatorT],
        carrier: TextMapPropagatorT,
        context: typing.Optional[Context] = None,
    ) -> Context:
        trace_id = format_trace_id(trace.INVALID_TRACE_ID)
        span_id = format_span_id(trace.INVALID_SPAN_ID)
        sampled = "0"
        flags = None

        single_header = _extract_first_element(
            getter.get(carrier, self.SINGLE_HEADER_KEY))
        if single_header:
            # The b3 spec calls for the sampling state to be
            # "deferred", which is unspecified. This concept does not
            # translate to SpanContext, so we set it as recorded.
            sampled = "1"
            fields = single_header.split("-", 4)

            if len(fields) == 1:
                sampled = fields[0]
            elif len(fields) == 2:
                trace_id, span_id = fields
            elif len(fields) == 3:
                trace_id, span_id, sampled = fields
            elif len(fields) == 4:
                trace_id, span_id, sampled, _ = fields
            else:
                return trace.set_span_in_context(trace.INVALID_SPAN)
        else:
            trace_id = (_extract_first_element(
                getter.get(carrier, self.TRACE_ID_KEY)) or trace_id)
            span_id = (_extract_first_element(
                getter.get(carrier, self.SPAN_ID_KEY)) or span_id)
            sampled = (_extract_first_element(
                getter.get(carrier, self.SAMPLED_KEY)) or sampled)
            flags = (_extract_first_element(getter.get(
                carrier, self.FLAGS_KEY)) or flags)

        if (self._trace_id_regex.fullmatch(trace_id) is None
                or self._span_id_regex.fullmatch(span_id) is None):
            id_generator = trace.get_tracer_provider().id_generator
            trace_id = id_generator.generate_trace_id()
            span_id = id_generator.generate_span_id()
            sampled = "0"

        else:
            trace_id = int(trace_id, 16)
            span_id = int(span_id, 16)

        options = 0
        # The b3 spec provides no defined behavior for both sample and
        # flag values set. Since the setting of at least one implies
        # the desire for some form of sampling, propagate if either
        # header is set to allow.
        if sampled in self._SAMPLE_PROPAGATE_VALUES or flags == "1":
            options |= trace.TraceFlags.SAMPLED

        return trace.set_span_in_context(
            trace.NonRecordingSpan(
                trace.SpanContext(
                    # trace an span ids are encoded in hex, so must be converted
                    trace_id=trace_id,
                    span_id=span_id,
                    is_remote=True,
                    trace_flags=trace.TraceFlags(options),
                    trace_state=trace.TraceState(),
                )))
Example #12
0
 def setUpClass(cls):
     generator = id_generator.RandomIdGenerator()
     cls.serialized_trace_id = trace_api.format_trace_id(
         generator.generate_trace_id())
     cls.serialized_span_id = trace_api.format_span_id(
         generator.generate_span_id())