Example #1
0
    def _test_encode_max_tag_length(self, max_tag_value_length: int):
        otel_span, expected_tag_output = self.get_data_for_max_tag_length_test(
            max_tag_value_length)
        service_name = otel_span.name

        expected_output = zipkin_pb2.ListOfSpans(spans=[
            zipkin_pb2.Span(
                trace_id=ProtobufEncoder._encode_trace_id(
                    otel_span.context.trace_id),
                id=ProtobufEncoder._encode_span_id(otel_span.context.span_id),
                name=service_name,
                timestamp=ProtobufEncoder._nsec_to_usec_round(
                    otel_span.start_time),
                duration=ProtobufEncoder._nsec_to_usec_round(
                    otel_span.end_time - otel_span.start_time),
                local_endpoint=zipkin_pb2.Endpoint(service_name=service_name),
                kind=ProtobufEncoder.SPAN_KIND_MAP[SpanKind.INTERNAL],
                tags=expected_tag_output,
                annotations=None,
                debug=True,
            )
        ])

        actual_output = zipkin_pb2.ListOfSpans.FromString(
            ProtobufEncoder(max_tag_value_length).serialize([otel_span],
                                                            NodeEndpoint()))

        self.assertEqual(actual_output, expected_output)
Example #2
0
class ZipkinExporter(SpanExporter):
    def __init__(
        self,
        endpoint: Optional[str] = None,
        local_node_ipv4: IpInput = None,
        local_node_ipv6: IpInput = None,
        local_node_port: Optional[int] = None,
        max_tag_value_length: Optional[int] = None,
    ):
        self.local_node = NodeEndpoint(
            local_node_ipv4, local_node_ipv6, local_node_port
        )

        if endpoint is None:
            endpoint = (
                environ.get(OTEL_EXPORTER_ZIPKIN_ENDPOINT) or DEFAULT_ENDPOINT
            )
        self.endpoint = endpoint

        self.encoder = ProtobufEncoder(max_tag_value_length)

    def export(self, spans: Sequence[Span]) -> SpanExportResult:
        # Populate service_name from first span
        # We restrict any SpanProcessor to be only associated with a single
        # TracerProvider, so it is safe to assume that all Spans in a single
        # batch all originate from one TracerProvider (and in turn have all
        # the same service.name)
        if spans:
            service_name = spans[0].resource.attributes.get(SERVICE_NAME)
            if service_name:
                self.local_node.service_name = service_name
        result = requests.post(
            url=self.endpoint,
            data=self.encoder.serialize(spans, self.local_node),
            headers={"Content-Type": self.encoder.content_type()},
        )

        if result.status_code not in REQUESTS_SUCCESS_STATUS_CODES:
            logger.error(
                "Traces cannot be uploaded; status code: %s, message %s",
                result.status_code,
                result.text,
            )
            return SpanExportResult.FAILURE
        return SpanExportResult.SUCCESS

    def shutdown(self) -> None:
        pass
Example #3
0
    def __init__(
        self,
        endpoint: Optional[str] = None,
        local_node_ipv4: IpInput = None,
        local_node_ipv6: IpInput = None,
        local_node_port: Optional[int] = None,
        max_tag_value_length: Optional[int] = None,
        timeout: Optional[int] = None,
    ):
        """Zipkin exporter.

        Args:
            version: The protocol version to be used.
            endpoint: The endpoint of the Zipkin collector.
            local_node_ipv4: Primary IPv4 address associated with this connection.
            local_node_ipv6: Primary IPv6 address associated with this connection.
            local_node_port: Depending on context, this could be a listen port or the
                client-side of a socket.
            max_tag_value_length: Max length string attribute values can have.
            timeout: Maximum time the Zipkin exporter will wait for each batch export.
                The default value is 10s.

            The tuple (local_node_ipv4, local_node_ipv6, local_node_port) is used to represent
            the network context of a node in the service graph.
        """
        self.local_node = NodeEndpoint(
            local_node_ipv4, local_node_ipv6, local_node_port
        )

        if endpoint is None:
            endpoint = (
                environ.get(OTEL_EXPORTER_ZIPKIN_ENDPOINT) or DEFAULT_ENDPOINT
            )
        self.endpoint = endpoint

        self.encoder = ProtobufEncoder(max_tag_value_length)

        self.session = requests.Session()
        self.session.headers.update(
            {"Content-Type": self.encoder.content_type()}
        )
        self._closed = False
        self.timeout = timeout or int(
            environ.get(OTEL_EXPORTER_ZIPKIN_TIMEOUT, 10)
        )
