From 1231f98bfbecb6130ab29c6d77292dfd8f7a31f8 Mon Sep 17 00:00:00 2001 From: Reiley Yang Date: Thu, 26 Sep 2019 20:33:12 -0700 Subject: [PATCH 01/23] avoid requests hijack while running inside exporter --- opentelemetry-sdk/src/opentelemetry/sdk/trace/export/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/trace/export/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/trace/export/__init__.py index 2f42c19c39..95e7d4419c 100644 --- a/opentelemetry-sdk/src/opentelemetry/sdk/trace/export/__init__.py +++ b/opentelemetry-sdk/src/opentelemetry/sdk/trace/export/__init__.py @@ -22,6 +22,7 @@ from opentelemetry.sdk import util from .. import Span, SpanProcessor +from opentelemetry.context import Context logger = logging.getLogger(__name__) From df2bb0b27e89479715f229ac22b3baf218d213ab Mon Sep 17 00:00:00 2001 From: Reiley Yang Date: Thu, 26 Sep 2019 20:40:57 -0700 Subject: [PATCH 02/23] lint --- opentelemetry-sdk/src/opentelemetry/sdk/trace/export/__init__.py | 1 - 1 file changed, 1 deletion(-) diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/trace/export/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/trace/export/__init__.py index 95e7d4419c..2f42c19c39 100644 --- a/opentelemetry-sdk/src/opentelemetry/sdk/trace/export/__init__.py +++ b/opentelemetry-sdk/src/opentelemetry/sdk/trace/export/__init__.py @@ -22,7 +22,6 @@ from opentelemetry.sdk import util from .. import Span, SpanProcessor -from opentelemetry.context import Context logger = logging.getLogger(__name__) From d516a253d14f81d77b4b183b2b6bc0eb5e684ebc Mon Sep 17 00:00:00 2001 From: Reiley Yang Date: Wed, 25 Sep 2019 20:34:18 -0700 Subject: [PATCH 03/23] azmon tracer exporter initial commit --- .../README.rst | 7 + .../ext/azure_monitor/protocol.py | 206 ++++++++++++++++++ .../opentelemetry/ext/azure_monitor/trace.py | 89 +++++++- .../opentelemetry/ext/azure_monitor/util.py | 44 ++++ 4 files changed, 344 insertions(+), 2 deletions(-) create mode 100644 ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/protocol.py create mode 100644 ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/util.py diff --git a/ext/opentelemetry-ext-azure-monitor/README.rst b/ext/opentelemetry-ext-azure-monitor/README.rst index d8c3e16074..976d9a531e 100644 --- a/ext/opentelemetry-ext-azure-monitor/README.rst +++ b/ext/opentelemetry-ext-azure-monitor/README.rst @@ -3,6 +3,13 @@ OpenTelemetry Azure Monitor Exporters This library provides integration with Microsoft Azure Monitor. +Installation +------------ + +:: + + pip install opentelemetry-ext-azure-monitor + References ---------- diff --git a/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/protocol.py b/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/protocol.py new file mode 100644 index 0000000000..1d72a65587 --- /dev/null +++ b/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/protocol.py @@ -0,0 +1,206 @@ +# Copyright 2019, OpenTelemetry Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +class BaseObject(dict): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + for key in kwargs: + self[key] = kwargs[key] + + def __repr__(self): + tmp = {} + while True: + for item in self.items(): + if item[0] not in tmp: + tmp[item[0]] = item[1] + if self._default == self: + break + self = self._default + return repr(tmp) + + def __setattr__(self, name, value): + self[name] = value + + def __getattr__(self, name): + try: + return self[name] + except KeyError: + raise AttributeError("'{}' object has no attribute {}".format( + type(self).__name__, + name, + )) + + def __getitem__(self, key): + if self._default is self: + return super().__getitem__(key) + if key in self: + return super().__getitem__(key) + return self._default[key] + + +BaseObject._default = BaseObject() + + +class Data(BaseObject): + _default = BaseObject( + baseData=None, + baseType=None, + ) + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.baseData = self.baseData + self.baseType = self.baseType + + +class DataPoint(BaseObject): + _default = BaseObject( + ns='', + name='', + kind=None, + value=0.0, + count=None, + min=None, + max=None, + stdDev=None, + ) + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.name = self.name + self.value = self.value + + +class Envelope(BaseObject): + _default = BaseObject( + ver=1, + name='', + time='', + sampleRate=None, + seq=None, + iKey=None, + flags=None, + tags=None, + data=None, + ) + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.name = self.name + self.time = self.time + + +class Event(BaseObject): + _default = BaseObject( + ver=2, + name='', + properties=None, + measurements=None, + ) + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.ver = self.ver + self.name = self.name + + +class ExceptionData(BaseObject): + _default = BaseObject( + ver=2, + exceptions=[], + severityLevel=None, + problemId=None, + properties=None, + measurements=None, + ) + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.ver = self.ver + self.exceptions = self.exceptions + + +class Message(BaseObject): + _default = BaseObject( + ver=2, + message='', + severityLevel=None, + properties=None, + measurements=None, + ) + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.ver = self.ver + self.message = self.message + + +class MetricData(BaseObject): + _default = BaseObject( + ver=2, + metrics=[], + properties=None, + ) + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.ver = self.ver + self.metrics = self.metrics + + +class RemoteDependency(BaseObject): + _default = BaseObject( + ver=2, + name='', + id='', + resultCode='', + duration='', + success=True, + data=None, + type=None, + target=None, + properties=None, + measurements=None, + ) + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.ver = self.ver + self.name = self.name + self.resultCode = self.resultCode + self.duration = self.duration + + +class Request(BaseObject): + _default = BaseObject( + ver=2, + id='', + duration='', + responseCode='', + success=True, + source=None, + name=None, + url=None, + properties=None, + measurements=None, + ) + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.ver = self.ver + self.id = self.id + self.duration = self.duration + self.responseCode = self.responseCode + self.success = self.success diff --git a/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/trace.py b/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/trace.py index a65cdd92a1..7b62d38b85 100644 --- a/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/trace.py +++ b/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/trace.py @@ -12,14 +12,99 @@ # See the License for the specific language governing permissions and # limitations under the License. +from opentelemetry import trace as trace_api +from opentelemetry.ext.azure_monitor import protocol +from opentelemetry.ext.azure_monitor import util from opentelemetry.sdk.trace.export import SpanExporter, SpanExportResult +from opentelemetry.sdk.util import ns_to_iso_str +from urllib.parse import urlparse class AzureMonitorSpanExporter(SpanExporter): - def __init__(self): - pass + def __init__(self, **options): + self.options = util.Options(**options) + if not self.options.instrumentation_key: + raise ValueError("The instrumentation_key is not provided.") def export(self, spans): for span in spans: print(span) # TODO: add actual implementation here + print(self.span_to_envelope(span)) return SpanExportResult.SUCCESS + + @staticmethod + def ns_to_duration(nanoseconds): + n = (nanoseconds + 500000) // 1000000 # duration in milliseconds + n, ms = divmod(n, 1000) + n, s = divmod(n, 60) + n, m = divmod(n, 60) + d, h = divmod(n, 24) + return "{:d}.{:02d}:{:02d}:{:02d}.{:03d}".format(d, h, m, s, ms) + + def span_to_envelope(self, span): + envelope = protocol.Envelope( + iKey=self.options.instrumentation_key, + tags=dict(util.azure_monitor_context), + time=ns_to_iso_str(span.start_time), + ) + envelope.tags["ai.operation.id"] = "{:032x}".format(span.context.trace_id) + if span.parent: + envelope.tags["ai.operation.parentId"] = "|{:032x}.{:016x}.".format( + span.context.trace_id, + span.parent.span_id, + ) + if span.kind in (trace_api.SpanKind.SERVER, trace_api.SpanKind.CONSUMER): + envelope.name = "Microsoft.ApplicationInsights.Request" + data = protocol.Request( + id="|{:032x}.{:016x}.".format(span.context.trace_id, span.context.span_id), + duration=self.ns_to_duration(span.end_time - span.start_time), + responseCode="0", + success=False, + properties={}, + ) + envelope.data = protocol.Data(baseData=data, baseType="RequestData") + if "http.method" in span.attributes: + data.name = span.attributes["http.method"] + if "http.route" in span.attributes: + data.name = data.name + " " + span.attributes["http.route"] + envelope.tags["ai.operation.name"] = data.name + if "http.url" in span.attributes: + data.url = span.attributes["http.url"] + if "http.status_code" in span.attributes: + status_code = span.attributes["http.status_code"] + data.responseCode = str(status_code) + data.success = ( + status_code >= 200 and status_code <= 399 + ) + else: + envelope.name = \ + "Microsoft.ApplicationInsights.RemoteDependency" + data = protocol.RemoteDependency( + name=span.name, # TODO + id="|{:032x}.{:016x}.".format(span.context.trace_id, span.context.span_id), + resultCode="0", # TODO + duration=self.ns_to_duration(span.end_time - span.start_time), + success=True, # TODO + properties={}, + ) + envelope.data = protocol.Data( + baseData=data, + baseType="RemoteDependencyData", + ) + if span.kind in (trace_api.SpanKind.CLIENT, trace_api.SpanKind.PRODUCER): + data.type = "HTTP" # TODO + if "http.url" in span.attributes: + url = span.attributes["http.url"] + # TODO: error handling, probably put scheme as well + data.name = urlparse(url).netloc + if "http.status_code" in span.attributes: + data.resultCode = str(span.attributes["http.status_code"]) + else: # SpanKind.INTERNAL + data.type = "InProc" + # TODO: links, tracestate, tags + for key in span.attributes: + # This removes redundant data from ApplicationInsights + if key.startswith("http."): + continue + data.properties[key] = span.attributes[key] + return envelope diff --git a/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/util.py b/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/util.py new file mode 100644 index 0000000000..d71dbb2c8c --- /dev/null +++ b/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/util.py @@ -0,0 +1,44 @@ +# Copyright 2019, OpenTelemetry Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import locale +import os +import platform +import sys + +from opentelemetry.ext.azure_monitor.protocol import BaseObject +from opentelemetry.ext.azure_monitor.version import __version__ as ext_version +from opentelemetry.sdk.version import __version__ as opentelemetry_version + +azure_monitor_context = { + 'ai.cloud.role': os.path.basename(sys.argv[0]) or 'Python Application', + 'ai.cloud.roleInstance': platform.node(), + 'ai.device.id': platform.node(), + 'ai.device.locale': locale.getdefaultlocale()[0], + 'ai.device.osVersion': platform.version(), + 'ai.device.type': 'Other', + 'ai.internal.sdkVersion': 'py{}:ot{}:ext{}'.format( + platform.python_version(), + opentelemetry_version, + ext_version, + ), +} + + +class Options(BaseObject): + _default = BaseObject( + endpoint='https://dc.services.visualstudio.com/v2/track', + instrumentation_key=os.getenv('APPINSIGHTS_INSTRUMENTATIONKEY', None), + timeout=10.0, # networking timeout in seconds + ) From 31d87da1e0ff588c692045f5be764d6c75b81fad Mon Sep 17 00:00:00 2001 From: Reiley Yang Date: Wed, 25 Sep 2019 20:37:10 -0700 Subject: [PATCH 04/23] cosmetic change --- .../src/opentelemetry/ext/azure_monitor/trace.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/trace.py b/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/trace.py index 7b62d38b85..aee7dad34a 100644 --- a/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/trace.py +++ b/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/trace.py @@ -12,11 +12,11 @@ # See the License for the specific language governing permissions and # limitations under the License. -from opentelemetry import trace as trace_api from opentelemetry.ext.azure_monitor import protocol from opentelemetry.ext.azure_monitor import util from opentelemetry.sdk.trace.export import SpanExporter, SpanExportResult from opentelemetry.sdk.util import ns_to_iso_str +from opentelemetry.trace import SpanKind from urllib.parse import urlparse @@ -53,7 +53,7 @@ def span_to_envelope(self, span): span.context.trace_id, span.parent.span_id, ) - if span.kind in (trace_api.SpanKind.SERVER, trace_api.SpanKind.CONSUMER): + 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), @@ -91,7 +91,7 @@ def span_to_envelope(self, span): baseData=data, baseType="RemoteDependencyData", ) - if span.kind in (trace_api.SpanKind.CLIENT, trace_api.SpanKind.PRODUCER): + if span.kind in (SpanKind.CLIENT, SpanKind.PRODUCER): data.type = "HTTP" # TODO if "http.url" in span.attributes: url = span.attributes["http.url"] From 96581a08dbe257c45dc157932b8be1edbb946a21 Mon Sep 17 00:00:00 2001 From: Reiley Yang Date: Wed, 25 Sep 2019 20:38:16 -0700 Subject: [PATCH 05/23] cosmetic change --- .../src/opentelemetry/ext/azure_monitor/trace.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/trace.py b/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/trace.py index aee7dad34a..2b7d49ae93 100644 --- a/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/trace.py +++ b/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/trace.py @@ -32,8 +32,7 @@ def export(self, spans): print(self.span_to_envelope(span)) return SpanExportResult.SUCCESS - @staticmethod - def ns_to_duration(nanoseconds): + def ns_to_duration(self, nanoseconds): n = (nanoseconds + 500000) // 1000000 # duration in milliseconds n, ms = divmod(n, 1000) n, s = divmod(n, 60) From 7116279894751d3da43f2da9d57093151eb26a53 Mon Sep 17 00:00:00 2001 From: Reiley Yang Date: Wed, 25 Sep 2019 21:00:04 -0700 Subject: [PATCH 06/23] make it work --- .../examples/trace.py | 4 ++ .../opentelemetry/ext/azure_monitor/trace.py | 58 ++++++++++++++++--- 2 files changed, 53 insertions(+), 9 deletions(-) diff --git a/ext/opentelemetry-ext-azure-monitor/examples/trace.py b/ext/opentelemetry-ext-azure-monitor/examples/trace.py index 49b38c051d..14a064670d 100644 --- a/ext/opentelemetry-ext-azure-monitor/examples/trace.py +++ b/ext/opentelemetry-ext-azure-monitor/examples/trace.py @@ -1,8 +1,12 @@ +import logging + from opentelemetry import trace from opentelemetry.ext.azure_monitor import AzureMonitorSpanExporter from opentelemetry.sdk.trace import Tracer from opentelemetry.sdk.trace.export import SimpleExportSpanProcessor +logging.basicConfig(level=logging.INFO) + trace.set_preferred_tracer_implementation(lambda T: Tracer()) tracer = trace.tracer() tracer.add_span_processor( diff --git a/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/trace.py b/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/trace.py index 2b7d49ae93..0c745dcf7b 100644 --- a/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/trace.py +++ b/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/trace.py @@ -12,6 +12,10 @@ # See the License for the specific language governing permissions and # limitations under the License. +import json +import logging +import requests + from opentelemetry.ext.azure_monitor import protocol from opentelemetry.ext.azure_monitor import util from opentelemetry.sdk.trace.export import SpanExporter, SpanExportResult @@ -19,6 +23,8 @@ from opentelemetry.trace import SpanKind from urllib.parse import urlparse +logger = logging.getLogger(__name__) + class AzureMonitorSpanExporter(SpanExporter): def __init__(self, **options): @@ -27,10 +33,47 @@ def __init__(self, **options): raise ValueError("The instrumentation_key is not provided.") def export(self, spans): - for span in spans: - print(span) # TODO: add actual implementation here - print(self.span_to_envelope(span)) - return SpanExportResult.SUCCESS + envelopes = tuple(map(self.span_to_envelope, spans)) + + try: + response = requests.post( + url=self.options.endpoint, + data=json.dumps(envelopes), + headers={ + 'Accept': 'application/json', + 'Content-Type': 'application/json; charset=utf-8', + }, + timeout=self.options.timeout, + ) + except requests.RequestException as ex: + logger.warning('Transient client side error %s.', ex) + return SpanExportResult.FAILED_RETRYABLE + + text = 'N/A' + data = None + try: + text = response.text + except Exception as ex: + logger.warning('Error while reading response body %s.', ex) + else: + try: + data = json.loads(text) + except Exception: + pass + + if response.status_code == 200: + logger.info('Transmission succeeded: %s.', text) + return SpanExportResult.SUCCESS + + if response.status_code in ( + 206, # Partial Content + 429, # Too Many Requests + 500, # Internal Server Error + 503, # Service Unavailable + ): + return SpanExportResult.FAILED_RETRYABLE + + return SpanExportResult.FAILED_NOT_RETRYABLE def ns_to_duration(self, nanoseconds): n = (nanoseconds + 500000) // 1000000 # duration in milliseconds @@ -79,7 +122,7 @@ def span_to_envelope(self, span): envelope.name = \ "Microsoft.ApplicationInsights.RemoteDependency" data = protocol.RemoteDependency( - name=span.name, # TODO + name=span.name, id="|{:032x}.{:016x}.".format(span.context.trace_id, span.context.span_id), resultCode="0", # TODO duration=self.ns_to_duration(span.end_time - span.start_time), @@ -98,12 +141,9 @@ def span_to_envelope(self, span): data.name = urlparse(url).netloc if "http.status_code" in span.attributes: data.resultCode = str(span.attributes["http.status_code"]) - else: # SpanKind.INTERNAL + else: # SpanKind.INTERNAL data.type = "InProc" # TODO: links, tracestate, tags for key in span.attributes: - # This removes redundant data from ApplicationInsights - if key.startswith("http."): - continue data.properties[key] = span.attributes[key] return envelope From ab0e406e92680c6e4803ddd104bc56245de14fc9 Mon Sep 17 00:00:00 2001 From: Reiley Yang Date: Wed, 25 Sep 2019 21:15:07 -0700 Subject: [PATCH 07/23] cosmetic change --- .../ext/azure_monitor/protocol.py | 26 +++++++-------- .../opentelemetry/ext/azure_monitor/trace.py | 33 ++++++++++--------- .../opentelemetry/ext/azure_monitor/util.py | 18 +++++----- 3 files changed, 40 insertions(+), 37 deletions(-) diff --git a/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/protocol.py b/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/protocol.py index 1d72a65587..002913ddb4 100644 --- a/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/protocol.py +++ b/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/protocol.py @@ -67,8 +67,8 @@ def __init__(self, *args, **kwargs): class DataPoint(BaseObject): _default = BaseObject( - ns='', - name='', + ns="", + name="", kind=None, value=0.0, count=None, @@ -86,8 +86,8 @@ def __init__(self, *args, **kwargs): class Envelope(BaseObject): _default = BaseObject( ver=1, - name='', - time='', + name="", + time="", sampleRate=None, seq=None, iKey=None, @@ -105,7 +105,7 @@ def __init__(self, *args, **kwargs): class Event(BaseObject): _default = BaseObject( ver=2, - name='', + name="", properties=None, measurements=None, ) @@ -135,7 +135,7 @@ def __init__(self, *args, **kwargs): class Message(BaseObject): _default = BaseObject( ver=2, - message='', + message="", severityLevel=None, properties=None, measurements=None, @@ -163,10 +163,10 @@ def __init__(self, *args, **kwargs): class RemoteDependency(BaseObject): _default = BaseObject( ver=2, - name='', - id='', - resultCode='', - duration='', + name="", + id="", + resultCode="", + duration="", success=True, data=None, type=None, @@ -186,9 +186,9 @@ def __init__(self, *args, **kwargs): class Request(BaseObject): _default = BaseObject( ver=2, - id='', - duration='', - responseCode='', + id="", + duration="", + responseCode="", success=True, source=None, name=None, diff --git a/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/trace.py b/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/trace.py index 0c745dcf7b..ec267e6b71 100644 --- a/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/trace.py +++ b/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/trace.py @@ -40,21 +40,21 @@ def export(self, spans): url=self.options.endpoint, data=json.dumps(envelopes), headers={ - 'Accept': 'application/json', - 'Content-Type': 'application/json; charset=utf-8', + "Accept": "application/json", + "Content-Type": "application/json; charset=utf-8", }, timeout=self.options.timeout, ) except requests.RequestException as ex: - logger.warning('Transient client side error %s.', ex) + logger.warning("Transient client side error %s.", ex) return SpanExportResult.FAILED_RETRYABLE - text = 'N/A' + text = "N/A" data = None try: text = response.text except Exception as ex: - logger.warning('Error while reading response body %s.', ex) + logger.warning("Error while reading response body %s.", ex) else: try: data = json.loads(text) @@ -62,14 +62,14 @@ def export(self, spans): pass if response.status_code == 200: - logger.info('Transmission succeeded: %s.', text) + logger.info("Transmission succeeded: %s.", text) return SpanExportResult.SUCCESS if response.status_code in ( - 206, # Partial Content - 429, # Too Many Requests - 500, # Internal Server Error - 503, # Service Unavailable + 206, # Partial Content + 429, # Too Many Requests + 500, # Internal Server Error + 503, # Service Unavailable ): return SpanExportResult.FAILED_RETRYABLE @@ -89,7 +89,9 @@ def span_to_envelope(self, span): tags=dict(util.azure_monitor_context), time=ns_to_iso_str(span.start_time), ) - envelope.tags["ai.operation.id"] = "{:032x}".format(span.context.trace_id) + envelope.tags["ai.operation.id"] = "{:032x}".format( + span.context.trace_id + ) if span.parent: envelope.tags["ai.operation.parentId"] = "|{:032x}.{:016x}.".format( span.context.trace_id, @@ -115,15 +117,16 @@ def span_to_envelope(self, span): if "http.status_code" in span.attributes: status_code = span.attributes["http.status_code"] data.responseCode = str(status_code) - data.success = ( - status_code >= 200 and status_code <= 399 - ) + data.success = status_code >= 200 and status_code <= 399 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="|{:032x}.{:016x}.".format( + span.context.trace_id, + span.context.span_id, + ), resultCode="0", # TODO duration=self.ns_to_duration(span.end_time - span.start_time), success=True, # TODO diff --git a/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/util.py b/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/util.py index d71dbb2c8c..252c1347d5 100644 --- a/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/util.py +++ b/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/util.py @@ -22,13 +22,13 @@ from opentelemetry.sdk.version import __version__ as opentelemetry_version azure_monitor_context = { - 'ai.cloud.role': os.path.basename(sys.argv[0]) or 'Python Application', - 'ai.cloud.roleInstance': platform.node(), - 'ai.device.id': platform.node(), - 'ai.device.locale': locale.getdefaultlocale()[0], - 'ai.device.osVersion': platform.version(), - 'ai.device.type': 'Other', - 'ai.internal.sdkVersion': 'py{}:ot{}:ext{}'.format( + "ai.cloud.role": os.path.basename(sys.argv[0]) or "Python Application", + "ai.cloud.roleInstance": platform.node(), + "ai.device.id": platform.node(), + "ai.device.locale": locale.getdefaultlocale()[0], + "ai.device.osVersion": platform.version(), + "ai.device.type": "Other", + "ai.internal.sdkVersion": "py{}:ot{}:ext{}".format( platform.python_version(), opentelemetry_version, ext_version, @@ -38,7 +38,7 @@ class Options(BaseObject): _default = BaseObject( - endpoint='https://dc.services.visualstudio.com/v2/track', - instrumentation_key=os.getenv('APPINSIGHTS_INSTRUMENTATIONKEY', None), + endpoint="https://dc.services.visualstudio.com/v2/track", + instrumentation_key=os.getenv("APPINSIGHTS_INSTRUMENTATIONKEY", None), timeout=10.0, # networking timeout in seconds ) From d11e217e5240a494456a17642131902b42934a67 Mon Sep 17 00:00:00 2001 From: Reiley Yang Date: Wed, 25 Sep 2019 21:22:17 -0700 Subject: [PATCH 08/23] black auto format --- .../ext/azure_monitor/protocol.py | 27 ++++++------------- .../opentelemetry/ext/azure_monitor/trace.py | 24 +++++++++-------- .../opentelemetry/ext/azure_monitor/util.py | 4 +-- 3 files changed, 22 insertions(+), 33 deletions(-) diff --git a/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/protocol.py b/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/protocol.py index 002913ddb4..9a93763b8f 100644 --- a/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/protocol.py +++ b/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/protocol.py @@ -37,10 +37,11 @@ def __getattr__(self, name): try: return self[name] except KeyError: - raise AttributeError("'{}' object has no attribute {}".format( - type(self).__name__, - name, - )) + raise AttributeError( + "'{}' object has no attribute {}".format( + type(self).__name__, name + ) + ) def __getitem__(self, key): if self._default is self: @@ -54,10 +55,7 @@ def __getitem__(self, key): class Data(BaseObject): - _default = BaseObject( - baseData=None, - baseType=None, - ) + _default = BaseObject(baseData=None, baseType=None) def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) @@ -103,12 +101,7 @@ def __init__(self, *args, **kwargs): class Event(BaseObject): - _default = BaseObject( - ver=2, - name="", - properties=None, - measurements=None, - ) + _default = BaseObject(ver=2, name="", properties=None, measurements=None) def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) @@ -148,11 +141,7 @@ def __init__(self, *args, **kwargs): class MetricData(BaseObject): - _default = BaseObject( - ver=2, - metrics=[], - properties=None, - ) + _default = BaseObject(ver=2, metrics=[], properties=None) def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) diff --git a/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/trace.py b/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/trace.py index ec267e6b71..43ef3d0761 100644 --- a/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/trace.py +++ b/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/trace.py @@ -93,20 +93,25 @@ def span_to_envelope(self, span): span.context.trace_id ) if span.parent: - envelope.tags["ai.operation.parentId"] = "|{:032x}.{:016x}.".format( - span.context.trace_id, - span.parent.span_id, + envelope.tags[ + "ai.operation.parentId" + ] = "|{:032x}.{:016x}.".format( + span.context.trace_id, span.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="|{:032x}.{:016x}.".format( + span.context.trace_id, span.context.span_id + ), duration=self.ns_to_duration(span.end_time - span.start_time), responseCode="0", success=False, properties={}, ) - envelope.data = protocol.Data(baseData=data, baseType="RequestData") + envelope.data = protocol.Data( + baseData=data, baseType="RequestData" + ) if "http.method" in span.attributes: data.name = span.attributes["http.method"] if "http.route" in span.attributes: @@ -119,13 +124,11 @@ def span_to_envelope(self, span): data.responseCode = str(status_code) data.success = status_code >= 200 and status_code <= 399 else: - envelope.name = \ - "Microsoft.ApplicationInsights.RemoteDependency" + envelope.name = "Microsoft.ApplicationInsights.RemoteDependency" data = protocol.RemoteDependency( name=span.name, id="|{:032x}.{:016x}.".format( - span.context.trace_id, - span.context.span_id, + span.context.trace_id, span.context.span_id ), resultCode="0", # TODO duration=self.ns_to_duration(span.end_time - span.start_time), @@ -133,8 +136,7 @@ def span_to_envelope(self, span): properties={}, ) envelope.data = protocol.Data( - baseData=data, - baseType="RemoteDependencyData", + baseData=data, baseType="RemoteDependencyData" ) if span.kind in (SpanKind.CLIENT, SpanKind.PRODUCER): data.type = "HTTP" # TODO diff --git a/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/util.py b/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/util.py index 252c1347d5..f97dbd3e33 100644 --- a/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/util.py +++ b/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/util.py @@ -29,9 +29,7 @@ "ai.device.osVersion": platform.version(), "ai.device.type": "Other", "ai.internal.sdkVersion": "py{}:ot{}:ext{}".format( - platform.python_version(), - opentelemetry_version, - ext_version, + platform.python_version(), opentelemetry_version, ext_version ), } From 919ac7142674427d309d54a614a220ff073c666f Mon Sep 17 00:00:00 2001 From: Reiley Yang Date: Wed, 25 Sep 2019 21:40:43 -0700 Subject: [PATCH 09/23] cosmetic update --- .../ext/azure_monitor/protocol.py | 23 ++++++++++------- .../opentelemetry/ext/azure_monitor/trace.py | 25 +++++++++++-------- 2 files changed, 28 insertions(+), 20 deletions(-) diff --git a/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/protocol.py b/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/protocol.py index 9a93763b8f..2531fd4948 100644 --- a/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/protocol.py +++ b/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/protocol.py @@ -21,13 +21,16 @@ def __init__(self, *args, **kwargs): def __repr__(self): tmp = {} + current = self while True: - for item in self.items(): + for item in current.items(): if item[0] not in tmp: tmp[item[0]] = item[1] - if self._default == self: + if ( + current._default == current + ): # noqa pylint: disable=protected-access break - self = self._default + current = current._default # noqa pylint: disable=protected-access return repr(tmp) def __setattr__(self, name, value): @@ -51,7 +54,7 @@ def __getitem__(self, key): return self._default[key] -BaseObject._default = BaseObject() +BaseObject._default = BaseObject() # noqa pylint: disable=protected-access class Data(BaseObject): @@ -59,8 +62,8 @@ class Data(BaseObject): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) - self.baseData = self.baseData - self.baseType = self.baseType + self.baseData = self.baseData # noqa pylint: disable=invalid-name + self.baseType = self.baseType # noqa pylint: disable=invalid-name class DataPoint(BaseObject): @@ -168,7 +171,7 @@ def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.ver = self.ver self.name = self.name - self.resultCode = self.resultCode + self.resultCode = self.resultCode # noqa pylint: disable=invalid-name self.duration = self.duration @@ -189,7 +192,9 @@ class Request(BaseObject): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.ver = self.ver - self.id = self.id + self.id = self.id # noqa pylint: disable=invalid-name self.duration = self.duration - self.responseCode = self.responseCode + self.responseCode = ( + self.responseCode + ) # noqa pylint: disable=invalid-name self.success = self.success diff --git a/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/trace.py b/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/trace.py index 43ef3d0761..ca17f2c546 100644 --- a/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/trace.py +++ b/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/trace.py @@ -50,15 +50,15 @@ def export(self, spans): return SpanExportResult.FAILED_RETRYABLE text = "N/A" - data = None + data = None # noqa pylint: disable=unused-variable try: text = response.text - except Exception as ex: + except Exception as ex: # noqa pylint: disable=broad-except logger.warning("Error while reading response body %s.", ex) else: try: data = json.loads(text) - except Exception: + except Exception: # noqa pylint: disable=broad-except pass if response.status_code == 200: @@ -75,13 +75,16 @@ def export(self, spans): return SpanExportResult.FAILED_NOT_RETRYABLE - def ns_to_duration(self, nanoseconds): - n = (nanoseconds + 500000) // 1000000 # duration in milliseconds - n, ms = divmod(n, 1000) - n, s = divmod(n, 60) - n, m = divmod(n, 60) - d, h = divmod(n, 24) - return "{:d}.{:02d}:{:02d}:{:02d}.{:03d}".format(d, h, m, s, ms) + @staticmethod + def ns_to_duration(nanoseconds): + value = (nanoseconds + 500000) // 1000000 # duration in milliseconds + value, microseconds = divmod(value, 1000) + value, seconds = divmod(value, 60) + value, minutes = divmod(value, 60) + days, hours = divmod(value, 24) + return "{:d}.{:02d}:{:02d}:{:02d}.{:03d}".format( + days, hours, minutes, seconds, microseconds + ) def span_to_envelope(self, span): envelope = protocol.Envelope( @@ -122,7 +125,7 @@ def span_to_envelope(self, span): if "http.status_code" in span.attributes: status_code = span.attributes["http.status_code"] data.responseCode = str(status_code) - data.success = status_code >= 200 and status_code <= 399 + data.success = 200 <= status_code < 400 else: envelope.name = "Microsoft.ApplicationInsights.RemoteDependency" data = protocol.RemoteDependency( From 8620712c88cc258797869188c2d35286e728d7b8 Mon Sep 17 00:00:00 2001 From: Reiley Yang Date: Wed, 25 Sep 2019 21:47:12 -0700 Subject: [PATCH 10/23] cosmetic update --- .../src/opentelemetry/ext/azure_monitor/protocol.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/protocol.py b/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/protocol.py index 2531fd4948..20d959ea52 100644 --- a/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/protocol.py +++ b/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/protocol.py @@ -194,7 +194,5 @@ def __init__(self, *args, **kwargs): self.ver = self.ver self.id = self.id # noqa pylint: disable=invalid-name self.duration = self.duration - self.responseCode = ( - self.responseCode - ) # noqa pylint: disable=invalid-name + self.responseCode = self.responseCode # noqa pylint: disable=invalid-name self.success = self.success From 9ae0ee73556174b30fa424ac65e8fde75e7de536 Mon Sep 17 00:00:00 2001 From: Reiley Yang Date: Wed, 25 Sep 2019 21:48:10 -0700 Subject: [PATCH 11/23] make both black and lint happy --- .../src/opentelemetry/ext/azure_monitor/protocol.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/protocol.py b/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/protocol.py index 20d959ea52..3ec4140261 100644 --- a/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/protocol.py +++ b/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/protocol.py @@ -194,5 +194,7 @@ def __init__(self, *args, **kwargs): self.ver = self.ver self.id = self.id # noqa pylint: disable=invalid-name self.duration = self.duration - self.responseCode = self.responseCode # noqa pylint: disable=invalid-name + self.responseCode = ( + self.responseCode # noqa pylint: disable=invalid-name + ) # noqa pylint: disable=invalid-name self.success = self.success From 6a2c41b72ba1a7c2496488b116c15d8ec86dd781 Mon Sep 17 00:00:00 2001 From: Reiley Yang Date: Wed, 25 Sep 2019 21:48:38 -0700 Subject: [PATCH 12/23] make both black and lint happy --- .../src/opentelemetry/ext/azure_monitor/protocol.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/protocol.py b/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/protocol.py index 3ec4140261..5c01f0d179 100644 --- a/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/protocol.py +++ b/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/protocol.py @@ -194,7 +194,7 @@ def __init__(self, *args, **kwargs): self.ver = self.ver self.id = self.id # noqa pylint: disable=invalid-name self.duration = self.duration - self.responseCode = ( - self.responseCode # noqa pylint: disable=invalid-name - ) # noqa pylint: disable=invalid-name + self.responseCode = ( # noqa pylint: disable=invalid-name + self.responseCode + ) self.success = self.success From b7c965ef6f5cb02b6b287a23614307aaaee6d816 Mon Sep 17 00:00:00 2001 From: Reiley Yang Date: Wed, 25 Sep 2019 21:49:50 -0700 Subject: [PATCH 13/23] make both black and lint happy --- .../src/opentelemetry/ext/azure_monitor/protocol.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/protocol.py b/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/protocol.py index 5c01f0d179..ccdf5eef8d 100644 --- a/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/protocol.py +++ b/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/protocol.py @@ -27,8 +27,9 @@ def __repr__(self): if item[0] not in tmp: tmp[item[0]] = item[1] if ( - current._default == current - ): # noqa pylint: disable=protected-access + current._default # noqa pylint: disable=protected-access + == current + ): break current = current._default # noqa pylint: disable=protected-access return repr(tmp) From b8ed4580f1f05093efca6245fedfaa0384f90545 Mon Sep 17 00:00:00 2001 From: Reiley Yang Date: Wed, 25 Sep 2019 21:56:30 -0700 Subject: [PATCH 14/23] lint --- .../src/opentelemetry/ext/azure_monitor/trace.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/trace.py b/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/trace.py index ca17f2c546..c208df4127 100644 --- a/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/trace.py +++ b/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/trace.py @@ -57,7 +57,7 @@ def export(self, spans): logger.warning("Error while reading response body %s.", ex) else: try: - data = json.loads(text) + data = json.loads(text) # noqa pylint: disable=unused-variable except Exception: # noqa pylint: disable=broad-except pass From 97f882a4fefc3a8cf4cb2ab3e84fce45f5723ae5 Mon Sep 17 00:00:00 2001 From: Reiley Yang Date: Wed, 25 Sep 2019 22:48:11 -0700 Subject: [PATCH 15/23] lint --- .../src/opentelemetry/ext/azure_monitor/trace.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/trace.py b/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/trace.py index c208df4127..0da97b6e87 100644 --- a/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/trace.py +++ b/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/trace.py @@ -14,14 +14,14 @@ import json import logging +from urllib.parse import urlparse + import requests -from opentelemetry.ext.azure_monitor import protocol -from opentelemetry.ext.azure_monitor import util +from opentelemetry.ext.azure_monitor import protocol, util from opentelemetry.sdk.trace.export import SpanExporter, SpanExportResult from opentelemetry.sdk.util import ns_to_iso_str from opentelemetry.trace import SpanKind -from urllib.parse import urlparse logger = logging.getLogger(__name__) From 7ea1daed6bd6bed8a19486ad14177b946fc1a906 Mon Sep 17 00:00:00 2001 From: Reiley Yang Date: Thu, 26 Sep 2019 07:58:51 -0700 Subject: [PATCH 16/23] add license boilerplate --- .../examples/trace.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/ext/opentelemetry-ext-azure-monitor/examples/trace.py b/ext/opentelemetry-ext-azure-monitor/examples/trace.py index 14a064670d..8b0da29a02 100644 --- a/ext/opentelemetry-ext-azure-monitor/examples/trace.py +++ b/ext/opentelemetry-ext-azure-monitor/examples/trace.py @@ -1,3 +1,17 @@ +# Copyright 2019, OpenCensus Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + import logging from opentelemetry import trace From 4201674188836ea0ad246df645a9f35aea3c2234 Mon Sep 17 00:00:00 2001 From: Reiley Yang Date: Fri, 27 Sep 2019 12:17:47 -0700 Subject: [PATCH 17/23] add client/server example --- .../examples/client.py | 30 +++++++++++++ .../examples/server.py | 43 +++++++++++++++++++ .../opentelemetry/ext/azure_monitor/trace.py | 2 +- 3 files changed, 74 insertions(+), 1 deletion(-) create mode 100644 ext/opentelemetry-ext-azure-monitor/examples/client.py create mode 100644 ext/opentelemetry-ext-azure-monitor/examples/server.py diff --git a/ext/opentelemetry-ext-azure-monitor/examples/client.py b/ext/opentelemetry-ext-azure-monitor/examples/client.py new file mode 100644 index 0000000000..ff954788e6 --- /dev/null +++ b/ext/opentelemetry-ext-azure-monitor/examples/client.py @@ -0,0 +1,30 @@ +# Copyright 2019, OpenCensus Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import requests + +from opentelemetry import trace +from opentelemetry.ext import http_requests +from opentelemetry.ext.azure_monitor import AzureMonitorSpanExporter +from opentelemetry.sdk.trace import Tracer +from opentelemetry.sdk.trace.export import BatchExportSpanProcessor + +trace.set_preferred_tracer_implementation(lambda T: Tracer()) +tracer = trace.tracer() +http_requests.enable(tracer) +span_processor = BatchExportSpanProcessor(AzureMonitorSpanExporter()) +tracer.add_span_processor(span_processor) + +response = requests.get(url="http://127.0.0.1:5000/") +span_processor.shutdown() diff --git a/ext/opentelemetry-ext-azure-monitor/examples/server.py b/ext/opentelemetry-ext-azure-monitor/examples/server.py new file mode 100644 index 0000000000..cc5bc587be --- /dev/null +++ b/ext/opentelemetry-ext-azure-monitor/examples/server.py @@ -0,0 +1,43 @@ +# Copyright 2019, OpenCensus Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import flask +import requests + +from opentelemetry import trace +from opentelemetry.ext import http_requests +from opentelemetry.ext.azure_monitor import AzureMonitorSpanExporter +from opentelemetry.ext.wsgi import OpenTelemetryMiddleware +from opentelemetry.sdk.trace import Tracer +from opentelemetry.sdk.trace.export import BatchExportSpanProcessor + +trace.set_preferred_tracer_implementation(lambda T: Tracer()) + +http_requests.enable(trace.tracer()) +span_processor = BatchExportSpanProcessor(AzureMonitorSpanExporter()) +trace.tracer().add_span_processor(span_processor) + +app = flask.Flask(__name__) +app.wsgi_app = OpenTelemetryMiddleware(app.wsgi_app) + +@app.route("/") +def hello(): + with trace.tracer().start_span("parent"): + requests.get("https://www.wikipedia.org/wiki/Rabbit") + return "hello" + + +if __name__ == "__main__": + app.run(debug=True) + span_processor.shutdown() diff --git a/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/trace.py b/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/trace.py index 0da97b6e87..6878903ae5 100644 --- a/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/trace.py +++ b/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/trace.py @@ -99,7 +99,7 @@ def span_to_envelope(self, span): envelope.tags[ "ai.operation.parentId" ] = "|{:032x}.{:016x}.".format( - span.context.trace_id, span.parent.span_id + span.context.trace_id, span.context.span_id ) if span.kind in (SpanKind.CONSUMER, SpanKind.SERVER): envelope.name = "Microsoft.ApplicationInsights.Request" From 8c50cac3d05a87977dae1b9682b13897d54e3371 Mon Sep 17 00:00:00 2001 From: Reiley Yang Date: Fri, 27 Sep 2019 12:30:24 -0700 Subject: [PATCH 18/23] lint --- ext/opentelemetry-ext-azure-monitor/examples/server.py | 1 + 1 file changed, 1 insertion(+) diff --git a/ext/opentelemetry-ext-azure-monitor/examples/server.py b/ext/opentelemetry-ext-azure-monitor/examples/server.py index cc5bc587be..54727ef737 100644 --- a/ext/opentelemetry-ext-azure-monitor/examples/server.py +++ b/ext/opentelemetry-ext-azure-monitor/examples/server.py @@ -31,6 +31,7 @@ app = flask.Flask(__name__) app.wsgi_app = OpenTelemetryMiddleware(app.wsgi_app) + @app.route("/") def hello(): with trace.tracer().start_span("parent"): From bf53d3dbe5036485ab4ffbcf1e55d0224d1e1332 Mon Sep 17 00:00:00 2001 From: Reiley Yang Date: Fri, 27 Sep 2019 14:56:25 -0700 Subject: [PATCH 19/23] fix parent --- .../src/opentelemetry/ext/azure_monitor/trace.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/trace.py b/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/trace.py index 6878903ae5..d070cac85c 100644 --- a/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/trace.py +++ b/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/trace.py @@ -21,7 +21,7 @@ from opentelemetry.ext.azure_monitor import protocol, util from opentelemetry.sdk.trace.export import SpanExporter, SpanExportResult from opentelemetry.sdk.util import ns_to_iso_str -from opentelemetry.trace import SpanKind +from opentelemetry.trace import Span, SpanKind logger = logging.getLogger(__name__) @@ -96,10 +96,13 @@ def span_to_envelope(self, span): span.context.trace_id ) if span.parent: + parent = span.parent + if isinstance(parent, Span): + parent = parent.context envelope.tags[ "ai.operation.parentId" ] = "|{:032x}.{:016x}.".format( - span.context.trace_id, span.context.span_id + parent.trace_id, parent.span_id ) if span.kind in (SpanKind.CONSUMER, SpanKind.SERVER): envelope.name = "Microsoft.ApplicationInsights.Request" From 1d04f2a99724f2d2e758a47597e783b6ec88809d Mon Sep 17 00:00:00 2001 From: Reiley Yang Date: Fri, 27 Sep 2019 15:03:31 -0700 Subject: [PATCH 20/23] black --- .../src/opentelemetry/ext/azure_monitor/trace.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/trace.py b/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/trace.py index d070cac85c..497bf34c2c 100644 --- a/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/trace.py +++ b/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/trace.py @@ -101,9 +101,7 @@ def span_to_envelope(self, span): parent = parent.context envelope.tags[ "ai.operation.parentId" - ] = "|{:032x}.{:016x}.".format( - parent.trace_id, parent.span_id - ) + ] = "|{:032x}.{:016x}.".format(parent.trace_id, parent.span_id) if span.kind in (SpanKind.CONSUMER, SpanKind.SERVER): envelope.name = "Microsoft.ApplicationInsights.Request" data = protocol.Request( From a1d309f0f643ba920d58210a4770c58c50c24a2b Mon Sep 17 00:00:00 2001 From: Reiley Yang Date: Fri, 27 Sep 2019 15:17:16 -0700 Subject: [PATCH 21/23] lint --- .../src/opentelemetry/ext/azure_monitor/trace.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/trace.py b/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/trace.py index 497bf34c2c..ac03383201 100644 --- a/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/trace.py +++ b/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/trace.py @@ -86,7 +86,7 @@ def ns_to_duration(nanoseconds): days, hours, minutes, seconds, microseconds ) - def span_to_envelope(self, span): + def span_to_envelope(self, span): # noqa pylint: disable=too-many-branches envelope = protocol.Envelope( iKey=self.options.instrumentation_key, tags=dict(util.azure_monitor_context), @@ -95,10 +95,10 @@ def span_to_envelope(self, span): envelope.tags["ai.operation.id"] = "{:032x}".format( span.context.trace_id ) - if span.parent: - parent = span.parent - if isinstance(parent, Span): - parent = parent.context + parent = span.parent + if isinstance(parent, Span): + parent = parent.context + if parent: envelope.tags[ "ai.operation.parentId" ] = "|{:032x}.{:016x}.".format(parent.trace_id, parent.span_id) From cb56c315f83d9789a3142d496a6ad23b0661ea3d Mon Sep 17 00:00:00 2001 From: Reiley Yang Date: Fri, 27 Sep 2019 16:37:23 -0700 Subject: [PATCH 22/23] support links --- .../opentelemetry/ext/azure_monitor/trace.py | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/trace.py b/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/trace.py index ac03383201..16f9252fd0 100644 --- a/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/trace.py +++ b/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/trace.py @@ -152,7 +152,22 @@ def span_to_envelope(self, span): # noqa pylint: disable=too-many-branches data.resultCode = str(span.attributes["http.status_code"]) else: # SpanKind.INTERNAL data.type = "InProc" - # TODO: links, tracestate, tags for key in span.attributes: data.properties[key] = span.attributes[key] + if span.links: + links = [] + for link in span.links: + links.append( + { + "operation_Id": "{:032x}".format( + link.context.trace_id + ), + "id": "|{:032x}.{:016x}.".format( + link.context.trace_id, link.context.span_id + ), + } + ) + data.properties["_MS.links"] = json.dumps(links) + print(data.properties["_MS.links"]) + # TODO: tracestate, tags return envelope From e0e6fd4e848476db287c4c85599274878b38ad3a Mon Sep 17 00:00:00 2001 From: Reiley Yang Date: Fri, 27 Sep 2019 16:46:52 -0700 Subject: [PATCH 23/23] make examples simpler --- ext/opentelemetry-ext-azure-monitor/examples/trace.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/ext/opentelemetry-ext-azure-monitor/examples/trace.py b/ext/opentelemetry-ext-azure-monitor/examples/trace.py index 8b0da29a02..8e8f887aa1 100644 --- a/ext/opentelemetry-ext-azure-monitor/examples/trace.py +++ b/ext/opentelemetry-ext-azure-monitor/examples/trace.py @@ -12,15 +12,11 @@ # See the License for the specific language governing permissions and # limitations under the License. -import logging - from opentelemetry import trace from opentelemetry.ext.azure_monitor import AzureMonitorSpanExporter from opentelemetry.sdk.trace import Tracer from opentelemetry.sdk.trace.export import SimpleExportSpanProcessor -logging.basicConfig(level=logging.INFO) - trace.set_preferred_tracer_implementation(lambda T: Tracer()) tracer = trace.tracer() tracer.add_span_processor(