diff --git a/azure_monitor/examples/client.py b/azure_monitor/examples/client.py index cc2ae84..6607615 100644 --- a/azure_monitor/examples/client.py +++ b/azure_monitor/examples/client.py @@ -5,16 +5,16 @@ from azure_monitor import AzureMonitorSpanExporter from opentelemetry import trace from opentelemetry.ext import http_requests -from opentelemetry.sdk.trace import Tracer +from opentelemetry.sdk.trace import TracerSource from opentelemetry.sdk.trace.export import BatchExportSpanProcessor -trace.set_preferred_tracer_implementation(lambda T: Tracer()) -tracer = trace.tracer() -http_requests.enable(tracer) +trace.set_preferred_tracer_source_implementation(lambda T: TracerSource()) +tracer = trace.tracer_source().get_tracer(__name__) +http_requests.enable(trace.tracer_source()) span_processor = BatchExportSpanProcessor( AzureMonitorSpanExporter(instrumentation_key="") ) -trace.tracer().add_span_processor(span_processor) +trace.tracer_source().add_span_processor(span_processor) response = requests.get(url="http://127.0.0.1:8080/") span_processor.shutdown() diff --git a/azure_monitor/examples/request.py b/azure_monitor/examples/request.py index c59663b..975ac86 100644 --- a/azure_monitor/examples/request.py +++ b/azure_monitor/examples/request.py @@ -5,17 +5,18 @@ from azure_monitor import AzureMonitorSpanExporter from opentelemetry import trace from opentelemetry.ext import http_requests -from opentelemetry.sdk.trace import Tracer +from opentelemetry.sdk.trace import TracerSource from opentelemetry.sdk.trace.export import SimpleExportSpanProcessor -trace.set_preferred_tracer_implementation(Tracer) +trace.set_preferred_tracer_source_implementation(lambda T: TracerSource()) -http_requests.enable(trace.tracer()) +http_requests.enable(trace.tracer_source()) span_processor = SimpleExportSpanProcessor( AzureMonitorSpanExporter(instrumentation_key="") ) -trace.tracer().add_span_processor(span_processor) +trace.tracer_source().add_span_processor(span_processor) +tracer = trace.tracer_source().get_tracer(__name__) -with trace.tracer().start_as_current_span("parent"): +with tracer.start_as_current_span("parent"): response = requests.get("", timeout=5) diff --git a/azure_monitor/examples/server.py b/azure_monitor/examples/server.py index cf2583f..bdeb4f9 100644 --- a/azure_monitor/examples/server.py +++ b/azure_monitor/examples/server.py @@ -7,24 +7,31 @@ from opentelemetry import trace from opentelemetry.ext import http_requests from opentelemetry.ext.wsgi import OpenTelemetryMiddleware -from opentelemetry.sdk.trace import Tracer +from opentelemetry.sdk.trace import TracerSource from opentelemetry.sdk.trace.export import BatchExportSpanProcessor -trace.set_preferred_tracer_implementation(lambda T: Tracer()) +# The preferred tracer implementation must be set, as the opentelemetry-api +# defines the interface with a no-op implementation. +trace.set_preferred_tracer_source_implementation(lambda T: TracerSource()) +tracer = trace.tracer_source().get_tracer(__name__) -http_requests.enable(trace.tracer()) -span_processor = BatchExportSpanProcessor( - AzureMonitorSpanExporter(instrumentation_key="") -) -trace.tracer().add_span_processor(span_processor) +exporter = AzureMonitorSpanExporter(instrumentation_key="") +# SpanExporter receives the spans and send them to the target location. +span_processor = BatchExportSpanProcessor(exporter) +trace.tracer_source().add_span_processor(span_processor) + +# Integrations are the glue that binds the OpenTelemetry API and the +# frameworks and libraries that are used together, automatically creating +# Spans and propagating context as appropriate. +http_requests.enable(trace.tracer_source()) app = flask.Flask(__name__) app.wsgi_app = OpenTelemetryMiddleware(app.wsgi_app) @app.route("/") def hello(): - with trace.tracer().start_as_current_span("parent"): + with tracer.start_as_current_span("parent"): requests.get("https://www.wikipedia.org/wiki/Rabbit") return "hello" diff --git a/azure_monitor/examples/trace.py b/azure_monitor/examples/trace.py index ae490b0..031fc50 100644 --- a/azure_monitor/examples/trace.py +++ b/azure_monitor/examples/trace.py @@ -2,15 +2,15 @@ # Licensed under the MIT License. from azure_monitor import AzureMonitorSpanExporter from opentelemetry import trace -from opentelemetry.sdk.trace import Tracer +from opentelemetry.sdk.trace import TracerSource from opentelemetry.sdk.trace.export import SimpleExportSpanProcessor -trace.set_preferred_tracer_implementation(lambda T: Tracer()) -tracer = trace.tracer() +trace.set_preferred_tracer_source_implementation(lambda T: TracerSource()) span_processor = SimpleExportSpanProcessor( AzureMonitorSpanExporter(instrumentation_key="") ) -trace.tracer().add_span_processor(span_processor) +trace.tracer_source().add_span_processor(span_processor) +tracer = trace.tracer_source().get_tracer(__name__) with tracer.start_as_current_span("hello") as span: print("Hello, World!") diff --git a/azure_monitor/src/azure_monitor/trace.py b/azure_monitor/src/azure_monitor/trace.py index 1c61fe7..14da4cd 100644 --- a/azure_monitor/src/azure_monitor/trace.py +++ b/azure_monitor/src/azure_monitor/trace.py @@ -10,6 +10,7 @@ from opentelemetry.sdk.trace.export import SpanExporter, SpanExportResult from opentelemetry.sdk.util import ns_to_iso_str from opentelemetry.trace import Span, SpanKind +from opentelemetry.trace.status import StatusCanonicalCode logger = logging.getLogger(__name__) @@ -78,16 +79,16 @@ def span_to_envelope(self, span): # noqa pylint: disable=too-many-branches if parent: envelope.tags[ "ai.operation.parentId" - ] = "|{:032x}.{:016x}.".format(parent.trace_id, parent.span_id) + ] = "{:016x}".format(parent.span_id) if span.kind in (SpanKind.CONSUMER, SpanKind.SERVER): envelope.name = "Microsoft.ApplicationInsights.Request" data = protocol.Request( - id="|{:032x}.{:016x}.".format( - span.context.trace_id, span.context.span_id + id="{:016x}".format( + span.context.span_id ), duration=utils.ns_to_duration(span.end_time - span.start_time), - responseCode="0", - success=False, + responseCode=str(span.status.value), + success=False, # Modify based off attributes or Status properties={}, ) envelope.data = protocol.Data( @@ -104,16 +105,18 @@ def span_to_envelope(self, span): # noqa pylint: disable=too-many-branches status_code = span.attributes["http.status_code"] data.responseCode = str(status_code) data.success = 200 <= status_code < 400 + elif span.status == StatusCanonicalCode.OK: + data.success = True else: envelope.name = "Microsoft.ApplicationInsights.RemoteDependency" data = protocol.RemoteDependency( name=span.name, - id="|{:032x}.{:016x}.".format( - span.context.trace_id, span.context.span_id + id="{:016x}".format( + span.context.span_id ), - resultCode="0", # TODO + resultCode=str(span.status.value), duration=utils.ns_to_duration(span.end_time - span.start_time), - success=True, # TODO + success=False, # Modify based off attributes or Status properties={}, ) envelope.data = protocol.Data( @@ -136,10 +139,17 @@ def span_to_envelope(self, span): # noqa pylint: disable=too-many-branches data.name = span.attributes["http.method"] \ + "/" + parse_url.path if "http.status_code" in span.attributes: - data.resultCode = str(span.attributes["http.status_code"]) + status_code = span.attributes["http.status_code"] + data.resultCode = str(status_code) + data.success = 200 <= status_code < 400 + elif span.status == StatusCanonicalCode.OK: + data.success = True else: # SpanKind.INTERNAL data.type = "InProc" for key in span.attributes: + # This removes redundant data from ApplicationInsights + if key.startswith('http.'): + continue data.properties[key] = span.attributes[key] if span.links: links = [] @@ -149,8 +159,8 @@ def span_to_envelope(self, span): # noqa pylint: disable=too-many-branches "operation_Id": "{:032x}".format( link.context.trace_id ), - "id": "|{:032x}.{:016x}.".format( - link.context.trace_id, link.context.span_id + "id": "{:016x}".format( + link.context.span_id ), } ) diff --git a/azure_monitor/tests/trace/test_trace.py b/azure_monitor/tests/trace/test_trace.py index c232f32..ab76f6f 100644 --- a/azure_monitor/tests/trace/test_trace.py +++ b/azure_monitor/tests/trace/test_trace.py @@ -16,9 +16,9 @@ def test_ctor(self): Options._default.instrumentation_key = instrumentation_key def test_span_to_envelope(self): - from opentelemetry.trace import SpanKind + from opentelemetry.trace import Link, SpanContext, SpanKind + from opentelemetry.trace.status import StatusCanonicalCode from opentelemetry.sdk.trace import Span - from opentelemetry.trace import SpanContext exporter = AzureMonitorSpanExporter( instrumentation_key='12345678-1234-5678-abcd-12345678abcd' @@ -56,6 +56,7 @@ def test_span_to_envelope(self): links=None, kind=SpanKind.CLIENT ) + span.status = StatusCanonicalCode.OK span.start_time = start_time span.end_time = end_time envelope = exporter.span_to_envelope(span) @@ -67,7 +68,7 @@ def test_span_to_envelope(self): 'Microsoft.ApplicationInsights.RemoteDependency') self.assertEqual( envelope.tags['ai.operation.parentId'], - '|1bbd944a73a05d89eab5d3740a213ee7.a6f5d48acb4d31da.') + 'a6f5d48acb4d31da') self.assertEqual( envelope.tags['ai.operation.id'], '1bbd944a73a05d89eab5d3740a213ee7') @@ -85,7 +86,7 @@ def test_span_to_envelope(self): 'www.wikipedia.org') self.assertEqual( envelope.data.baseData.id, - '|1bbd944a73a05d89eab5d3740a213ee7.a6f5d48acb4d31d9.') + 'a6f5d48acb4d31d9') self.assertEqual( envelope.data.baseData.resultCode, '200') @@ -115,6 +116,7 @@ def test_span_to_envelope(self): links=None, kind=SpanKind.CLIENT ) + span.status = StatusCanonicalCode.OK span.start_time = start_time span.end_time = end_time envelope = exporter.span_to_envelope(span) @@ -126,7 +128,7 @@ def test_span_to_envelope(self): 'Microsoft.ApplicationInsights.RemoteDependency') self.assertEqual( envelope.tags['ai.operation.parentId'], - '|1bbd944a73a05d89eab5d3740a213ee7.a6f5d48acb4d31da.') + 'a6f5d48acb4d31da') self.assertEqual( envelope.tags['ai.operation.id'], '1bbd944a73a05d89eab5d3740a213ee7') @@ -138,7 +140,7 @@ def test_span_to_envelope(self): 'test') self.assertEqual( envelope.data.baseData.id, - '|1bbd944a73a05d89eab5d3740a213ee7.a6f5d48acb4d31d9.') + 'a6f5d48acb4d31d9') self.assertEqual( envelope.data.baseData.duration, '0.00:00:01.001') @@ -169,6 +171,7 @@ def test_span_to_envelope(self): links=None, kind=SpanKind.CLIENT ) + span.status = StatusCanonicalCode.OK span.start_time = start_time span.end_time = end_time envelope = exporter.span_to_envelope(span) @@ -180,7 +183,7 @@ def test_span_to_envelope(self): 'Microsoft.ApplicationInsights.RemoteDependency') self.assertEqual( envelope.tags['ai.operation.parentId'], - '|1bbd944a73a05d89eab5d3740a213ee7.a6f5d48acb4d31da.') + 'a6f5d48acb4d31da') self.assertEqual( envelope.tags['ai.operation.id'], '1bbd944a73a05d89eab5d3740a213ee7') @@ -198,7 +201,7 @@ def test_span_to_envelope(self): 'www.wikipedia.org') self.assertEqual( envelope.data.baseData.id, - '|1bbd944a73a05d89eab5d3740a213ee7.a6f5d48acb4d31d9.') + 'a6f5d48acb4d31d9') self.assertEqual( envelope.data.baseData.resultCode, '200') @@ -235,6 +238,7 @@ def test_span_to_envelope(self): links=None, kind=SpanKind.SERVER ) + span.status = StatusCanonicalCode.OK span.start_time = start_time span.end_time = end_time envelope = exporter.span_to_envelope(span) @@ -246,7 +250,7 @@ def test_span_to_envelope(self): 'Microsoft.ApplicationInsights.Request') self.assertEqual( envelope.tags['ai.operation.parentId'], - '|1bbd944a73a05d89eab5d3740a213ee7.a6f5d48acb4d31da.') + 'a6f5d48acb4d31da') self.assertEqual( envelope.tags['ai.operation.id'], '1bbd944a73a05d89eab5d3740a213ee7') @@ -258,7 +262,7 @@ def test_span_to_envelope(self): '2019-12-04T21:18:36.027613Z') self.assertEqual( envelope.data.baseData.id, - '|1bbd944a73a05d89eab5d3740a213ee7.a6f5d48acb4d31d9.') + 'a6f5d48acb4d31d9') self.assertEqual( envelope.data.baseData.duration, '0.00:00:01.001') @@ -301,6 +305,7 @@ def test_span_to_envelope(self): links=None, kind=SpanKind.SERVER ) + span.status = StatusCanonicalCode.OK span.start_time = start_time span.end_time = end_time envelope = exporter.span_to_envelope(span) @@ -312,7 +317,7 @@ def test_span_to_envelope(self): 'Microsoft.ApplicationInsights.Request') self.assertEqual( envelope.tags['ai.operation.parentId'], - '|1bbd944a73a05d89eab5d3740a213ee7.a6f5d48acb4d31da.') + 'a6f5d48acb4d31da') self.assertEqual( envelope.tags['ai.operation.id'], '1bbd944a73a05d89eab5d3740a213ee7') @@ -324,7 +329,7 @@ def test_span_to_envelope(self): '2019-12-04T21:18:36.027613Z') self.assertEqual( envelope.data.baseData.id, - '|1bbd944a73a05d89eab5d3740a213ee7.a6f5d48acb4d31d9.') + 'a6f5d48acb4d31d9') self.assertEqual( envelope.data.baseData.duration, '0.00:00:01.001') @@ -367,6 +372,7 @@ def test_span_to_envelope(self): links=None, kind=SpanKind.SERVER ) + span.status = StatusCanonicalCode.OK span.start_time = start_time span.end_time = end_time envelope = exporter.span_to_envelope(span) @@ -378,7 +384,7 @@ def test_span_to_envelope(self): 'Microsoft.ApplicationInsights.Request') self.assertEqual( envelope.tags['ai.operation.parentId'], - '|1bbd944a73a05d89eab5d3740a213ee7.a6f5d48acb4d31da.') + 'a6f5d48acb4d31da') self.assertEqual( envelope.tags['ai.operation.id'], '1bbd944a73a05d89eab5d3740a213ee7') @@ -387,7 +393,7 @@ def test_span_to_envelope(self): '2019-12-04T21:18:36.027613Z') self.assertEqual( envelope.data.baseData.id, - '|1bbd944a73a05d89eab5d3740a213ee7.a6f5d48acb4d31d9.') + 'a6f5d48acb4d31d9') self.assertEqual( envelope.data.baseData.duration, '0.00:00:01.001') @@ -411,6 +417,7 @@ def test_span_to_envelope(self): links=None, kind=SpanKind.INTERNAL ) + span.status = StatusCanonicalCode.OK span.start_time = start_time span.end_time = end_time envelope = exporter.span_to_envelope(span) @@ -437,10 +444,246 @@ def test_span_to_envelope(self): '0.00:00:01.001') self.assertEqual( envelope.data.baseData.id, - '|1bbd944a73a05d89eab5d3740a213ee7.a6f5d48acb4d31d9.') + 'a6f5d48acb4d31d9') self.assertEqual( envelope.data.baseData.type, 'InProc') self.assertEqual( envelope.data.baseType, 'RemoteDependencyData') + + # Attributes + span = Span( + name='test', + context=SpanContext( + trace_id=36873507687745823477771305566750195431, + span_id=12030755672171557337, + ), + parent=parent_span, + sampler=None, + trace_config=None, + resource=None, + attributes={ + 'component': 'http', + 'http.method': 'GET', + 'http.url': 'https://www.wikipedia.org/wiki/Rabbit', + 'http.status_code': 200, + 'test': 'asd' + }, + events=None, + links=None, + kind=SpanKind.CLIENT + ) + span.status = StatusCanonicalCode.OK + span.start_time = start_time + span.end_time = end_time + envelope = exporter.span_to_envelope(span) + self.assertEqual( + len(envelope.data.baseData.properties), 2) + self.assertEqual( + envelope.data.baseData.properties['component'], 'http') + self.assertEqual(envelope.data.baseData.properties['test'], 'asd') + + # Links + links = [] + links.append(Link(context=SpanContext( + trace_id=36873507687745823477771305566750195432, + span_id=12030755672171557338, + ))) + span = Span( + name='test', + context=SpanContext( + trace_id=36873507687745823477771305566750195431, + span_id=12030755672171557337, + ), + parent=parent_span, + sampler=None, + trace_config=None, + resource=None, + attributes={ + 'component': 'http', + 'http.method': 'GET', + 'http.url': 'https://www.wikipedia.org/wiki/Rabbit', + 'http.status_code': 200, + }, + events=None, + links=links, + kind=SpanKind.CLIENT + ) + span.status = StatusCanonicalCode.OK + span.start_time = start_time + span.end_time = end_time + envelope = exporter.span_to_envelope(span) + self.assertEqual( + len(envelope.data.baseData.properties), 2) + links_json = '[{"operation_Id": ' + \ + '"1bbd944a73a05d89eab5d3740a213ee8", "id": "a6f5d48acb4d31da"}]' + self.assertEqual(envelope.data.baseData.properties['_MS.links'], links_json) + + + # Status + span = Span( + name='test', + context=SpanContext( + trace_id=36873507687745823477771305566750195431, + span_id=12030755672171557337, + ), + parent=parent_span, + sampler=None, + trace_config=None, + resource=None, + attributes={ + 'component': 'http', + 'http.method': 'GET', + 'http.url': 'https://www.wikipedia.org/wiki/Rabbit', + 'http.status_code': 500, + }, + events=None, + links=None, + kind=SpanKind.SERVER + ) + span.status = StatusCanonicalCode.OK + span.start_time = start_time + span.end_time = end_time + envelope = exporter.span_to_envelope(span) + self.assertEqual( + envelope.data.baseData.responseCode, '500') + self.assertFalse(envelope.data.baseData.success) + + span = Span( + name='test', + context=SpanContext( + trace_id=36873507687745823477771305566750195431, + span_id=12030755672171557337, + ), + parent=parent_span, + sampler=None, + trace_config=None, + resource=None, + attributes={ + 'component': 'http', + 'http.method': 'GET', + 'http.url': 'https://www.wikipedia.org/wiki/Rabbit', + 'http.status_code': 500, + }, + events=None, + links=None, + kind=SpanKind.CLIENT + ) + span.status = StatusCanonicalCode.OK + span.start_time = start_time + span.end_time = end_time + envelope = exporter.span_to_envelope(span) + self.assertEqual( + envelope.data.baseData.resultCode, '500') + self.assertFalse(envelope.data.baseData.success) + + span = Span( + name='test', + context=SpanContext( + trace_id=36873507687745823477771305566750195431, + span_id=12030755672171557337, + ), + parent=parent_span, + sampler=None, + trace_config=None, + resource=None, + attributes={ + 'component': 'http', + 'http.method': 'GET', + 'http.url': 'https://www.wikipedia.org/wiki/Rabbit', + }, + events=None, + links=None, + kind=SpanKind.SERVER + ) + span.status = StatusCanonicalCode.OK + span.start_time = start_time + span.end_time = end_time + span.status = StatusCanonicalCode.OK + envelope = exporter.span_to_envelope(span) + self.assertEqual( + envelope.data.baseData.responseCode, '0') + self.assertTrue(envelope.data.baseData.success) + + span = Span( + name='test', + context=SpanContext( + trace_id=36873507687745823477771305566750195431, + span_id=12030755672171557337, + ), + parent=parent_span, + sampler=None, + trace_config=None, + resource=None, + attributes={ + 'component': 'http', + 'http.method': 'GET', + 'http.url': 'https://www.wikipedia.org/wiki/Rabbit', + }, + events=None, + links=None, + kind=SpanKind.CLIENT + ) + span.status = StatusCanonicalCode.OK + span.start_time = start_time + span.end_time = end_time + span.status = StatusCanonicalCode.OK + envelope = exporter.span_to_envelope(span) + self.assertEqual( + envelope.data.baseData.resultCode, '0') + self.assertTrue(envelope.data.baseData.success) + + span = Span( + name='test', + context=SpanContext( + trace_id=36873507687745823477771305566750195431, + span_id=12030755672171557337, + ), + parent=parent_span, + sampler=None, + trace_config=None, + resource=None, + attributes={ + 'component': 'http', + 'http.method': 'GET', + 'http.url': 'https://www.wikipedia.org/wiki/Rabbit', + }, + events=None, + links=None, + kind=SpanKind.SERVER + ) + span.start_time = start_time + span.end_time = end_time + span.status = StatusCanonicalCode.UNKNOWN + envelope = exporter.span_to_envelope(span) + self.assertEqual( + envelope.data.baseData.responseCode, '2') + self.assertFalse(envelope.data.baseData.success) + + span = Span( + name='test', + context=SpanContext( + trace_id=36873507687745823477771305566750195431, + span_id=12030755672171557337, + ), + parent=parent_span, + sampler=None, + trace_config=None, + resource=None, + attributes={ + 'component': 'http', + 'http.method': 'GET', + 'http.url': 'https://www.wikipedia.org/wiki/Rabbit', + }, + events=None, + links=None, + kind=SpanKind.CLIENT + ) + span.start_time = start_time + span.end_time = end_time + span.status = StatusCanonicalCode.UNKNOWN + envelope = exporter.span_to_envelope(span) + self.assertEqual( + envelope.data.baseData.resultCode, '2') + self.assertFalse(envelope.data.baseData.success)