Example #4
0
    def __init__(
        self,
        endpoint: Optional[str] = None,
        local_node_ipv4: IpInput = None,
        local_node_ipv6: IpInput = None,
        local_node_port: Optional[int] = None,
        max_tag_value_length: Optional[int] = None,
    ):
        self.local_node = NodeEndpoint(
            local_node_ipv4, local_node_ipv6, local_node_port
        )

        if endpoint is None:
            endpoint = (
                environ.get(OTEL_EXPORTER_ZIPKIN_ENDPOINT) or DEFAULT_ENDPOINT
            )
        self.endpoint = endpoint

        self.encoder = ProtobufEncoder(max_tag_value_length)
Example #5
0
    def test_dropped_span_attributes(self):
        otel_span = get_span_with_dropped_attributes_events_links()
        # pylint: disable=no-member
        tags = (
            ProtobufEncoder()
            ._encode_span(otel_span, zipkin_pb2.Endpoint())
            .tags
        )

        self.assertEqual("1", tags["otel.dropped_links_count"])
        self.assertEqual("2", tags["otel.dropped_attributes_count"])
        self.assertEqual("3", tags["otel.dropped_events_count"])
Example #6
0
 def test_encode_local_endpoint_explicits(self):
     ipv4 = "192.168.0.1"
     ipv6 = "2001:db8::c001"
     port = 414120
     self.assertEqual(
         ProtobufEncoder()._encode_local_endpoint(
             NodeEndpoint(ipv4, ipv6, port)),
         zipkin_pb2.Endpoint(
             service_name=TEST_SERVICE_NAME,
             ipv4=ipaddress.ip_address(ipv4).packed,
             ipv6=ipaddress.ip_address(ipv6).packed,
             port=port,
         ),
     )
Example #7
0
    def test_encode(self):
        local_endpoint = zipkin_pb2.Endpoint(service_name=TEST_SERVICE_NAME)
        span_kind = ProtobufEncoder.SPAN_KIND_MAP[SpanKind.INTERNAL]

        otel_spans = self.get_exhaustive_otel_span_list()
        trace_id = ProtobufEncoder._encode_trace_id(
            otel_spans[0].context.trace_id
        )
        expected_output = zipkin_pb2.ListOfSpans(
            spans=[
                zipkin_pb2.Span(
                    trace_id=trace_id,
                    id=ProtobufEncoder._encode_span_id(
                        otel_spans[0].context.span_id
                    ),
                    name=otel_spans[0].name,
                    timestamp=ProtobufEncoder._nsec_to_usec_round(
                        otel_spans[0].start_time
                    ),
                    duration=(
                        ProtobufEncoder._nsec_to_usec_round(
                            otel_spans[0].end_time - otel_spans[0].start_time
                        )
                    ),
                    local_endpoint=local_endpoint,
                    kind=span_kind,
                    tags={
                        "key_bool": "false",
                        "key_string": "hello_world",
                        "key_float": "111.22",
                        "otel.status_code": "OK",
                    },
                    debug=True,
                    parent_id=ProtobufEncoder._encode_span_id(
                        otel_spans[0].parent.span_id
                    ),
                    annotations=[
                        zipkin_pb2.Annotation(
                            timestamp=ProtobufEncoder._nsec_to_usec_round(
                                otel_spans[0].events[0].timestamp
                            ),
                            value=json.dumps(
                                {
                                    "event0": {
                                        "annotation_bool": True,
                                        "annotation_string": "annotation_test",
                                        "key_float": 0.3,
                                    }
                                },
                                sort_keys=True,
                            ),
                        ),
                    ],
                ),
                zipkin_pb2.Span(
                    trace_id=trace_id,
                    id=ProtobufEncoder._encode_span_id(
                        otel_spans[1].context.span_id
                    ),
                    name=otel_spans[1].name,
                    timestamp=ProtobufEncoder._nsec_to_usec_round(
                        otel_spans[1].start_time
                    ),
                    duration=(
                        ProtobufEncoder._nsec_to_usec_round(
                            otel_spans[1].end_time - otel_spans[1].start_time
                        )
                    ),
                    local_endpoint=local_endpoint,
                    kind=span_kind,
                    tags={
                        "key_resource": "some_resource",
                        "otel.status_code": "ERROR",
                        "error": "Example description",
                    },
                    debug=False,
                ),
                zipkin_pb2.Span(
                    trace_id=trace_id,
                    id=ProtobufEncoder._encode_span_id(
                        otel_spans[2].context.span_id
                    ),
                    name=otel_spans[2].name,
                    timestamp=ProtobufEncoder._nsec_to_usec_round(
                        otel_spans[2].start_time
                    ),
                    duration=(
                        ProtobufEncoder._nsec_to_usec_round(
                            otel_spans[2].end_time - otel_spans[2].start_time
                        )
                    ),
                    local_endpoint=local_endpoint,
                    kind=span_kind,
                    tags={
                        "key_string": "hello_world",
                        "key_resource": "some_resource",
                    },
                    debug=False,
                ),
                zipkin_pb2.Span(
                    trace_id=trace_id,
                    id=ProtobufEncoder._encode_span_id(
                        otel_spans[3].context.span_id
                    ),
                    name=otel_spans[3].name,
                    timestamp=ProtobufEncoder._nsec_to_usec_round(
                        otel_spans[3].start_time
                    ),
                    duration=(
                        ProtobufEncoder._nsec_to_usec_round(
                            otel_spans[3].end_time - otel_spans[3].start_time
                        )
                    ),
                    local_endpoint=local_endpoint,
                    kind=span_kind,
                    tags={NAME_KEY: "name", VERSION_KEY: "version"},
                    debug=False,
                ),
            ],
        )

        actual_output = zipkin_pb2.ListOfSpans.FromString(
            ProtobufEncoder().serialize(otel_spans, NodeEndpoint())
        )

        self.assertEqual(actual_output, expected_output)
