def setUp(self): self.exporter = OTLPLogExporter() self.server = server(ThreadPoolExecutor(max_workers=10)) self.server.add_insecure_port("[::]:4317") self.server.start() self.log_data_1 = LogData( log_record=LogRecord( timestamp=int(time.time() * 1e9), trace_id=2604504634922341076776623263868986797, span_id=5213367945872657620, trace_flags=TraceFlags(0x01), severity_text="WARNING", severity_number=SDKSeverityNumber.WARN, name="name", body="Zhengzhou, We have a heaviest rains in 1000 years", resource=SDKResource({"key": "value"}), attributes={ "a": 1, "b": "c" }, ), instrumentation_info=InstrumentationInfo("first_name", "first_version"), ) self.log_data_2 = LogData( log_record=LogRecord( timestamp=int(time.time() * 1e9), trace_id=2604504634922341076776623263868986799, span_id=5213367945872657623, trace_flags=TraceFlags(0x01), severity_text="INFO", severity_number=SDKSeverityNumber.INFO2, name="info name", body="Sydney, Opera House is closed", resource=SDKResource({"key": "value"}), attributes={"custom_attr": [1, 2, 3]}, ), instrumentation_info=InstrumentationInfo("second_name", "second_version"), ) self.log_data_3 = LogData( log_record=LogRecord( timestamp=int(time.time() * 1e9), trace_id=2604504634922341076776623263868986800, span_id=5213367945872657628, trace_flags=TraceFlags(0x01), severity_text="ERROR", severity_number=SDKSeverityNumber.WARN, name="error name", body="Mumbai, Boil water before drinking", resource=SDKResource({"service": "myapp"}), ), instrumentation_info=InstrumentationInfo("third_name", "third_version"), )
def test_instrumentation_info(self): tracer_provider = trace.TracerProvider() tracer1 = tracer_provider.get_tracer("instr1") tracer2 = tracer_provider.get_tracer("instr2", "1.3b3") span1 = tracer1.start_span("s1") span2 = tracer2.start_span("s2") self.assertEqual(span1.instrumentation_info, InstrumentationInfo("instr1", "")) self.assertEqual(span2.instrumentation_info, InstrumentationInfo("instr2", "1.3b3")) self.assertEqual(span2.instrumentation_info.version, "1.3b3") self.assertEqual(span2.instrumentation_info.name, "instr2") self.assertLess(span1.instrumentation_info, span2.instrumentation_info) # Check sortability.
def test_export(self): # pylint: disable=no-self-use """Check that the console exporter prints log records.""" log_data = LogData( log_record=LogRecord( timestamp=int(time.time() * 1e9), trace_id=2604504634922341076776623263868986797, span_id=5213367945872657620, trace_flags=TraceFlags(0x01), severity_text="WARN", severity_number=SeverityNumber.WARN, name="name", body="Zhengzhou, We have a heaviest rains in 1000 years", resource=SDKResource({"key": "value"}), attributes={"a": 1, "b": "c"}, ), instrumentation_info=InstrumentationInfo( "first_name", "first_version" ), ) exporter = ConsoleExporter() # Mocking stdout interferes with debugging and test reporting, mock on # the exporter instance instead. with patch.object(exporter, "out") as mock_stdout: exporter.export([log_data]) mock_stdout.write.assert_called_once_with( log_data.log_record.to_json() + os.linesep ) self.assertEqual(mock_stdout.write.call_count, 1) self.assertEqual(mock_stdout.flush.call_count, 1)
def setUpClass(cls): os.environ.clear() os.environ[ "APPINSIGHTS_INSTRUMENTATIONKEY"] = "1234abcd-5678-4efa-8abc-1234567890ab" cls._exporter = AzureMonitorLogExporter() cls._log_data = _logs.LogData( _logs.LogRecord( timestamp=1646865018558419456, trace_id=125960616039069540489478540494783893221, span_id=2909973987304607650, severity_text="WARNING", trace_flags=None, severity_number=SeverityNumber.WARN, name=None, body="Test message", resource=Resource.create(attributes={"asd": "test_resource"}), attributes={"test": "attribute"}, ), InstrumentationInfo("test_name"), ) cls._exc_data = _logs.LogData( _logs.LogRecord( timestamp=1646865018558419456, trace_id=125960616039069540489478540494783893221, span_id=2909973987304607650, severity_text="EXCEPTION", trace_flags=None, severity_number=SeverityNumber.FATAL, name=None, body="Test message", resource=Resource.create(attributes={"asd": "test_resource"}), attributes={ "test": "attribute", SpanAttributes.EXCEPTION_TYPE: "ZeroDivisionError", SpanAttributes.EXCEPTION_MESSAGE: "division by zero", SpanAttributes.EXCEPTION_STACKTRACE: 'Traceback (most recent call last):\n File "test.py", line 38, in <module>\n raise ZeroDivisionError()\nZeroDivisionError\n' }, ), InstrumentationInfo("test_name"), )
def setUp(self): tracer_provider = TracerProvider() self.exporter = OTLPSpanExporter(insecure=True) tracer_provider.add_span_processor( SimpleExportSpanProcessor(self.exporter) ) self.tracer = tracer_provider.get_tracer(__name__) self.server = server(ThreadPoolExecutor(max_workers=10)) self.server.add_insecure_port("[::]:55680") self.server.start() event_mock = Mock( **{ "timestamp": 1591240820506462784, "attributes": OrderedDict([("a", 1), ("b", False)]), } ) type(event_mock).name = PropertyMock(return_value="a") self.span = _Span( "a", context=Mock( **{ "trace_state": OrderedDict([("a", "b"), ("c", "d")]), "span_id": 10217189687419569865, "trace_id": 67545097771067222548457157018666467027, } ), resource=SDKResource(OrderedDict([("a", 1), ("b", False)])), parent=Mock(**{"span_id": 12345}), attributes=OrderedDict([("a", 1), ("b", True)]), events=[event_mock], links=[ Mock( **{ "context.trace_id": 1, "context.span_id": 2, "attributes": OrderedDict([("a", 1), ("b", False)]), "kind": OTLPSpan.SpanKind.SPAN_KIND_INTERNAL, # pylint: disable=no-member } ) ], instrumentation_info=InstrumentationInfo( name="name", version="version" ), ) self.span.start() self.span.end() Configuration._reset() # pylint: disable=protected-access
def get_log_emitter( self, instrumenting_module_name: str, instrumenting_module_verison: str = "", ) -> LogEmitter: return LogEmitter( self._resource, self._multi_log_processor, InstrumentationInfo(instrumenting_module_name, instrumenting_module_verison), )
def get_meter( self, instrumenting_module_name: str, instrumenting_library_version: str = "", ) -> "metrics_api.Meter": if not instrumenting_module_name: # Reject empty strings too. raise ValueError("get_meter called with missing module name.") return Meter( self, InstrumentationInfo(instrumenting_module_name, instrumenting_library_version), )
def _generate_metric(name, point) -> Metric: return Metric( resource=SDKResource(OrderedDict([("a", 1), ("b", False)])), instrumentation_info=InstrumentationInfo( "first_name", "first_version" ), attributes=BoundedAttributes(attributes={"a": 1, "b": True}), description="foo", name=name, unit="s", point=point, )
def get_tracer( self, instrumenting_module_name: str, instrumenting_library_version: str = "", ) -> "trace_api.Tracer": if not instrumenting_module_name: # Reject empty strings too. instrumenting_module_name = "ERROR:MISSING MODULE NAME" logger.error("get_tracer called with missing module name.") return Tracer( self, InstrumentationInfo(instrumenting_module_name, instrumenting_library_version), )
def get_meter( self, name: str, version: Optional[str] = None, schema_url: Optional[str] = None, ) -> Meter: if self._shutdown: _logger.warning( "A shutdown `MeterProvider` can not provide a `Meter`") return _DefaultMeter(name, version=version, schema_url=schema_url) return Meter(InstrumentationInfo(name, version, schema_url), self)
def get_meter( self, instrumenting_module_name: str, instrumenting_library_version: str = "", ) -> "metrics_api.Meter": """See `opentelemetry.metrics.MeterProvider`.get_meter.""" if not instrumenting_module_name: # Reject empty strings too. instrumenting_module_name = "ERROR:MISSING MODULE NAME" logger.error("get_meter called with missing module name.") return Accumulator( self, InstrumentationInfo(instrumenting_module_name, instrumenting_library_version), )
def _create_span_with_status(status: SDKStatus): span = _Span( "a", context=Mock( **{ "trace_state": OrderedDict([("a", "b"), ("c", "d")]), "span_id": 10217189687419569865, "trace_id": 67545097771067222548457157018666467027, }), parent=Mock(**{"span_id": 12345}), instrumentation_info=InstrumentationInfo(name="name", version="version"), ) span.set_status(status) return span
def test_span_types(self): test_instrumentations = [ "opentelemetry.instrumentation.aiohttp-client", "opentelemetry.instrumentation.dbapi", "opentelemetry.instrumentation.django", "opentelemetry.instrumentation.flask", "opentelemetry.instrumentation.grpc", "opentelemetry.instrumentation.jinja2", "opentelemetry.instrumentation.mysql", "opentelemetry.instrumentation.psycopg2", "opentelemetry.instrumentation.pymongo", "opentelemetry.instrumentation.pymysql", "opentelemetry.instrumentation.redis", "opentelemetry.instrumentation.requests", "opentelemetry.instrumentation.sqlalchemy", "opentelemetry.instrumentation.wsgi", ] for index, instrumentation in enumerate(test_instrumentations): # change tracer's instrumentation info before starting span self.tracer.instrumentation_info = InstrumentationInfo( instrumentation, "0" ) with self.tracer.start_span(str(index)): pass datadog_spans = get_spans(self.tracer, self.exporter) self.assertEqual(len(datadog_spans), 14) actual = [span.get("type") for span in datadog_spans] expected = [ "http", "sql", "web", "web", "grpc", "template", "sql", "sql", "mongodb", "sql", "redis", "http", "sql", "web", ] self.assertEqual(actual, expected)
def get_tracer( self, instrumenting_module_name: str, instrumenting_library_version: str = "", ) -> "trace_api.Tracer": if not instrumenting_module_name: # Reject empty strings too. instrumenting_module_name = "ERROR:MISSING MODULE NAME" logger.error("get_tracer called with missing module name.") return Tracer( self.sampler, self.resource, self._active_span_processor, self.ids_generator, InstrumentationInfo(instrumenting_module_name, instrumenting_library_version), )
def test_export_custom(self): # pylint: disable=no-self-use """Check that console exporter uses custom io, formatter.""" mock_record_str = Mock(str) def formatter(record): # pylint: disable=unused-argument return mock_record_str mock_stdout = Mock() exporter = ConsoleExporter(out=mock_stdout, formatter=formatter) log_data = LogData( log_record=LogRecord(), instrumentation_info=InstrumentationInfo( "first_name", "first_version" ), ) exporter.export([log_data]) mock_stdout.write.assert_called_once_with(mock_record_str)
def get_tracer( self, instrumenting_module_name: str, instrumenting_library_version: typing.Optional[str] = None, schema_url: typing.Optional[str] = None, ) -> "trace_api.Tracer": if not instrumenting_module_name: # Reject empty strings too. instrumenting_module_name = "" logger.error("get_tracer called with missing module name.") if instrumenting_library_version is None: instrumenting_library_version = "" return Tracer( self.sampler, self.resource, self._active_span_processor, self.id_generator, InstrumentationInfo( instrumenting_module_name, instrumenting_library_version, schema_url, ), self._span_limits, )
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 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): 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_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_datadog(self): # pylint: disable=invalid-name self.maxDiff = None span_names = ("test1", "test2", "test3") trace_id = 0x6E0C63257DE34C926F9EFCD03927272E 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_context = trace_api.SpanContext(trace_id, parent_id, is_remote=False) other_context = trace_api.SpanContext(trace_id, other_id, is_remote=False) instrumentation_info = InstrumentationInfo(__name__, "0") otel_spans = [ trace.Span( name=span_names[0], context=span_context, parent=parent_context, kind=trace_api.SpanKind.CLIENT, instrumentation_info=instrumentation_info, ), trace.Span( name=span_names[1], context=parent_context, parent=None, instrumentation_info=instrumentation_info, ), trace.Span( name=span_names[2], context=other_context, parent=None, ), ] otel_spans[0].start(start_time=start_times[0]) 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].end(end_time=end_times[2]) # pylint: disable=protected-access exporter = datadog.DatadogSpanExporter() datadog_spans = [ span.to_dict() for span in exporter._translate_to_datadog(otel_spans) ] expected_spans = [ dict( trace_id=trace_id_low, parent_id=parent_id, span_id=span_id, name="tests.test_datadog_exporter.CLIENT", resource=span_names[0], start=start_times[0], duration=durations[0], error=0, service="test-service", meta={ "env": "test", "team": "testers" }, ), dict( trace_id=trace_id_low, parent_id=0, span_id=parent_id, name="tests.test_datadog_exporter.INTERNAL", resource=span_names[1], start=start_times[1], duration=durations[1], error=0, service="test-service", meta={ "env": "test", "team": "testers", "version": "0.0.1" }, ), dict( trace_id=trace_id_low, parent_id=0, span_id=other_id, name=span_names[2], resource=span_names[2], start=start_times[2], duration=durations[2], error=0, service="test-service", meta={ "env": "test", "team": "testers", "version": "0.0.1" }, ), ] self.assertEqual(datadog_spans, expected_spans)
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_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)