def get_exhaustive_otel_span_list() -> List[trace._Span]: trace_id = 0x6E0C63257DE34C926F9EFCD03927272E base_time = 683647322 * 10**9 # in ns start_times = ( base_time, base_time + 150 * 10**6, base_time + 300 * 10**6, base_time + 400 * 10**6, ) end_times = ( start_times[0] + (50 * 10**6), start_times[1] + (100 * 10**6), start_times[2] + (200 * 10**6), start_times[3] + (300 * 10**6), ) parent_span_context = trace_api.SpanContext(trace_id, 0x1111111111111111, is_remote=False) other_context = trace_api.SpanContext(trace_id, 0x2222222222222222, is_remote=False) span1 = trace._Span( name="test-span-1", context=trace_api.SpanContext( trace_id, 0x34BF92DEEFC58C92, is_remote=False, trace_flags=TraceFlags(TraceFlags.SAMPLED), ), parent=parent_span_context, events=(trace.Event( name="event0", timestamp=base_time + 50 * 10**6, attributes={ "annotation_bool": True, "annotation_string": "annotation_test", "key_float": 0.3, }, ), ), links=(trace_api.Link(context=other_context, attributes={"key_bool": True}), ), resource=trace.Resource({}), ) span1.start(start_time=start_times[0]) span1.set_attribute("key_bool", False) span1.set_attribute("key_string", "hello_world") span1.set_attribute("key_float", 111.22) span1.set_status(Status(StatusCode.OK)) span1.end(end_time=end_times[0]) span2 = trace._Span( name="test-span-2", context=parent_span_context, parent=None, resource=trace.Resource( attributes={"key_resource": "some_resource"}), ) span2.start(start_time=start_times[1]) span2.set_status(Status(StatusCode.ERROR, "Example description")) span2.end(end_time=end_times[1]) span3 = trace._Span( name="test-span-3", context=other_context, parent=None, resource=trace.Resource( attributes={"key_resource": "some_resource"}), ) span3.start(start_time=start_times[2]) span3.set_attribute("key_string", "hello_world") span3.end(end_time=end_times[2]) span4 = trace._Span( name="test-span-3", context=other_context, parent=None, resource=trace.Resource({}), instrumentation_info=InstrumentationInfo(name="name", version="version"), ) span4.start(start_time=start_times[3]) span4.end(end_time=end_times[3]) return [span1, span2, span3, span4]
def test_translate_to_jaeger(self): # pylint: disable=invalid-name self.maxDiff = None span_names = ("test1", "test2", "test3") trace_id = 0x6E0C63257DE34C926F9EFCD03927272E trace_id_high = 0x6E0C63257DE34C92 trace_id_low = 0x6F9EFCD03927272E span_id = 0x34BF92DEEFC58C92 parent_id = 0x1111111111111111 other_id = 0x2222222222222222 base_time = 683647322 * 10 ** 9 # in ns start_times = ( base_time, base_time + 150 * 10 ** 6, base_time + 300 * 10 ** 6, ) durations = (50 * 10 ** 6, 100 * 10 ** 6, 200 * 10 ** 6) end_times = ( start_times[0] + durations[0], start_times[1] + durations[1], start_times[2] + durations[2], ) span_context = trace_api.SpanContext( trace_id, span_id, is_remote=False ) parent_span_context = trace_api.SpanContext( trace_id, parent_id, is_remote=False ) other_context = trace_api.SpanContext( trace_id, other_id, is_remote=False ) event_attributes = { "annotation_bool": True, "annotation_string": "annotation_test", "key_float": 0.3, } event_timestamp = base_time + 50 * 10 ** 6 event = trace.Event( name="event0", timestamp=event_timestamp, attributes=event_attributes, ) link_attributes = {"key_bool": True} link = trace_api.Link( context=other_context, attributes=link_attributes ) default_tags = [ jaeger.Tag( key="span.kind", vType=jaeger.TagType.STRING, vStr="internal", ), ] otel_spans = [ trace._Span( name=span_names[0], context=span_context, parent=parent_span_context, events=(event,), links=(link,), kind=trace_api.SpanKind.CLIENT, resource=Resource( attributes={"key_resource": "some_resource"} ), ), trace._Span( name=span_names[1], context=parent_span_context, parent=None, resource=Resource({}), ), trace._Span( name=span_names[2], context=other_context, parent=None, resource=Resource({}), instrumentation_info=InstrumentationInfo( name="name", version="version" ), ), ] otel_spans[0].start(start_time=start_times[0]) # added here to preserve order otel_spans[0].set_attribute("key_bool", False) otel_spans[0].set_attribute("key_string", "hello_world") otel_spans[0].set_attribute("key_float", 111.22) otel_spans[0].set_attribute("key_tuple", ("tuple_element",)) otel_spans[0].set_status( Status(StatusCode.ERROR, "Example description") ) otel_spans[0].end(end_time=end_times[0]) otel_spans[1].start(start_time=start_times[1]) otel_spans[1].end(end_time=end_times[1]) otel_spans[2].start(start_time=start_times[2]) otel_spans[2].set_status(Status(StatusCode.OK, "Example description")) otel_spans[2].end(end_time=end_times[2]) translate = Translate(otel_spans) # pylint: disable=protected-access spans = translate._translate(ThriftTranslator()) expected_spans = [ jaeger.Span( operationName=span_names[0], traceIdHigh=trace_id_high, traceIdLow=trace_id_low, spanId=span_id, parentSpanId=parent_id, startTime=start_times[0] // 10 ** 3, duration=durations[0] // 10 ** 3, flags=0, tags=[ jaeger.Tag( key="key_bool", vType=jaeger.TagType.BOOL, vBool=False ), jaeger.Tag( key="key_string", vType=jaeger.TagType.STRING, vStr="hello_world", ), jaeger.Tag( key="key_float", vType=jaeger.TagType.DOUBLE, vDouble=111.22, ), jaeger.Tag( key="key_tuple", vType=jaeger.TagType.STRING, vStr="('tuple_element',)", ), jaeger.Tag( key="key_resource", vType=jaeger.TagType.STRING, vStr="some_resource", ), jaeger.Tag( key="otel.status_code", vType=jaeger.TagType.STRING, vStr="ERROR", ), jaeger.Tag( key="otel.status_description", vType=jaeger.TagType.STRING, vStr="Example description", ), jaeger.Tag( key="span.kind", vType=jaeger.TagType.STRING, vStr="client", ), jaeger.Tag( key="error", vType=jaeger.TagType.BOOL, vBool=True ), ], references=[ jaeger.SpanRef( refType=jaeger.SpanRefType.FOLLOWS_FROM, traceIdHigh=trace_id_high, traceIdLow=trace_id_low, spanId=other_id, ) ], logs=[ jaeger.Log( timestamp=event_timestamp // 10 ** 3, fields=[ jaeger.Tag( key="annotation_bool", vType=jaeger.TagType.BOOL, vBool=True, ), jaeger.Tag( key="annotation_string", vType=jaeger.TagType.STRING, vStr="annotation_test", ), jaeger.Tag( key="key_float", vType=jaeger.TagType.DOUBLE, vDouble=0.3, ), jaeger.Tag( key="message", vType=jaeger.TagType.STRING, vStr="event0", ), ], ) ], ), jaeger.Span( operationName=span_names[1], traceIdHigh=trace_id_high, traceIdLow=trace_id_low, spanId=parent_id, parentSpanId=0, startTime=start_times[1] // 10 ** 3, duration=durations[1] // 10 ** 3, flags=0, tags=default_tags, ), jaeger.Span( operationName=span_names[2], traceIdHigh=trace_id_high, traceIdLow=trace_id_low, spanId=other_id, parentSpanId=0, startTime=start_times[2] // 10 ** 3, duration=durations[2] // 10 ** 3, flags=0, tags=[ jaeger.Tag( key="otel.status_code", vType=jaeger.TagType.STRING, vStr="OK", ), jaeger.Tag( key="otel.status_description", vType=jaeger.TagType.STRING, vStr="Example description", ), jaeger.Tag( key="span.kind", vType=jaeger.TagType.STRING, vStr="internal", ), jaeger.Tag( key=jaeger_exporter.translate.NAME_KEY, vType=jaeger.TagType.STRING, vStr="name", ), jaeger.Tag( key=jaeger_exporter.translate.VERSION_KEY, vType=jaeger.TagType.STRING, vStr="version", ), ], ), ] # events are complicated to compare because order of fields # (attributes) in otel is not important but in jeager it is self.assertCountEqual( spans[0].logs[0].fields, expected_spans[0].logs[0].fields ) # get rid of fields to be able to compare the whole spans spans[0].logs[0].fields = None expected_spans[0].logs[0].fields = None self.assertEqual(spans, expected_spans)
def test_translate_to_jaeger(self): span_names = ("test1", "test2", "test3") trace_id = 0x6E0C63257DE34C926F9EFCD03927272E span_id = 0x34BF92DEEFC58C92 parent_id = 0x1111111111111111 other_id = 0x2222222222222222 base_time = 683647322 * 10 ** 9 # in ns start_times = ( base_time, base_time + 150 * 10 ** 6, base_time + 300 * 10 ** 6, ) durations = (50 * 10 ** 6, 100 * 10 ** 6, 200 * 10 ** 6) end_times = ( start_times[0] + durations[0], start_times[1] + durations[1], start_times[2] + durations[2], ) span_context = trace_api.SpanContext( trace_id, span_id, is_remote=False ) parent_span_context = trace_api.SpanContext( trace_id, parent_id, is_remote=False ) other_context = trace_api.SpanContext( trace_id, other_id, is_remote=False ) event_attributes = OrderedDict( [ ("annotation_bool", True), ("annotation_string", "annotation_test"), ("key_float", 0.3), ] ) event_timestamp = base_time + 50 * 10 ** 6 # pylint:disable=protected-access event_timestamp_proto = pb_translator._proto_timestamp_from_epoch_nanos( event_timestamp ) event = trace.Event( name="event0", timestamp=event_timestamp, attributes=event_attributes, ) link_attributes = {"key_bool": True} link = trace_api.Link( context=other_context, attributes=link_attributes ) default_tags = [ model_pb2.KeyValue( key="span.kind", v_type=model_pb2.ValueType.STRING, v_str="internal", ), ] otel_spans = [ trace._Span( name=span_names[0], context=span_context, parent=parent_span_context, events=(event,), links=(link,), kind=trace_api.SpanKind.CLIENT, ), trace._Span( name=span_names[1], context=parent_span_context, parent=None ), trace._Span( name=span_names[2], context=other_context, parent=None ), ] otel_spans[0].start(start_time=start_times[0]) # added here to preserve order otel_spans[0].set_attribute("key_bool", False) otel_spans[0].set_attribute("key_string", "hello_world") otel_spans[0].set_attribute("key_float", 111.22) otel_spans[0].set_attribute("key_tuple", ("tuple_element",)) otel_spans[0].resource = Resource( attributes={"key_resource": "some_resource"} ) otel_spans[0].set_status( Status(StatusCode.ERROR, "Example description") ) otel_spans[0].end(end_time=end_times[0]) otel_spans[1].start(start_time=start_times[1]) otel_spans[1].resource = Resource({}) otel_spans[1].end(end_time=end_times[1]) otel_spans[2].start(start_time=start_times[2]) otel_spans[2].resource = Resource({}) otel_spans[2].set_status(Status(StatusCode.OK, "Example description")) otel_spans[2].end(end_time=end_times[2]) otel_spans[2].instrumentation_info = InstrumentationInfo( name="name", version="version" ) translate = Translate(otel_spans) # pylint: disable=protected-access spans = translate._translate(pb_translator.ProtobufTranslator("svc")) span1_start_time = pb_translator._proto_timestamp_from_epoch_nanos( start_times[0] ) span2_start_time = pb_translator._proto_timestamp_from_epoch_nanos( start_times[1] ) span3_start_time = pb_translator._proto_timestamp_from_epoch_nanos( start_times[2] ) span1_end_time = pb_translator._proto_timestamp_from_epoch_nanos( end_times[0] ) span2_end_time = pb_translator._proto_timestamp_from_epoch_nanos( end_times[1] ) span3_end_time = pb_translator._proto_timestamp_from_epoch_nanos( end_times[2] ) span1_duration = pb_translator._duration_from_two_time_stamps( span1_start_time, span1_end_time ) span2_duration = pb_translator._duration_from_two_time_stamps( span2_start_time, span2_end_time ) span3_duration = pb_translator._duration_from_two_time_stamps( span3_start_time, span3_end_time ) expected_spans = [ model_pb2.Span( operation_name=span_names[0], trace_id=pb_translator._trace_id_to_bytes(trace_id), span_id=pb_translator._span_id_to_bytes(span_id), start_time=span1_start_time, duration=span1_duration, flags=0, tags=[ model_pb2.KeyValue( key="key_bool", v_type=model_pb2.ValueType.BOOL, v_bool=False, ), model_pb2.KeyValue( key="key_string", v_type=model_pb2.ValueType.STRING, v_str="hello_world", ), model_pb2.KeyValue( key="key_float", v_type=model_pb2.ValueType.FLOAT64, v_float64=111.22, ), model_pb2.KeyValue( key="key_tuple", v_type=model_pb2.ValueType.STRING, v_str="('tuple_element',)", ), model_pb2.KeyValue( key="key_resource", v_type=model_pb2.ValueType.STRING, v_str="some_resource", ), model_pb2.KeyValue( key="otel.status_code", v_type=model_pb2.ValueType.STRING, v_str="ERROR", ), model_pb2.KeyValue( key="otel.status_description", v_type=model_pb2.ValueType.STRING, v_str="Example description", ), model_pb2.KeyValue( key="span.kind", v_type=model_pb2.ValueType.STRING, v_str="client", ), model_pb2.KeyValue( key="error", v_type=model_pb2.ValueType.BOOL, v_bool=True, ), ], references=[ model_pb2.SpanRef( ref_type=model_pb2.SpanRefType.FOLLOWS_FROM, trace_id=pb_translator._trace_id_to_bytes(trace_id), span_id=pb_translator._span_id_to_bytes(other_id), ) ], logs=[ model_pb2.Log( timestamp=event_timestamp_proto, fields=[ model_pb2.KeyValue( key="annotation_bool", v_type=model_pb2.ValueType.BOOL, v_bool=True, ), model_pb2.KeyValue( key="annotation_string", v_type=model_pb2.ValueType.STRING, v_str="annotation_test", ), model_pb2.KeyValue( key="key_float", v_type=model_pb2.ValueType.FLOAT64, v_float64=0.3, ), model_pb2.KeyValue( key="message", v_type=model_pb2.ValueType.STRING, v_str="event0", ), ], ) ], process=model_pb2.Process( service_name="svc", tags=[ model_pb2.KeyValue( key="key_resource", v_str="some_resource", v_type=model_pb2.ValueType.STRING, ) ], ), ), model_pb2.Span( operation_name=span_names[1], trace_id=pb_translator._trace_id_to_bytes(trace_id), span_id=pb_translator._span_id_to_bytes(parent_id), start_time=span2_start_time, duration=span2_duration, flags=0, tags=default_tags, process=model_pb2.Process(service_name="svc",), ), model_pb2.Span( operation_name=span_names[2], trace_id=pb_translator._trace_id_to_bytes(trace_id), span_id=pb_translator._span_id_to_bytes(other_id), start_time=span3_start_time, duration=span3_duration, flags=0, tags=[ model_pb2.KeyValue( key="otel.status_code", v_type=model_pb2.ValueType.STRING, v_str="OK", ), model_pb2.KeyValue( key="otel.status_description", v_type=model_pb2.ValueType.STRING, v_str="Example description", ), model_pb2.KeyValue( key="span.kind", v_type=model_pb2.ValueType.STRING, v_str="internal", ), model_pb2.KeyValue( key="otel.instrumentation_library.name", v_type=model_pb2.ValueType.STRING, v_str="name", ), model_pb2.KeyValue( key="otel.instrumentation_library.version", v_type=model_pb2.ValueType.STRING, v_str="version", ), ], process=model_pb2.Process(service_name="svc",), ), ] # events are complicated to compare because order of fields # (attributes) in otel is not important but in jeager it is # pylint: disable=no-member self.assertCountEqual( spans[0].logs[0].fields, expected_spans[0].logs[0].fields, ) self.assertEqual(spans, expected_spans)
def test_export_protobuf(self): span_names = ("test1", "test2", "test3", "test4") trace_id = 0x6E0C63257DE34C926F9EFCD03927272E span_id = 0x34BF92DEEFC58C92 parent_id = 0x1111111111111111 other_id = 0x2222222222222222 base_time = 683647322 * 10**9 # in ns start_times = ( base_time, base_time + 150 * 10**6, base_time + 300 * 10**6, base_time + 400 * 10**6, ) durations = (50 * 10**6, 100 * 10**6, 200 * 10**6, 300 * 10**6) end_times = ( start_times[0] + durations[0], start_times[1] + durations[1], start_times[2] + durations[2], start_times[3] + durations[3], ) span_context = trace_api.SpanContext( trace_id, span_id, is_remote=False, trace_flags=TraceFlags(TraceFlags.SAMPLED), ) parent_span_context = trace_api.SpanContext(trace_id, parent_id, is_remote=False) other_context = trace_api.SpanContext(trace_id, other_id, is_remote=False) event_attributes = { "annotation_bool": True, "annotation_string": "annotation_test", "key_float": 0.3, } event_timestamp = base_time + 50 * 10**6 event = trace.Event( name="event0", timestamp=event_timestamp, attributes=event_attributes, ) link_attributes = {"key_bool": True} link = trace_api.Link(context=other_context, attributes=link_attributes) otel_spans = [ trace._Span( name=span_names[0], context=span_context, parent=parent_span_context, resource=Resource({}), events=(event, ), links=(link, ), ), trace._Span( name=span_names[1], context=parent_span_context, parent=None, resource=Resource( attributes={"key_resource": "some_resource"}), ), trace._Span( name=span_names[2], context=other_context, parent=None, resource=Resource( attributes={"key_resource": "some_resource"}), ), trace._Span( name=span_names[3], context=other_context, parent=None, resource=Resource({}), instrumentation_info=InstrumentationInfo(name="name", version="version"), ), ] otel_spans[0].start(start_time=start_times[0]) # added here to preserve order otel_spans[0].set_attribute("key_bool", False) otel_spans[0].set_attribute("key_string", "hello_world") otel_spans[0].set_attribute("key_float", 111.22) otel_spans[0].set_status( Status(StatusCode.ERROR, "Example description")) otel_spans[0].end(end_time=end_times[0]) otel_spans[1].start(start_time=start_times[1]) otel_spans[1].set_status(Status(StatusCode.OK)) otel_spans[1].end(end_time=end_times[1]) otel_spans[2].start(start_time=start_times[2]) otel_spans[2].set_attribute("key_string", "hello_world") otel_spans[2].end(end_time=end_times[2]) otel_spans[3].start(start_time=start_times[3]) otel_spans[3].end(end_time=end_times[3]) service_name = "test-service" local_endpoint = zipkin_pb2.Endpoint(service_name=service_name, port=9411) span_kind = SPAN_KIND_MAP_PROTOBUF[SpanKind.INTERNAL] expected_spans = zipkin_pb2.ListOfSpans(spans=[ zipkin_pb2.Span( trace_id=trace_id.to_bytes(length=16, byteorder="big", signed=False), id=ZipkinSpanExporter.format_pbuf_span_id(span_id), name=span_names[0], timestamp=nsec_to_usec_round(start_times[0]), duration=nsec_to_usec_round(durations[0]), local_endpoint=local_endpoint, kind=span_kind, tags={ "key_bool": "false", "key_string": "hello_world", "key_float": "111.22", "otel.status_code": "ERROR", "error": "Example description", }, debug=True, parent_id=ZipkinSpanExporter.format_pbuf_span_id(parent_id), annotations=[ zipkin_pb2.Annotation( timestamp=nsec_to_usec_round(event_timestamp), value=json.dumps({ "event0": { "annotation_bool": True, "annotation_string": "annotation_test", "key_float": 0.3, } }), ), ], ), zipkin_pb2.Span( trace_id=trace_id.to_bytes(length=16, byteorder="big", signed=False), id=ZipkinSpanExporter.format_pbuf_span_id(parent_id), name=span_names[1], timestamp=nsec_to_usec_round(start_times[1]), duration=nsec_to_usec_round(durations[1]), local_endpoint=local_endpoint, kind=span_kind, tags={ "key_resource": "some_resource", "otel.status_code": "OK", }, ), zipkin_pb2.Span( trace_id=trace_id.to_bytes(length=16, byteorder="big", signed=False), id=ZipkinSpanExporter.format_pbuf_span_id(other_id), name=span_names[2], timestamp=nsec_to_usec_round(start_times[2]), duration=nsec_to_usec_round(durations[2]), local_endpoint=local_endpoint, kind=span_kind, tags={ "key_string": "hello_world", "key_resource": "some_resource", }, ), zipkin_pb2.Span( trace_id=trace_id.to_bytes(length=16, byteorder="big", signed=False), id=ZipkinSpanExporter.format_pbuf_span_id(other_id), name=span_names[3], timestamp=nsec_to_usec_round(start_times[3]), duration=nsec_to_usec_round(durations[3]), local_endpoint=local_endpoint, kind=span_kind, tags={ NAME_KEY: "name", VERSION_KEY: "version" }, ), ], ) exporter = ZipkinSpanExporter( service_name, transport_format=TRANSPORT_FORMAT_PROTOBUF) mock_post = MagicMock() with patch("requests.post", mock_post): mock_post.return_value = MockResponse(200) status = exporter.export(otel_spans) self.assertEqual(SpanExportResult.SUCCESS, status) # pylint: disable=unsubscriptable-object kwargs = mock_post.call_args[1] self.assertEqual(kwargs["url"], "http://localhost:9411/api/v2/spans") self.assertEqual(kwargs["headers"]["Content-Type"], "application/x-protobuf") self.assertEqual(zipkin_pb2.ListOfSpans.FromString(kwargs["data"]), expected_spans)
def test_export_json(self): span_names = ("test1", "test2", "test3", "test4") trace_id = 0x6E0C63257DE34C926F9EFCD03927272E span_id = 0x34BF92DEEFC58C92 parent_id = 0x1111111111111111 other_id = 0x2222222222222222 base_time = 683647322 * 10**9 # in ns start_times = ( base_time, base_time + 150 * 10**6, base_time + 300 * 10**6, base_time + 400 * 10**6, ) durations = (50 * 10**6, 100 * 10**6, 200 * 10**6, 300 * 10**6) end_times = ( start_times[0] + durations[0], start_times[1] + durations[1], start_times[2] + durations[2], start_times[3] + durations[3], ) span_context = trace_api.SpanContext( trace_id, span_id, is_remote=False, trace_flags=TraceFlags(TraceFlags.SAMPLED), ) parent_span_context = trace_api.SpanContext(trace_id, parent_id, is_remote=False) other_context = trace_api.SpanContext(trace_id, other_id, is_remote=False) event_attributes = { "annotation_bool": True, "annotation_string": "annotation_test", "key_float": 0.3, } event_timestamp = base_time + 50 * 10**6 event = trace.Event( name="event0", timestamp=event_timestamp, attributes=event_attributes, ) link_attributes = {"key_bool": True} link = trace_api.Link(context=other_context, attributes=link_attributes) otel_spans = [ trace._Span( name=span_names[0], context=span_context, parent=parent_span_context, events=(event, ), links=(link, ), resource=Resource({}), ), trace._Span( name=span_names[1], context=parent_span_context, parent=None, resource=Resource( attributes={"key_resource": "some_resource"}), ), trace._Span( name=span_names[2], context=other_context, parent=None, resource=Resource( attributes={"key_resource": "some_resource"}), ), trace._Span( name=span_names[3], context=other_context, parent=None, resource=Resource({}), instrumentation_info=InstrumentationInfo(name="name", version="version"), ), ] otel_spans[0].start(start_time=start_times[0]) # added here to preserve order otel_spans[0].set_attribute("key_bool", False) otel_spans[0].set_attribute("key_string", "hello_world") otel_spans[0].set_attribute("key_float", 111.22) otel_spans[0].set_status( Status(StatusCode.ERROR, "Example description")) otel_spans[0].end(end_time=end_times[0]) otel_spans[1].start(start_time=start_times[1]) otel_spans[1].end(end_time=end_times[1]) otel_spans[2].start(start_time=start_times[2]) otel_spans[2].set_attribute("key_string", "hello_world") otel_spans[2].end(end_time=end_times[2]) otel_spans[3].start(start_time=start_times[3]) otel_spans[3].end(end_time=end_times[3]) service_name = "test-service" local_endpoint = {"serviceName": service_name, "port": 9411} span_kind = SPAN_KIND_MAP_JSON[SpanKind.INTERNAL] exporter = ZipkinSpanExporter(service_name) expected_spans = [ { "traceId": format(trace_id, "x"), "id": format(span_id, "x"), "name": span_names[0], "timestamp": start_times[0] // 10**3, "duration": durations[0] // 10**3, "localEndpoint": local_endpoint, "kind": span_kind, "tags": { "key_bool": "false", "key_string": "hello_world", "key_float": "111.22", "otel.status_code": "ERROR", "error": "Example description", }, "debug": True, "parentId": format(parent_id, "x"), "annotations": [{ "timestamp": event_timestamp // 10**3, "value": { "event0": { "annotation_bool": True, "annotation_string": "annotation_test", "key_float": 0.3, } }, }], }, { "traceId": format(trace_id, "x"), "id": format(parent_id, "x"), "name": span_names[1], "timestamp": start_times[1] // 10**3, "duration": durations[1] // 10**3, "localEndpoint": local_endpoint, "kind": span_kind, "tags": { "key_resource": "some_resource" }, "annotations": None, }, { "traceId": format(trace_id, "x"), "id": format(other_id, "x"), "name": span_names[2], "timestamp": start_times[2] // 10**3, "duration": durations[2] // 10**3, "localEndpoint": local_endpoint, "kind": span_kind, "tags": { "key_string": "hello_world", "key_resource": "some_resource", }, "annotations": None, }, { "traceId": format(trace_id, "x"), "id": format(other_id, "x"), "name": span_names[3], "timestamp": start_times[3] // 10**3, "duration": durations[3] // 10**3, "localEndpoint": local_endpoint, "kind": span_kind, "tags": { NAME_KEY: "name", VERSION_KEY: "version" }, "annotations": None, }, ] mock_post = MagicMock() with patch("requests.post", mock_post): mock_post.return_value = MockResponse(200) status = exporter.export(otel_spans) self.assertEqual(SpanExportResult.SUCCESS, status) # pylint: disable=unsubscriptable-object kwargs = mock_post.call_args[1] self.assertEqual(kwargs["url"], "http://localhost:9411/api/v2/spans") self.assertEqual(kwargs["headers"]["Content-Type"], "application/json") actual_spans = sorted(json.loads(kwargs["data"]), key=lambda span: span["timestamp"]) for expected, actual in zip(expected_spans, actual_spans): expected_annotations = expected.pop("annotations", None) actual_annotations = actual.pop("annotations", None) if actual_annotations: for annotation in actual_annotations: annotation["value"] = json.loads(annotation["value"]) self.assertEqual(expected, actual) self.assertEqual(expected_annotations, actual_annotations)
def test_translate_to_collector(self): trace_id = 0x6E0C63257DE34C926F9EFCD03927272E span_id = 0x34BF92DEEFC58C92 parent_id = 0x1111111111111111 base_time = 683647322 * 10**9 # in ns start_times = ( base_time, base_time + 150 * 10**6, base_time + 300 * 10**6, ) durations = (50 * 10**6, 100 * 10**6, 200 * 10**6) end_times = ( start_times[0] + durations[0], start_times[1] + durations[1], start_times[2] + durations[2], ) span_context = trace_api.SpanContext( trace_id, span_id, is_remote=False, trace_flags=TraceFlags(TraceFlags.SAMPLED), trace_state=trace_api.TraceState({"testKey": "testValue"}), ) parent_context = trace_api.SpanContext(trace_id, parent_id, is_remote=False) other_context = trace_api.SpanContext(trace_id, span_id, is_remote=False) event_attributes = { "annotation_bool": True, "annotation_string": "annotation_test", "key_float": 0.3, } event_timestamp = base_time + 50 * 10**6 event = trace.Event( name="event0", timestamp=event_timestamp, attributes=event_attributes, ) link_attributes = {"key_bool": True} link_1 = trace_api.Link(context=other_context, attributes=link_attributes) link_2 = trace_api.Link(context=parent_context, attributes=link_attributes) span_1 = trace.Span( name="test1", context=span_context, parent=parent_context, events=(event, ), links=(link_1, ), kind=trace_api.SpanKind.CLIENT, ) span_2 = trace.Span( name="test2", context=parent_context, parent=None, kind=trace_api.SpanKind.SERVER, ) span_3 = trace.Span( name="test3", context=other_context, links=(link_2, ), parent=span_2.get_context(), ) otel_spans = [span_1, span_2, span_3] otel_spans[0].start(start_time=start_times[0]) otel_spans[0].set_attribute("key_bool", False) otel_spans[0].set_attribute("key_string", "hello_world") otel_spans[0].set_attribute("key_float", 111.22) otel_spans[0].set_attribute("key_int", 333) otel_spans[0].set_status( trace_api.Status( trace_api.status.StatusCanonicalCode.INTERNAL, "test description", )) otel_spans[0].end(end_time=end_times[0]) otel_spans[1].start(start_time=start_times[1]) otel_spans[1].set_status( trace_api.Status( trace_api.status.StatusCanonicalCode.INTERNAL, {"test", "val"}, )) otel_spans[1].end(end_time=end_times[1]) otel_spans[2].start(start_time=start_times[2]) otel_spans[2].end(end_time=end_times[2]) output_spans = translate_to_collector(otel_spans) self.assertEqual(len(output_spans), 3) self.assertEqual(output_spans[0].trace_id, b"n\x0cc%}\xe3L\x92o\x9e\xfc\xd09''.") self.assertEqual(output_spans[0].span_id, b"4\xbf\x92\xde\xef\xc5\x8c\x92") self.assertEqual(output_spans[0].name, trace_pb2.TruncatableString(value="test1")) self.assertEqual(output_spans[1].name, trace_pb2.TruncatableString(value="test2")) self.assertEqual(output_spans[2].name, trace_pb2.TruncatableString(value="test3")) self.assertEqual( output_spans[0].start_time.seconds, int(start_times[0] / 1000000000), ) self.assertEqual(output_spans[0].end_time.seconds, int(end_times[0] / 1000000000)) self.assertEqual(output_spans[0].kind, trace_api.SpanKind.CLIENT.value) self.assertEqual(output_spans[1].kind, trace_api.SpanKind.SERVER.value) self.assertEqual(output_spans[0].parent_span_id, b"\x11\x11\x11\x11\x11\x11\x11\x11") self.assertEqual(output_spans[2].parent_span_id, b"\x11\x11\x11\x11\x11\x11\x11\x11") self.assertEqual( output_spans[0].status.code, trace_api.status.StatusCanonicalCode.INTERNAL.value, ) self.assertEqual(output_spans[0].status.message, "test description") self.assertEqual(len(output_spans[0].tracestate.entries), 1) self.assertEqual(output_spans[0].tracestate.entries[0].key, "testKey") self.assertEqual(output_spans[0].tracestate.entries[0].value, "testValue") self.assertEqual( output_spans[0].attributes.attribute_map["key_bool"].bool_value, False, ) self.assertEqual( output_spans[0].attributes.attribute_map["key_string"]. string_value.value, "hello_world", ) self.assertEqual( output_spans[0].attributes.attribute_map["key_float"].double_value, 111.22, ) self.assertEqual( output_spans[0].attributes.attribute_map["key_int"].int_value, 333) self.assertEqual( output_spans[0].time_events.time_event[0].time.seconds, 683647322) self.assertEqual( output_spans[0].time_events.time_event[0].annotation.description. value, "event0", ) self.assertEqual( output_spans[0].time_events.time_event[0].annotation.attributes. attribute_map["annotation_bool"].bool_value, True, ) self.assertEqual( output_spans[0].time_events.time_event[0].annotation.attributes. attribute_map["annotation_string"].string_value.value, "annotation_test", ) self.assertEqual( output_spans[0].time_events.time_event[0].annotation.attributes. attribute_map["key_float"].double_value, 0.3, ) self.assertEqual( output_spans[0].links.link[0].trace_id, b"n\x0cc%}\xe3L\x92o\x9e\xfc\xd09''.", ) self.assertEqual( output_spans[0].links.link[0].span_id, b"4\xbf\x92\xde\xef\xc5\x8c\x92", ) self.assertEqual( output_spans[0].links.link[0].type, trace_pb2.Span.Link.Type.TYPE_UNSPECIFIED, ) self.assertEqual( output_spans[1].status.code, trace_api.status.StatusCanonicalCode.INTERNAL.value, ) self.assertEqual( output_spans[2].links.link[0].type, trace_pb2.Span.Link.Type.PARENT_LINKED_SPAN, ) self.assertEqual( output_spans[0].links.link[0].attributes.attribute_map["key_bool"]. bool_value, True, )
def test_export(self): span_names = ("test1", "test2", "test3", "test4") trace_id = 0x6E0C63257DE34C926F9EFCD03927272E span_id = 0x34BF92DEEFC58C92 parent_id = 0x1111111111111111 other_id = 0x2222222222222222 base_time = 683647322 * 10**9 # in ns start_times = ( base_time, base_time + 150 * 10**6, base_time + 300 * 10**6, base_time + 400 * 10**6, ) durations = (50 * 10**6, 100 * 10**6, 200 * 10**6, 300 * 10**6) end_times = ( start_times[0] + durations[0], start_times[1] + durations[1], start_times[2] + durations[2], start_times[3] + durations[3], ) span_context = trace_api.SpanContext( trace_id, span_id, is_remote=False, trace_flags=TraceFlags(TraceFlags.SAMPLED), ) parent_context = trace_api.SpanContext(trace_id, parent_id, is_remote=False) other_context = trace_api.SpanContext(trace_id, other_id, is_remote=False) event_attributes = { "annotation_bool": True, "annotation_string": "annotation_test", "key_float": 0.3, } event_timestamp = base_time + 50 * 10**6 event = trace.Event( name="event0", timestamp=event_timestamp, attributes=event_attributes, ) link_attributes = {"key_bool": True} link = trace_api.Link(context=other_context, attributes=link_attributes) otel_spans = [ trace.Span( name=span_names[0], context=span_context, parent=parent_context, events=(event, ), links=(link, ), ), trace.Span(name=span_names[1], context=parent_context, parent=None), trace.Span(name=span_names[2], context=other_context, parent=None), trace.Span(name=span_names[3], context=other_context, parent=None), ] otel_spans[0].start(start_time=start_times[0]) otel_spans[0].resource = Resource({}) # added here to preserve order otel_spans[0].set_attribute("key_bool", False) otel_spans[0].set_attribute("key_string", "hello_world") otel_spans[0].set_attribute("key_float", 111.22) otel_spans[0].set_status( Status(StatusCanonicalCode.UNKNOWN, "Example description")) otel_spans[0].end(end_time=end_times[0]) otel_spans[1].start(start_time=start_times[1]) otel_spans[1].resource = Resource( attributes={"key_resource": "some_resource"}) otel_spans[1].end(end_time=end_times[1]) otel_spans[2].start(start_time=start_times[2]) otel_spans[2].set_attribute("key_string", "hello_world") otel_spans[2].resource = Resource( attributes={"key_resource": "some_resource"}) otel_spans[2].end(end_time=end_times[2]) otel_spans[3].start(start_time=start_times[3]) otel_spans[3].resource = Resource({}) otel_spans[3].end(end_time=end_times[3]) otel_spans[3].instrumentation_info = InstrumentationInfo( name="name", version="version") service_name = "test-service" local_endpoint = {"serviceName": service_name, "port": 9411} exporter = ZipkinSpanExporter(service_name) expected = [ { "traceId": format(trace_id, "x"), "id": format(span_id, "x"), "name": span_names[0], "timestamp": start_times[0] // 10**3, "duration": durations[0] // 10**3, "localEndpoint": local_endpoint, "kind": None, "tags": { "key_bool": "False", "key_string": "hello_world", "key_float": "111.22", "otel.status_code": "2", "otel.status_description": "Example description", }, "annotations": [{ "timestamp": event_timestamp // 10**3, "value": "event0", }], "debug": True, "parentId": format(parent_id, "x"), }, { "traceId": format(trace_id, "x"), "id": format(parent_id, "x"), "name": span_names[1], "timestamp": start_times[1] // 10**3, "duration": durations[1] // 10**3, "localEndpoint": local_endpoint, "kind": None, "tags": { "key_resource": "some_resource", "otel.status_code": "0", }, "annotations": None, }, { "traceId": format(trace_id, "x"), "id": format(other_id, "x"), "name": span_names[2], "timestamp": start_times[2] // 10**3, "duration": durations[2] // 10**3, "localEndpoint": local_endpoint, "kind": None, "tags": { "key_string": "hello_world", "key_resource": "some_resource", "otel.status_code": "0", }, "annotations": None, }, { "traceId": format(trace_id, "x"), "id": format(other_id, "x"), "name": span_names[3], "timestamp": start_times[3] // 10**3, "duration": durations[3] // 10**3, "localEndpoint": local_endpoint, "kind": None, "tags": { "otel.instrumentation_library.name": "name", "otel.instrumentation_library.version": "version", "otel.status_code": "0", }, "annotations": None, }, ] mock_post = MagicMock() with patch("requests.post", mock_post): mock_post.return_value = MockResponse(200) status = exporter.export(otel_spans) self.assertEqual(SpanExportResult.SUCCESS, status) mock_post.assert_called_with( url="http://localhost:9411/api/v2/spans", data=json.dumps(expected), headers={"Content-Type": "application/json"}, )
def test_span_members(self): tracer = trace.Tracer("test_span_members") other_context1 = trace_api.SpanContext( trace_id=trace.generate_trace_id(), span_id=trace.generate_span_id(), ) other_context2 = trace_api.SpanContext( trace_id=trace.generate_trace_id(), span_id=trace.generate_span_id(), ) self.assertIsNone(tracer.get_current_span()) with tracer.start_span("root") as root: root.set_attribute("component", "http") root.set_attribute("http.method", "GET") root.set_attribute("http.url", "https://example.com:779/path/12/?q=d#123") root.set_attribute("http.status_code", 200) root.set_attribute("http.status_text", "OK") root.set_attribute("misc.pi", 3.14) # Setting an attribute with the same key as an existing attribute # SHOULD overwrite the existing attribute's value. root.set_attribute("attr-key", "attr-value1") root.set_attribute("attr-key", "attr-value2") root.add_event("event0") root.add_event("event1", {"name": "birthday"}) root.add_link(other_context1) root.add_link(other_context2, {"name": "neighbor"}) root.update_name("toor") self.assertEqual(root.name, "toor") # The public API does not expose getters. # Checks by accessing the span members directly self.assertEqual(len(root.attributes), 7) self.assertEqual(root.attributes["component"], "http") self.assertEqual(root.attributes["http.method"], "GET") self.assertEqual( root.attributes["http.url"], "https://example.com:779/path/12/?q=d#123", ) self.assertEqual(root.attributes["http.status_code"], 200) self.assertEqual(root.attributes["http.status_text"], "OK") self.assertEqual(root.attributes["misc.pi"], 3.14) self.assertEqual(root.attributes["attr-key"], "attr-value2") self.assertEqual(len(root.events), 2) self.assertEqual(root.events[0], trace.Event(name="event0", attributes={})) self.assertEqual( root.events[1], trace.Event(name="event1", attributes={"name": "birthday"}), ) self.assertEqual(len(root.links), 2) self.assertEqual(root.links[0].context.trace_id, other_context1.trace_id) self.assertEqual(root.links[0].context.span_id, other_context1.span_id) self.assertEqual(root.links[0].attributes, {}) self.assertEqual(root.links[1].context.trace_id, other_context2.trace_id) self.assertEqual(root.links[1].context.span_id, other_context2.span_id) self.assertEqual(root.links[1].attributes, {"name": "neighbor"})