Example #8
0
 def test_encode_local_endpoint_default(self):
     self.assertEqual(
         ProtobufEncoder()._encode_local_endpoint(NodeEndpoint()),
         zipkin_pb2.Endpoint(service_name=TEST_SERVICE_NAME),
     )
Example #9
0
 def get_encoder(*args, **kwargs) -> ProtobufEncoder:
     return ProtobufEncoder(*args, **kwargs)
Example #10
0
class ZipkinExporter(SpanExporter):
    def __init__(
        self,
        endpoint: Optional[str] = None,
        local_node_ipv4: IpInput = None,
        local_node_ipv6: IpInput = None,
        local_node_port: Optional[int] = None,
        max_tag_value_length: Optional[int] = None,
        timeout: Optional[int] = None,
    ):
        """Zipkin exporter.

        Args:
            version: The protocol version to be used.
            endpoint: The endpoint of the Zipkin collector.
            local_node_ipv4: Primary IPv4 address associated with this connection.
            local_node_ipv6: Primary IPv6 address associated with this connection.
            local_node_port: Depending on context, this could be a listen port or the
                client-side of a socket.
            max_tag_value_length: Max length string attribute values can have.
            timeout: Maximum time the Zipkin exporter will wait for each batch export.
                The default value is 10s.

            The tuple (local_node_ipv4, local_node_ipv6, local_node_port) is used to represent
            the network context of a node in the service graph.
        """
        self.local_node = NodeEndpoint(local_node_ipv4, local_node_ipv6,
                                       local_node_port)

        if endpoint is None:
            endpoint = (environ.get(OTEL_EXPORTER_ZIPKIN_ENDPOINT)
                        or DEFAULT_ENDPOINT)
        self.endpoint = endpoint

        self.encoder = ProtobufEncoder(max_tag_value_length)

        self.session = requests.Session()
        self.session.headers.update(
            {"Content-Type": self.encoder.content_type()})
        self._closed = False
        self.timeout = timeout or int(
            environ.get(OTEL_EXPORTER_ZIPKIN_TIMEOUT, 10))

    def export(self, spans: Sequence[Span]) -> SpanExportResult:
        # After the call to Shutdown subsequent calls to Export are
        # not allowed and should return a Failure result
        if self._closed:
            logger.warning("Exporter already shutdown, ignoring batch")
            return SpanExportResult.FAILURE
        # Populate service_name from first span
        # We restrict any SpanProcessor to be only associated with a single
        # TracerProvider, so it is safe to assume that all Spans in a single
        # batch all originate from one TracerProvider (and in turn have all
        # the same service.name)
        if spans:
            service_name = spans[0].resource.attributes.get(SERVICE_NAME)
            if service_name:
                self.local_node.service_name = service_name
        result = self.session.post(
            url=self.endpoint,
            data=self.encoder.serialize(spans, self.local_node),
            timeout=self.timeout,
        )

        if result.status_code not in REQUESTS_SUCCESS_STATUS_CODES:
            logger.error(
                "Traces cannot be uploaded; status code: %s, message %s",
                result.status_code,
                result.text,
            )
            return SpanExportResult.FAILURE
        return SpanExportResult.SUCCESS

    def shutdown(self) -> None:
        if self._closed:
            logger.warning("Exporter already shutdown, ignoring call")
            return
        self.session.close()
        self._closed = True