From 368d4d58593a2e32e0564d132b1a3ce8731a1145 Mon Sep 17 00:00:00 2001 From: Leighton Date: Fri, 27 Sep 2019 18:16:37 -0700 Subject: [PATCH 01/22] exporter --- .../examples/metrics.py | 38 +++++++ .../ext/azure_monitor/__init__.py | 3 +- .../ext/azure_monitor/metrics.py | 98 +++++++++++++++++++ .../src/opentelemetry/sdk/metrics/__init__.py | 39 ++++++-- .../sdk/metrics/export/__init__.py | 73 ++++++++++++++ 5 files changed, 243 insertions(+), 8 deletions(-) create mode 100644 ext/opentelemetry-ext-azure-monitor/examples/metrics.py create mode 100644 ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/metrics.py create mode 100644 opentelemetry-sdk/src/opentelemetry/sdk/metrics/export/__init__.py diff --git a/ext/opentelemetry-ext-azure-monitor/examples/metrics.py b/ext/opentelemetry-ext-azure-monitor/examples/metrics.py new file mode 100644 index 00000000000..af2fb91fba7 --- /dev/null +++ b/ext/opentelemetry-ext-azure-monitor/examples/metrics.py @@ -0,0 +1,38 @@ +# 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. + +from opentelemetry import metrics +from opentelemetry.ext.azure_monitor import AzureMonitorMetricsExporter +from opentelemetry.sdk.metrics import Counter, Meter +from opentelemetry.sdk.metrics.export import ConsoleMetricsExporter + +metrics.set_preferred_meter_implementation(lambda T: Meter()) +meter = metrics.meter() +exporter = AzureMonitorMetricsExporter() +console_exporter = ConsoleMetricsExporter() + +counter = meter.create_metric( + "available memory", + "available memory", + "bytes", + int, + Counter, + ("environment",), +) +label_values = ("staging",) +counter_handle = counter.get_handle(label_values) +counter_handle.add(100) + +console_exporter.export([(counter, label_values)]) +exporter.export([(counter, label_values)]) diff --git a/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/__init__.py b/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/__init__.py index 81222c546eb..93cd8e102d9 100644 --- a/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/__init__.py +++ b/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/__init__.py @@ -17,7 +17,8 @@ Microsoft Azure Monitor. """ +from opentelemetry.ext.azure_monitor.metrics import AzureMonitorMetricsExporter from opentelemetry.ext.azure_monitor.trace import AzureMonitorSpanExporter from opentelemetry.ext.azure_monitor.version import __version__ # noqa -__all__ = ["AzureMonitorSpanExporter"] +__all__ = ["AzureMonitorMetricsExporter", "AzureMonitorSpanExporter"] diff --git a/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/metrics.py b/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/metrics.py new file mode 100644 index 00000000000..60fa25c2802 --- /dev/null +++ b/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/metrics.py @@ -0,0 +1,98 @@ +# 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 json +import logging + +import requests + +from opentelemetry.ext.azure_monitor import protocol, util +from opentelemetry.sdk.metrics.export import MetricsExporter,\ + MetricsExportResult + + +logger = logging.getLogger(__name__) + + +class AzureMonitorMetricsExporter(MetricsExporter): + 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, metric_tuples): + envelopes = tuple(map(self.metric_tuple_to_envelope, metric_tuples)) + + 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 MetricsExportResult.FAILED_RETRYABLE + + text = "N/A" + data = None # noqa pylint: disable=unused-variable + try: + text = response.text + except Exception as ex: # noqa pylint: disable=broad-except + logger.warning("Error while reading response body %s.", ex) + else: + try: + data = json.loads(text) # noqa pylint: disable=unused-variable + except Exception: # noqa pylint: disable=broad-except + pass + + if response.status_code == 200: + logger.info("Transmission succeeded: %s.", text) + return MetricsExportResult.SUCCESS + + if response.status_code in ( + 206, # Partial Content + 429, # Too Many Requests + 500, # Internal Server Error + 503, # Service Unavailable + ): + return MetricsExportResult.FAILED_RETRYABLE + + return MetricsExportResult.FAILED_NOT_RETRYABLE + + def metric_tuple_to_envelope(self, metric_tuple): + metric = metric_tuple[0] + label_values = metric_tuple[1] + handle = metric.get_handle(label_values) + envelope = protocol.Envelope( + iKey=self.options.instrumentation_key, + tags=dict(util.azure_monitor_context), + time=handle.time_stamp.isoformat(), + ) + envelope.name = "Microsoft.ApplicationInsights.Metric" + # label_keys and label_values assumed to have the same length + properties = {metric.label_keys[idx]: label_values[idx] + for idx, value in enumerate(label_values, start=0)} + data_point = protocol.DataPoint(ns=metric.name, + name=metric.name, + value=handle.data) + data = protocol.MetricData( + metrics=[data_point], + properties=properties + ) + envelope.data = protocol.Data(baseData=data, baseType="MetricData") + return envelope diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py index f80a72c7706..8c0ffb0e59d 100644 --- a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py +++ b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py @@ -13,6 +13,8 @@ # limitations under the License. import logging +from datetime import datetime + from typing import Sequence, Tuple, Type from opentelemetry import metrics as metrics_api @@ -20,7 +22,7 @@ logger = logging.getLogger(__name__) -class BaseHandle: +class MetricHandle: def __init__( self, value_type: Type[metrics_api.ValueT], @@ -31,6 +33,7 @@ def __init__( self.value_type = value_type self.enabled = enabled self.monotonic = monotonic + self.time_stamp = datetime.utcnow() def _validate_update(self, value: metrics_api.ValueT): if not self.enabled: @@ -42,40 +45,52 @@ def _validate_update(self, value: metrics_api.ValueT): return False return True + def __repr__(self): + return '{}(data="{}", time_stamp={})'.format( + type(self).__name__, self.data, self.time_stamp + ) + + def __str__(self): + return '{}(data="{}", time_stamp={})'.format( + type(self).__name__, self.data, self.time_stamp + ) + -class CounterHandle(metrics_api.CounterHandle, BaseHandle): +class CounterHandle(metrics_api.CounterHandle, MetricHandle): def update(self, value: metrics_api.ValueT) -> None: if self._validate_update(value): if self.monotonic and value < 0: logger.warning("Monotonic counter cannot descend.") return self.data += value + self.time_stamp = datetime.utcnow() def add(self, value: metrics_api.ValueT) -> None: """See `opentelemetry.metrics.CounterHandle._add`.""" self.update(value) -class GaugeHandle(metrics_api.GaugeHandle, BaseHandle): +class GaugeHandle(metrics_api.GaugeHandle, MetricHandle): def update(self, value: metrics_api.ValueT) -> None: if self._validate_update(value): if self.monotonic and value < self.data: logger.warning("Monotonic gauge cannot descend.") return self.data = value + self.time_stamp = datetime.utcnow() def set(self, value: metrics_api.ValueT) -> None: """See `opentelemetry.metrics.GaugeHandle._set`.""" self.update(value) -class MeasureHandle(metrics_api.MeasureHandle, BaseHandle): +class MeasureHandle(metrics_api.MeasureHandle, MetricHandle): def update(self, value: metrics_api.ValueT) -> None: if self._validate_update(value): if self.monotonic and value < 0: logger.warning("Monotonic measure cannot accept negatives.") return - # TODO: record + self.time_stamp = datetime.utcnow() def record(self, value: metrics_api.ValueT) -> None: """See `opentelemetry.metrics.MeasureHandle._record`.""" @@ -85,7 +100,7 @@ def record(self, value: metrics_api.ValueT) -> None: class Metric(metrics_api.Metric): """See `opentelemetry.metrics.Metric`.""" - HANDLE_TYPE = BaseHandle + HANDLE_TYPE = MetricHandle def __init__( self, @@ -106,7 +121,7 @@ def __init__( self.monotonic = monotonic self.handles = {} - def get_handle(self, label_values: Sequence[str]) -> BaseHandle: + def get_handle(self, label_values: Sequence[str]) -> MetricHandle: """See `opentelemetry.metrics.Metric.get_handle`.""" handle = self.handles.get(label_values) if not handle: @@ -116,6 +131,16 @@ def get_handle(self, label_values: Sequence[str]) -> BaseHandle: self.handles[label_values] = handle return handle + def __repr__(self): + return '{}(name="{}", description={})'.format( + type(self).__name__, self.name, self.description + ) + + def __str__(self): + return '{}(name="{}", description={})'.format( + type(self).__name__, self.name, self.description + ) + class Counter(Metric): """See `opentelemetry.metrics.Counter`. diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/export/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/export/__init__.py new file mode 100644 index 00000000000..c655551a82f --- /dev/null +++ b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/export/__init__.py @@ -0,0 +1,73 @@ +# 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. + +from enum import Enum +from typing import Sequence, Tuple + +from .. import Metric + + +class MetricsExportResult(Enum): + SUCCESS = 0 + FAILED_RETRYABLE = 1 + FAILED_NOT_RETRYABLE = 2 + + +class MetricsExporter: + """Interface for exporting metrics. + + Interface to be implemented by services that want to export recorded in + its own format. + """ + + def export( + self, + metric_tuples: Sequence[Tuple[Metric, Sequence[str]]] + ) -> "MetricsExportResult": + """Exports a batch of telemetry data. + + Args: + metric_tuples: A sequence of metric pairs. A metric pair consists + of a `Metric` and a sequence of strings. The sequence of + strings will be used to get the corresponding `MetricHandle` + from the `Metric` to import. + + Returns: + The result of the export + """ + + def shutdown(self) -> None: + """Shuts down the exporter. + + Called when the SDK is shut down. + """ + + +class ConsoleMetricsExporter(MetricsExporter): + """Implementation of :class:`MetricsExporter` that prints metric handles + to the console. + + This class can be used for diagnostic purposes. It prints the exported + metric handles to the console STDOUT. + """ + + def export( + self, + metric_tuples: Sequence[Tuple[Metric, Sequence[str]]] + ) -> "MetricsExportResult": + for metric_tuple in metric_tuples: + handle = metric_tuple[0].get_handle(metric_tuple[1]) + print(str(metric_tuple[0]) + ", LabelValues: " + + str(metric_tuple[1]) + ", " + str(handle)) + return MetricsExportResult.SUCCESS From 12d73f9159c15350e2ab9ef895909e30e78e2483 Mon Sep 17 00:00:00 2001 From: Leighton Date: Fri, 27 Sep 2019 18:28:35 -0700 Subject: [PATCH 02/22] add example --- .../examples/metrics.py | 18 +++++++++++++++--- .../opentelemetry/ext/azure_monitor/metrics.py | 5 +++-- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/ext/opentelemetry-ext-azure-monitor/examples/metrics.py b/ext/opentelemetry-ext-azure-monitor/examples/metrics.py index af2fb91fba7..8adf98d75ff 100644 --- a/ext/opentelemetry-ext-azure-monitor/examples/metrics.py +++ b/ext/opentelemetry-ext-azure-monitor/examples/metrics.py @@ -14,7 +14,7 @@ from opentelemetry import metrics from opentelemetry.ext.azure_monitor import AzureMonitorMetricsExporter -from opentelemetry.sdk.metrics import Counter, Meter +from opentelemetry.sdk.metrics import Counter, Gauge, Meter from opentelemetry.sdk.metrics.export import ConsoleMetricsExporter metrics.set_preferred_meter_implementation(lambda T: Meter()) @@ -30,9 +30,21 @@ Counter, ("environment",), ) +gauge = meter.create_metric( + "process cpu usage", + "process cpu usage", + "percentage", + float, + Gauge, + ("environment",), +) label_values = ("staging",) +label_values2 = ("testing",) counter_handle = counter.get_handle(label_values) counter_handle.add(100) +gauge_handle = gauge.get_handle(label_values2) +gauge_handle.set(20.5) + -console_exporter.export([(counter, label_values)]) -exporter.export([(counter, label_values)]) +console_exporter.export([(counter, label_values), (gauge, label_values2)]) +exporter.export([(counter, label_values), (gauge, label_values2)]) diff --git a/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/metrics.py b/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/metrics.py index 60fa25c2802..fc07cfaa94f 100644 --- a/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/metrics.py +++ b/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/metrics.py @@ -32,6 +32,7 @@ def __init__(self, **options): raise ValueError("The instrumentation_key is not provided.") def export(self, metric_tuples): + # Metric tuples is a sequence of metric to label values pairs envelopes = tuple(map(self.metric_tuple_to_envelope, metric_tuples)) try: @@ -88,8 +89,8 @@ def metric_tuple_to_envelope(self, metric_tuple): properties = {metric.label_keys[idx]: label_values[idx] for idx, value in enumerate(label_values, start=0)} data_point = protocol.DataPoint(ns=metric.name, - name=metric.name, - value=handle.data) + name=metric.name, + value=handle.data) data = protocol.MetricData( metrics=[data_point], properties=properties From b2af66cf3c61f313b466ca51eaf09b51c453e201 Mon Sep 17 00:00:00 2001 From: Leighton Date: Fri, 27 Sep 2019 18:35:14 -0700 Subject: [PATCH 03/22] black --- .../ext/azure_monitor/metrics.py | 21 ++++++++++--------- .../sdk/metrics/export/__init__.py | 15 +++++++------ 2 files changed, 20 insertions(+), 16 deletions(-) diff --git a/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/metrics.py b/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/metrics.py index fc07cfaa94f..95c0eb080d3 100644 --- a/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/metrics.py +++ b/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/metrics.py @@ -18,8 +18,10 @@ import requests from opentelemetry.ext.azure_monitor import protocol, util -from opentelemetry.sdk.metrics.export import MetricsExporter,\ - MetricsExportResult +from opentelemetry.sdk.metrics.export import ( + MetricsExporter, + MetricsExportResult, +) logger = logging.getLogger(__name__) @@ -86,14 +88,13 @@ def metric_tuple_to_envelope(self, metric_tuple): ) envelope.name = "Microsoft.ApplicationInsights.Metric" # label_keys and label_values assumed to have the same length - properties = {metric.label_keys[idx]: label_values[idx] - for idx, value in enumerate(label_values, start=0)} - data_point = protocol.DataPoint(ns=metric.name, - name=metric.name, - value=handle.data) - data = protocol.MetricData( - metrics=[data_point], - properties=properties + properties = { + metric.label_keys[idx]: label_values[idx] + for idx, value in enumerate(label_values, start=0) + } + data_point = protocol.DataPoint( + ns=metric.name, name=metric.name, value=handle.data ) + data = protocol.MetricData(metrics=[data_point], properties=properties) envelope.data = protocol.Data(baseData=data, baseType="MetricData") return envelope diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/export/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/export/__init__.py index c655551a82f..e87a01450ce 100644 --- a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/export/__init__.py +++ b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/export/__init__.py @@ -32,8 +32,7 @@ class MetricsExporter: """ def export( - self, - metric_tuples: Sequence[Tuple[Metric, Sequence[str]]] + self, metric_tuples: Sequence[Tuple[Metric, Sequence[str]]] ) -> "MetricsExportResult": """Exports a batch of telemetry data. @@ -63,11 +62,15 @@ class ConsoleMetricsExporter(MetricsExporter): """ def export( - self, - metric_tuples: Sequence[Tuple[Metric, Sequence[str]]] + self, metric_tuples: Sequence[Tuple[Metric, Sequence[str]]] ) -> "MetricsExportResult": for metric_tuple in metric_tuples: handle = metric_tuple[0].get_handle(metric_tuple[1]) - print(str(metric_tuple[0]) + ", LabelValues: " + - str(metric_tuple[1]) + ", " + str(handle)) + print( + str(metric_tuple[0]) + + ", LabelValues: " + + str(metric_tuple[1]) + + ", " + + str(handle) + ) return MetricsExportResult.SUCCESS From 400fa9934b1416054c8c412ecdd3869ffd1f9b8a Mon Sep 17 00:00:00 2001 From: Leighton Date: Fri, 27 Sep 2019 18:41:28 -0700 Subject: [PATCH 04/22] isort' --- .../src/opentelemetry/ext/azure_monitor/metrics.py | 1 - opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py | 1 - 2 files changed, 2 deletions(-) diff --git a/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/metrics.py b/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/metrics.py index 95c0eb080d3..2f5507a6726 100644 --- a/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/metrics.py +++ b/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/metrics.py @@ -23,7 +23,6 @@ MetricsExportResult, ) - logger = logging.getLogger(__name__) diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py index 8c0ffb0e59d..6e646d339b3 100644 --- a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py +++ b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py @@ -14,7 +14,6 @@ import logging from datetime import datetime - from typing import Sequence, Tuple, Type from opentelemetry import metrics as metrics_api From 2090ea7de863e90e205ac8e70cf1897d9d07061c Mon Sep 17 00:00:00 2001 From: Leighton Date: Sat, 28 Sep 2019 11:06:13 -0700 Subject: [PATCH 05/22] address comments --- .../examples/metrics.py | 1 + .../ext/azure_monitor/metrics.py | 58 +++-------------- .../opentelemetry/ext/azure_monitor/trace.py | 50 ++------------- .../ext/azure_monitor/transport.py | 63 +++++++++++++++++++ .../opentelemetry/ext/azure_monitor/util.py | 4 ++ .../src/opentelemetry/sdk/metrics/__init__.py | 19 +++--- .../sdk/metrics/export/__init__.py | 12 ++-- 7 files changed, 95 insertions(+), 112 deletions(-) create mode 100644 ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/transport.py diff --git a/ext/opentelemetry-ext-azure-monitor/examples/metrics.py b/ext/opentelemetry-ext-azure-monitor/examples/metrics.py index 8adf98d75ff..22d17aeb4d8 100644 --- a/ext/opentelemetry-ext-azure-monitor/examples/metrics.py +++ b/ext/opentelemetry-ext-azure-monitor/examples/metrics.py @@ -48,3 +48,4 @@ console_exporter.export([(counter, label_values), (gauge, label_values2)]) exporter.export([(counter, label_values), (gauge, label_values2)]) +exporter.shutdown() diff --git a/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/metrics.py b/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/metrics.py index 2f5507a6726..08a5f3dabb7 100644 --- a/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/metrics.py +++ b/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/metrics.py @@ -12,12 +12,9 @@ # 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, util +from opentelemetry.ext.azure_monitor import protocol, transport, util from opentelemetry.sdk.metrics.export import ( MetricsExporter, MetricsExportResult, @@ -26,64 +23,25 @@ logger = logging.getLogger(__name__) -class AzureMonitorMetricsExporter(MetricsExporter): +class AzureMonitorMetricsExporter(MetricsExporter, transport.TransportMixin): def __init__(self, **options): self.options = util.Options(**options) - if not self.options.instrumentation_key: - raise ValueError("The instrumentation_key is not provided.") + util.validate_key(self.options.instrumentation_key) + self.export_result_type = MetricsExportResult def export(self, metric_tuples): # Metric tuples is a sequence of metric to label values pairs - envelopes = tuple(map(self.metric_tuple_to_envelope, metric_tuples)) - - 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 MetricsExportResult.FAILED_RETRYABLE - - text = "N/A" - data = None # noqa pylint: disable=unused-variable - try: - text = response.text - except Exception as ex: # noqa pylint: disable=broad-except - logger.warning("Error while reading response body %s.", ex) - else: - try: - data = json.loads(text) # noqa pylint: disable=unused-variable - except Exception: # noqa pylint: disable=broad-except - pass - - if response.status_code == 200: - logger.info("Transmission succeeded: %s.", text) - return MetricsExportResult.SUCCESS - - if response.status_code in ( - 206, # Partial Content - 429, # Too Many Requests - 500, # Internal Server Error - 503, # Service Unavailable - ): - return MetricsExportResult.FAILED_RETRYABLE - - return MetricsExportResult.FAILED_NOT_RETRYABLE + envelopes = tuple(map(self._metric_tuple_to_envelope, metric_tuples)) + return self._transmit(envelopes) - def metric_tuple_to_envelope(self, metric_tuple): + def _metric_tuple_to_envelope(self, metric_tuple): metric = metric_tuple[0] label_values = metric_tuple[1] handle = metric.get_handle(label_values) envelope = protocol.Envelope( iKey=self.options.instrumentation_key, tags=dict(util.azure_monitor_context), - time=handle.time_stamp.isoformat(), + time=handle.timestamp.isoformat(), ) envelope.name = "Microsoft.ApplicationInsights.Metric" # label_keys and label_values assumed to have the same length 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 16f9252fd01..62a6f45c622 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 @@ -16,9 +16,8 @@ import logging from urllib.parse import urlparse -import requests -from opentelemetry.ext.azure_monitor import protocol, util +from opentelemetry.ext.azure_monitor import protocol, transport, util from opentelemetry.sdk.trace.export import SpanExporter, SpanExportResult from opentelemetry.sdk.util import ns_to_iso_str from opentelemetry.trace import Span, SpanKind @@ -26,54 +25,15 @@ logger = logging.getLogger(__name__) -class AzureMonitorSpanExporter(SpanExporter): +class AzureMonitorSpanExporter(SpanExporter, transport.TransportMixin): def __init__(self, **options): self.options = util.Options(**options) - if not self.options.instrumentation_key: - raise ValueError("The instrumentation_key is not provided.") + util.validate_key(self.options.instrumentation_key) + self.export_result_type = SpanExportResult def export(self, spans): 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 # noqa pylint: disable=unused-variable - try: - text = response.text - except Exception as ex: # noqa pylint: disable=broad-except - logger.warning("Error while reading response body %s.", ex) - else: - try: - data = json.loads(text) # noqa pylint: disable=unused-variable - except Exception: # noqa pylint: disable=broad-except - 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 + return self._transmit(envelopes) @staticmethod def ns_to_duration(nanoseconds): diff --git a/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/transport.py b/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/transport.py new file mode 100644 index 00000000000..d97a6c9d3a4 --- /dev/null +++ b/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/transport.py @@ -0,0 +1,63 @@ +# 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 json +import logging +import requests + +logger = logging.getLogger(__name__) + + +class TransportMixin(object): + + def _transmit(self, envelopes): + 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 self.export_result_type.FAILED_RETRYABLE + + text = "N/A" + data = None # noqa pylint: disable=unused-variable + try: + text = response.text + except Exception as ex: # noqa pylint: disable=broad-except + logger.warning("Error while reading response body %s.", ex) + else: + try: + data = json.loads(text) # noqa pylint: disable=unused-variable + except Exception: # noqa pylint: disable=broad-except + pass + + if response.status_code == 200: + logger.info("Transmission succeeded: %s.", text) + return self.export_result_type.SUCCESS + + if response.status_code in ( + 206, # Partial Content + 429, # Too Many Requests + 500, # Internal Server Error + 503, # Service Unavailable + ): + return self.export_result_type.FAILED_RETRYABLE + + return self.export_result_type.FAILED_NOT_RETRYABLE 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 f97dbd3e33c..a4c84e89fe9 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 @@ -40,3 +40,7 @@ class Options(BaseObject): instrumentation_key=os.getenv("APPINSIGHTS_INSTRUMENTATIONKEY", None), timeout=10.0, # networking timeout in seconds ) + +def validate_key(instrumentation_key): + if not instrumentation_key: + raise ValueError("The instrumentation_key is not provided.") diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py index 6e646d339b3..11b4c08ff1b 100644 --- a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py +++ b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py @@ -13,10 +13,11 @@ # limitations under the License. import logging -from datetime import datetime + from typing import Sequence, Tuple, Type from opentelemetry import metrics as metrics_api +from opentelemetry.sdk.util import time_ns logger = logging.getLogger(__name__) @@ -32,7 +33,7 @@ def __init__( self.value_type = value_type self.enabled = enabled self.monotonic = monotonic - self.time_stamp = datetime.utcnow() + self.timestamp = time_ns() def _validate_update(self, value: metrics_api.ValueT): if not self.enabled: @@ -45,13 +46,13 @@ def _validate_update(self, value: metrics_api.ValueT): return True def __repr__(self): - return '{}(data="{}", time_stamp={})'.format( - type(self).__name__, self.data, self.time_stamp + return '{}(data="{}", timestamp={})'.format( + type(self).__name__, self.data, self.timestamp ) def __str__(self): - return '{}(data="{}", time_stamp={})'.format( - type(self).__name__, self.data, self.time_stamp + return '{}(data="{}", timestamp={})'.format( + type(self).__name__, self.data, self.timestamp ) @@ -62,7 +63,7 @@ def update(self, value: metrics_api.ValueT) -> None: logger.warning("Monotonic counter cannot descend.") return self.data += value - self.time_stamp = datetime.utcnow() + self.timestamp = time_ns() def add(self, value: metrics_api.ValueT) -> None: """See `opentelemetry.metrics.CounterHandle._add`.""" @@ -76,7 +77,7 @@ def update(self, value: metrics_api.ValueT) -> None: logger.warning("Monotonic gauge cannot descend.") return self.data = value - self.time_stamp = datetime.utcnow() + self.timestamp = time_ns() def set(self, value: metrics_api.ValueT) -> None: """See `opentelemetry.metrics.GaugeHandle._set`.""" @@ -89,7 +90,7 @@ def update(self, value: metrics_api.ValueT) -> None: if self.monotonic and value < 0: logger.warning("Monotonic measure cannot accept negatives.") return - self.time_stamp = datetime.utcnow() + self.timestamp = time_ns() def record(self, value: metrics_api.ValueT) -> None: """See `opentelemetry.metrics.MeasureHandle._record`.""" diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/export/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/export/__init__.py index e87a01450ce..822e76a9bdd 100644 --- a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/export/__init__.py +++ b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/export/__init__.py @@ -27,8 +27,8 @@ class MetricsExportResult(Enum): class MetricsExporter: """Interface for exporting metrics. - Interface to be implemented by services that want to export recorded in - its own format. + Interface to be implemented by services that want to export recorded + metrics in its own format. """ def export( @@ -66,11 +66,7 @@ def export( ) -> "MetricsExportResult": for metric_tuple in metric_tuples: handle = metric_tuple[0].get_handle(metric_tuple[1]) - print( - str(metric_tuple[0]) - + ", LabelValues: " - + str(metric_tuple[1]) - + ", " - + str(handle) + print('{}(data="{}", label_values="{}", metric_data={})'.format( + type(self).__name__, metric_tuple[0], metric_tuple[1], handle) ) return MetricsExportResult.SUCCESS From 0377559943fb10425cc0b70818b9c47b091ce7c4 Mon Sep 17 00:00:00 2001 From: Leighton Date: Sat, 28 Sep 2019 11:17:40 -0700 Subject: [PATCH 06/22] black --- .../src/opentelemetry/ext/azure_monitor/transport.py | 1 - .../src/opentelemetry/ext/azure_monitor/util.py | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/transport.py b/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/transport.py index d97a6c9d3a4..cbf1479f12e 100644 --- a/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/transport.py +++ b/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/transport.py @@ -20,7 +20,6 @@ class TransportMixin(object): - def _transmit(self, envelopes): try: response = requests.post( 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 a4c84e89fe9..3ac3f2599cd 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 @@ -41,6 +41,7 @@ class Options(BaseObject): timeout=10.0, # networking timeout in seconds ) + def validate_key(instrumentation_key): if not instrumentation_key: raise ValueError("The instrumentation_key is not provided.") From 214d7a062211eca8635cd2d30194a25a6d35f691 Mon Sep 17 00:00:00 2001 From: Leighton Date: Sat, 28 Sep 2019 11:30:37 -0700 Subject: [PATCH 07/22] black --- .../src/opentelemetry/sdk/metrics/export/__init__.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/export/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/export/__init__.py index 822e76a9bdd..bac19412b12 100644 --- a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/export/__init__.py +++ b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/export/__init__.py @@ -66,7 +66,12 @@ def export( ) -> "MetricsExportResult": for metric_tuple in metric_tuples: handle = metric_tuple[0].get_handle(metric_tuple[1]) - print('{}(data="{}", label_values="{}", metric_data={})'.format( - type(self).__name__, metric_tuple[0], metric_tuple[1], handle) + print( + '{}(data="{}", label_values="{}", metric_data={})'.format( + type(self).__name__, + metric_tuple[0], + metric_tuple[1], + handle, + ) ) return MetricsExportResult.SUCCESS From 062d2671160f3cf2e4a5b1a42b5f37273844d071 Mon Sep 17 00:00:00 2001 From: Leighton Date: Sat, 28 Sep 2019 11:42:46 -0700 Subject: [PATCH 08/22] lint --- .../src/opentelemetry/ext/azure_monitor/transport.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/transport.py b/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/transport.py index cbf1479f12e..3af3a92a58c 100644 --- a/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/transport.py +++ b/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/transport.py @@ -19,7 +19,7 @@ logger = logging.getLogger(__name__) -class TransportMixin(object): +class TransportMixin: def _transmit(self, envelopes): try: response = requests.post( From 736b2b759bf37efb97ec457ffdec3ce46a0f8edf Mon Sep 17 00:00:00 2001 From: Leighton Date: Sat, 28 Sep 2019 11:53:36 -0700 Subject: [PATCH 09/22] isort --- opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py | 1 - 1 file changed, 1 deletion(-) diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py index 11b4c08ff1b..959f739662c 100644 --- a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py +++ b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py @@ -13,7 +13,6 @@ # limitations under the License. import logging - from typing import Sequence, Tuple, Type from opentelemetry import metrics as metrics_api From 501d5155242732c85564f1913d8c5ecd66cd5cbb Mon Sep 17 00:00:00 2001 From: Leighton Date: Sat, 28 Sep 2019 11:59:17 -0700 Subject: [PATCH 10/22] isort --- .../src/opentelemetry/ext/azure_monitor/trace.py | 1 - .../src/opentelemetry/ext/azure_monitor/transport.py | 1 + 2 files 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 62a6f45c622..fc650e573f3 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 @@ -16,7 +16,6 @@ import logging from urllib.parse import urlparse - from opentelemetry.ext.azure_monitor import protocol, transport, util from opentelemetry.sdk.trace.export import SpanExporter, SpanExportResult from opentelemetry.sdk.util import ns_to_iso_str diff --git a/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/transport.py b/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/transport.py index 3af3a92a58c..72bd2ae7486 100644 --- a/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/transport.py +++ b/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/transport.py @@ -14,6 +14,7 @@ import json import logging + import requests logger = logging.getLogger(__name__) From b217627840441928184ac5c411491c5ddd3e108c Mon Sep 17 00:00:00 2001 From: Leighton Date: Mon, 30 Sep 2019 10:28:37 -0700 Subject: [PATCH 11/22] address comments --- .../ext/azure_monitor/transport.py | 8 ++--- .../src/opentelemetry/sdk/metrics/__init__.py | 34 +++++++------------ 2 files changed, 16 insertions(+), 26 deletions(-) diff --git a/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/transport.py b/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/transport.py index 72bd2ae7486..f51d49656fb 100644 --- a/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/transport.py +++ b/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/transport.py @@ -32,16 +32,16 @@ def _transmit(self, envelopes): }, timeout=self.options.timeout, ) - except requests.RequestException as ex: - logger.warning("Transient client side error %s.", ex) + except requests.RequestException: + logger.exception("Transient client side error.") return self.export_result_type.FAILED_RETRYABLE text = "N/A" data = None # noqa pylint: disable=unused-variable try: text = response.text - except Exception as ex: # noqa pylint: disable=broad-except - logger.warning("Error while reading response body %s.", ex) + except Exception: # noqa pylint: disable=broad-except + logger.exception("Error while reading response body %s.") else: try: data = json.loads(text) # noqa pylint: disable=unused-variable diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py index 959f739662c..2f0d59635fa 100644 --- a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py +++ b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py @@ -21,7 +21,7 @@ logger = logging.getLogger(__name__) -class MetricHandle: +class BaseHandle: def __init__( self, value_type: Type[metrics_api.ValueT], @@ -32,7 +32,7 @@ def __init__( self.value_type = value_type self.enabled = enabled self.monotonic = monotonic - self.timestamp = time_ns() + self.last_update_timestamp = time_ns() def _validate_update(self, value: metrics_api.ValueT): if not self.enabled: @@ -45,51 +45,46 @@ def _validate_update(self, value: metrics_api.ValueT): return True def __repr__(self): - return '{}(data="{}", timestamp={})'.format( - type(self).__name__, self.data, self.timestamp - ) - - def __str__(self): - return '{}(data="{}", timestamp={})'.format( - type(self).__name__, self.data, self.timestamp + return '{}(data="{}", last_update_timestamp={})'.format( + type(self).__name__, self.data, self.last_update_timestamp ) -class CounterHandle(metrics_api.CounterHandle, MetricHandle): +class CounterHandle(metrics_api.CounterHandle, BaseHandle): def update(self, value: metrics_api.ValueT) -> None: if self._validate_update(value): if self.monotonic and value < 0: logger.warning("Monotonic counter cannot descend.") return self.data += value - self.timestamp = time_ns() + self.last_update_timestamp = time_ns() def add(self, value: metrics_api.ValueT) -> None: """See `opentelemetry.metrics.CounterHandle._add`.""" self.update(value) -class GaugeHandle(metrics_api.GaugeHandle, MetricHandle): +class GaugeHandle(metrics_api.GaugeHandle, BaseHandle): def update(self, value: metrics_api.ValueT) -> None: if self._validate_update(value): if self.monotonic and value < self.data: logger.warning("Monotonic gauge cannot descend.") return self.data = value - self.timestamp = time_ns() + self.last_update_timestamp = time_ns() def set(self, value: metrics_api.ValueT) -> None: """See `opentelemetry.metrics.GaugeHandle._set`.""" self.update(value) -class MeasureHandle(metrics_api.MeasureHandle, MetricHandle): +class MeasureHandle(metrics_api.MeasureHandle, BaseHandle): def update(self, value: metrics_api.ValueT) -> None: if self._validate_update(value): if self.monotonic and value < 0: logger.warning("Monotonic measure cannot accept negatives.") return - self.timestamp = time_ns() + self.last_update_timestamp = time_ns() def record(self, value: metrics_api.ValueT) -> None: """See `opentelemetry.metrics.MeasureHandle._record`.""" @@ -99,7 +94,7 @@ def record(self, value: metrics_api.ValueT) -> None: class Metric(metrics_api.Metric): """See `opentelemetry.metrics.Metric`.""" - HANDLE_TYPE = MetricHandle + HANDLE_TYPE = BaseHandle def __init__( self, @@ -120,7 +115,7 @@ def __init__( self.monotonic = monotonic self.handles = {} - def get_handle(self, label_values: Sequence[str]) -> MetricHandle: + def get_handle(self, label_values: Sequence[str]) -> BaseHandle: """See `opentelemetry.metrics.Metric.get_handle`.""" handle = self.handles.get(label_values) if not handle: @@ -135,11 +130,6 @@ def __repr__(self): type(self).__name__, self.name, self.description ) - def __str__(self): - return '{}(name="{}", description={})'.format( - type(self).__name__, self.name, self.description - ) - class Counter(Metric): """See `opentelemetry.metrics.Counter`. From e893947e1b33ee9dfa6f07aa8f8d16e650176408 Mon Sep 17 00:00:00 2001 From: Leighton Date: Mon, 30 Sep 2019 11:34:15 -0700 Subject: [PATCH 12/22] Fix timestamp format --- .../examples/metrics.py | 1 + .../src/opentelemetry/ext/azure_monitor/metrics.py | 2 +- .../src/opentelemetry/ext/azure_monitor/transport.py | 2 +- .../src/opentelemetry/sdk/metrics/__init__.py | 10 +++++----- 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/ext/opentelemetry-ext-azure-monitor/examples/metrics.py b/ext/opentelemetry-ext-azure-monitor/examples/metrics.py index 22d17aeb4d8..1952d1891ce 100644 --- a/ext/opentelemetry-ext-azure-monitor/examples/metrics.py +++ b/ext/opentelemetry-ext-azure-monitor/examples/metrics.py @@ -48,4 +48,5 @@ console_exporter.export([(counter, label_values), (gauge, label_values2)]) exporter.export([(counter, label_values), (gauge, label_values2)]) +console_exporter.shutdown() exporter.shutdown() diff --git a/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/metrics.py b/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/metrics.py index 08a5f3dabb7..ae2d352dd3e 100644 --- a/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/metrics.py +++ b/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/metrics.py @@ -41,7 +41,7 @@ def _metric_tuple_to_envelope(self, metric_tuple): envelope = protocol.Envelope( iKey=self.options.instrumentation_key, tags=dict(util.azure_monitor_context), - time=handle.timestamp.isoformat(), + time=handle.last_update_timestamp, ) envelope.name = "Microsoft.ApplicationInsights.Metric" # label_keys and label_values assumed to have the same length diff --git a/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/transport.py b/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/transport.py index f51d49656fb..eae68ca9af7 100644 --- a/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/transport.py +++ b/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/transport.py @@ -49,7 +49,7 @@ def _transmit(self, envelopes): pass if response.status_code == 200: - logger.info("Transmission succeeded: %s.", text) + logger.debug("Transmission succeeded: %s.", text) return self.export_result_type.SUCCESS if response.status_code in ( diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py index 2f0d59635fa..4b4e4a26b88 100644 --- a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py +++ b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py @@ -16,7 +16,7 @@ from typing import Sequence, Tuple, Type from opentelemetry import metrics as metrics_api -from opentelemetry.sdk.util import time_ns +from opentelemetry.sdk.util import ns_to_iso_str, time_ns logger = logging.getLogger(__name__) @@ -32,7 +32,7 @@ def __init__( self.value_type = value_type self.enabled = enabled self.monotonic = monotonic - self.last_update_timestamp = time_ns() + self.last_update_timestamp = ns_to_iso_str(time_ns()) def _validate_update(self, value: metrics_api.ValueT): if not self.enabled: @@ -57,7 +57,7 @@ def update(self, value: metrics_api.ValueT) -> None: logger.warning("Monotonic counter cannot descend.") return self.data += value - self.last_update_timestamp = time_ns() + self.last_update_timestamp = ns_to_iso_str(time_ns()) def add(self, value: metrics_api.ValueT) -> None: """See `opentelemetry.metrics.CounterHandle._add`.""" @@ -71,7 +71,7 @@ def update(self, value: metrics_api.ValueT) -> None: logger.warning("Monotonic gauge cannot descend.") return self.data = value - self.last_update_timestamp = time_ns() + self.last_update_timestamp = ns_to_iso_str(time_ns()) def set(self, value: metrics_api.ValueT) -> None: """See `opentelemetry.metrics.GaugeHandle._set`.""" @@ -84,7 +84,7 @@ def update(self, value: metrics_api.ValueT) -> None: if self.monotonic and value < 0: logger.warning("Monotonic measure cannot accept negatives.") return - self.last_update_timestamp = time_ns() + self.last_update_timestamp = ns_to_iso_str(time_ns()) def record(self, value: metrics_api.ValueT) -> None: """See `opentelemetry.metrics.MeasureHandle._record`.""" From 352d807de10de28b0f3751a46e747d0f64b6128f Mon Sep 17 00:00:00 2001 From: Leighton Date: Mon, 30 Sep 2019 12:17:33 -0700 Subject: [PATCH 13/22] fix spacing --- .../src/opentelemetry/sdk/metrics/export/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/export/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/export/__init__.py index bac19412b12..51cbe1a18ca 100644 --- a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/export/__init__.py +++ b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/export/__init__.py @@ -39,7 +39,7 @@ def export( Args: metric_tuples: A sequence of metric pairs. A metric pair consists of a `Metric` and a sequence of strings. The sequence of - strings will be used to get the corresponding `MetricHandle` + strings will be used to get the corresponding `MetricHandle` from the `Metric` to import. Returns: From 3c1b6e71f83bc08851dc58a4bb2c1cefb933ae59 Mon Sep 17 00:00:00 2001 From: Leighton Date: Mon, 30 Sep 2019 14:24:07 -0700 Subject: [PATCH 14/22] Consolidate exportresult --- .../examples/metrics.py | 21 +++---------------- .../ext/azure_monitor/metrics.py | 17 ++++++++------- .../opentelemetry/ext/azure_monitor/trace.py | 3 +-- .../ext/azure_monitor/transport.py | 8 +++---- .../src/opentelemetry/sdk/metrics/__init__.py | 10 ++++----- .../sdk/metrics/export/__init__.py | 13 ++++-------- .../sdk/trace/export/__init__.py | 12 +++-------- .../trace/export/in_memory_span_exporter.py | 9 ++++---- .../src/opentelemetry/sdk/util.py | 7 +++++++ .../tests/trace/export/test_export.py | 5 +++-- .../export/test_in_memory_span_exporter.py | 4 ++-- 11 files changed, 47 insertions(+), 62 deletions(-) diff --git a/ext/opentelemetry-ext-azure-monitor/examples/metrics.py b/ext/opentelemetry-ext-azure-monitor/examples/metrics.py index 1952d1891ce..869963a0add 100644 --- a/ext/opentelemetry-ext-azure-monitor/examples/metrics.py +++ b/ext/opentelemetry-ext-azure-monitor/examples/metrics.py @@ -14,13 +14,11 @@ from opentelemetry import metrics from opentelemetry.ext.azure_monitor import AzureMonitorMetricsExporter -from opentelemetry.sdk.metrics import Counter, Gauge, Meter -from opentelemetry.sdk.metrics.export import ConsoleMetricsExporter +from opentelemetry.sdk.metrics import Counter, Meter metrics.set_preferred_meter_implementation(lambda T: Meter()) meter = metrics.meter() exporter = AzureMonitorMetricsExporter() -console_exporter = ConsoleMetricsExporter() counter = meter.create_metric( "available memory", @@ -30,23 +28,10 @@ Counter, ("environment",), ) -gauge = meter.create_metric( - "process cpu usage", - "process cpu usage", - "percentage", - float, - Gauge, - ("environment",), -) + label_values = ("staging",) -label_values2 = ("testing",) counter_handle = counter.get_handle(label_values) counter_handle.add(100) -gauge_handle = gauge.get_handle(label_values2) -gauge_handle.set(20.5) - -console_exporter.export([(counter, label_values), (gauge, label_values2)]) -exporter.export([(counter, label_values), (gauge, label_values2)]) -console_exporter.shutdown() +exporter.export([(counter, label_values)]) exporter.shutdown() diff --git a/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/metrics.py b/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/metrics.py index ae2d352dd3e..0d2624cd5f4 100644 --- a/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/metrics.py +++ b/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/metrics.py @@ -15,10 +15,8 @@ import logging from opentelemetry.ext.azure_monitor import protocol, transport, util -from opentelemetry.sdk.metrics.export import ( - MetricsExporter, - MetricsExportResult, -) +from opentelemetry.sdk.metrics.export import MetricsExporter +from opentelemetry.sdk.util import ns_to_iso_str logger = logging.getLogger(__name__) @@ -27,10 +25,15 @@ class AzureMonitorMetricsExporter(MetricsExporter, transport.TransportMixin): def __init__(self, **options): self.options = util.Options(**options) util.validate_key(self.options.instrumentation_key) - self.export_result_type = MetricsExportResult def export(self, metric_tuples): - # Metric tuples is a sequence of metric to label values pairs + """Exports metrics with specified label values. + + Args: + metrics_tuples: a sequence of metric to label value pairs. + The metric handles with the corresponding label values for + each metric will be used for exporting. + """ envelopes = tuple(map(self._metric_tuple_to_envelope, metric_tuples)) return self._transmit(envelopes) @@ -41,7 +44,7 @@ def _metric_tuple_to_envelope(self, metric_tuple): envelope = protocol.Envelope( iKey=self.options.instrumentation_key, tags=dict(util.azure_monitor_context), - time=handle.last_update_timestamp, + time=ns_to_iso_str(handle.last_update_timestamp), ) envelope.name = "Microsoft.ApplicationInsights.Metric" # label_keys and label_values assumed to have the same length 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 fc650e573f3..320a0fe12a3 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 @@ -17,7 +17,7 @@ from urllib.parse import urlparse from opentelemetry.ext.azure_monitor import protocol, transport, util -from opentelemetry.sdk.trace.export import SpanExporter, SpanExportResult +from opentelemetry.sdk.trace.export import SpanExporter from opentelemetry.sdk.util import ns_to_iso_str from opentelemetry.trace import Span, SpanKind @@ -28,7 +28,6 @@ class AzureMonitorSpanExporter(SpanExporter, transport.TransportMixin): def __init__(self, **options): self.options = util.Options(**options) util.validate_key(self.options.instrumentation_key) - self.export_result_type = SpanExportResult def export(self, spans): envelopes = tuple(map(self.span_to_envelope, spans)) diff --git a/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/transport.py b/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/transport.py index eae68ca9af7..887c284bacd 100644 --- a/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/transport.py +++ b/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/transport.py @@ -32,16 +32,16 @@ def _transmit(self, envelopes): }, timeout=self.options.timeout, ) - except requests.RequestException: - logger.exception("Transient client side error.") + except requests.RequestException as ex: + logger.warning("Transient client side error %s.", ex) return self.export_result_type.FAILED_RETRYABLE text = "N/A" data = None # noqa pylint: disable=unused-variable try: text = response.text - except Exception: # noqa pylint: disable=broad-except - logger.exception("Error while reading response body %s.") + except Exception as ex: # noqa pylint: disable=broad-except + logger.warning("Error while reading response body %s.", ex) else: try: data = json.loads(text) # noqa pylint: disable=unused-variable diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py index 4b4e4a26b88..2f0d59635fa 100644 --- a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py +++ b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py @@ -16,7 +16,7 @@ from typing import Sequence, Tuple, Type from opentelemetry import metrics as metrics_api -from opentelemetry.sdk.util import ns_to_iso_str, time_ns +from opentelemetry.sdk.util import time_ns logger = logging.getLogger(__name__) @@ -32,7 +32,7 @@ def __init__( self.value_type = value_type self.enabled = enabled self.monotonic = monotonic - self.last_update_timestamp = ns_to_iso_str(time_ns()) + self.last_update_timestamp = time_ns() def _validate_update(self, value: metrics_api.ValueT): if not self.enabled: @@ -57,7 +57,7 @@ def update(self, value: metrics_api.ValueT) -> None: logger.warning("Monotonic counter cannot descend.") return self.data += value - self.last_update_timestamp = ns_to_iso_str(time_ns()) + self.last_update_timestamp = time_ns() def add(self, value: metrics_api.ValueT) -> None: """See `opentelemetry.metrics.CounterHandle._add`.""" @@ -71,7 +71,7 @@ def update(self, value: metrics_api.ValueT) -> None: logger.warning("Monotonic gauge cannot descend.") return self.data = value - self.last_update_timestamp = ns_to_iso_str(time_ns()) + self.last_update_timestamp = time_ns() def set(self, value: metrics_api.ValueT) -> None: """See `opentelemetry.metrics.GaugeHandle._set`.""" @@ -84,7 +84,7 @@ def update(self, value: metrics_api.ValueT) -> None: if self.monotonic and value < 0: logger.warning("Monotonic measure cannot accept negatives.") return - self.last_update_timestamp = ns_to_iso_str(time_ns()) + self.last_update_timestamp = time_ns() def record(self, value: metrics_api.ValueT) -> None: """See `opentelemetry.metrics.MeasureHandle._record`.""" diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/export/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/export/__init__.py index 51cbe1a18ca..1e748b83420 100644 --- a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/export/__init__.py +++ b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/export/__init__.py @@ -16,12 +16,7 @@ from typing import Sequence, Tuple from .. import Metric - - -class MetricsExportResult(Enum): - SUCCESS = 0 - FAILED_RETRYABLE = 1 - FAILED_NOT_RETRYABLE = 2 +from opentelemetry.sdk.util import ExportResult class MetricsExporter: @@ -33,7 +28,7 @@ class MetricsExporter: def export( self, metric_tuples: Sequence[Tuple[Metric, Sequence[str]]] - ) -> "MetricsExportResult": + ) -> "ExportResult": """Exports a batch of telemetry data. Args: @@ -63,7 +58,7 @@ class ConsoleMetricsExporter(MetricsExporter): def export( self, metric_tuples: Sequence[Tuple[Metric, Sequence[str]]] - ) -> "MetricsExportResult": + ) -> "ExportResult": for metric_tuple in metric_tuples: handle = metric_tuple[0].get_handle(metric_tuple[1]) print( @@ -74,4 +69,4 @@ def export( handle, ) ) - return MetricsExportResult.SUCCESS + return ExportResult.SUCCESS diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/trace/export/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/trace/export/__init__.py index 2f42c19c393..688ec5fff39 100644 --- a/opentelemetry-sdk/src/opentelemetry/sdk/trace/export/__init__.py +++ b/opentelemetry-sdk/src/opentelemetry/sdk/trace/export/__init__.py @@ -26,12 +26,6 @@ logger = logging.getLogger(__name__) -class SpanExportResult(Enum): - SUCCESS = 0 - FAILED_RETRYABLE = 1 - FAILED_NOT_RETRYABLE = 2 - - class SpanExporter: """Interface for exporting spans. @@ -42,7 +36,7 @@ class SpanExporter: `SimpleExportSpanProcessor` or a `BatchSpanProcessor`. """ - def export(self, spans: typing.Sequence[Span]) -> "SpanExportResult": + def export(self, spans: typing.Sequence[Span]) -> "ExportResult": """Exports a batch of telemetry data. Args: @@ -217,7 +211,7 @@ class ConsoleSpanExporter(SpanExporter): spans to the console STDOUT. """ - def export(self, spans: typing.Sequence[Span]) -> SpanExportResult: + def export(self, spans: typing.Sequence[Span]) -> ExportResult: for span in spans: print(span) - return SpanExportResult.SUCCESS + return util.ExportResult.SUCCESS diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/trace/export/in_memory_span_exporter.py b/opentelemetry-sdk/src/opentelemetry/sdk/trace/export/in_memory_span_exporter.py index 0818805f39c..8c41ffa3cf8 100644 --- a/opentelemetry-sdk/src/opentelemetry/sdk/trace/export/in_memory_span_exporter.py +++ b/opentelemetry-sdk/src/opentelemetry/sdk/trace/export/in_memory_span_exporter.py @@ -16,7 +16,8 @@ import typing from .. import Span -from . import SpanExporter, SpanExportResult +from . import SpanExporter +from opentelemetry.sdk import util class InMemorySpanExporter(SpanExporter): @@ -42,13 +43,13 @@ def get_finished_spans(self): with self._lock: return tuple(self._finished_spans) - def export(self, spans: typing.Sequence[Span]) -> SpanExportResult: + def export(self, spans: typing.Sequence[Span]) -> util.ExportResult: """Stores a list of spans in memory.""" if self._stopped: - return SpanExportResult.FAILED_NOT_RETRYABLE + return util.ExportResult.FAILED_NOT_RETRYABLE with self._lock: self._finished_spans.extend(spans) - return SpanExportResult.SUCCESS + return util.ExportResult.SUCCESS def shutdown(self): """Shut downs the exporter. diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/util.py b/opentelemetry-sdk/src/opentelemetry/sdk/util.py index ede52e83078..4901b428caf 100644 --- a/opentelemetry-sdk/src/opentelemetry/sdk/util.py +++ b/opentelemetry-sdk/src/opentelemetry/sdk/util.py @@ -16,6 +16,7 @@ import threading import time from collections import OrderedDict, deque +from enum import Enum try: # pylint: disable=ungrouped-imports @@ -141,3 +142,9 @@ def from_map(cls, maxlen, mapping): # pylint: disable=protected-access bounded_dict._dict = mapping return bounded_dict + + +class ExportResult(Enum): + SUCCESS = 0 + FAILED_RETRYABLE = 1 + FAILED_NOT_RETRYABLE = 2 \ No newline at end of file diff --git a/opentelemetry-sdk/tests/trace/export/test_export.py b/opentelemetry-sdk/tests/trace/export/test_export.py index de7a5cd9d7e..c5cd08b8b40 100644 --- a/opentelemetry-sdk/tests/trace/export/test_export.py +++ b/opentelemetry-sdk/tests/trace/export/test_export.py @@ -19,6 +19,7 @@ from opentelemetry import trace as trace_api from opentelemetry.sdk import trace from opentelemetry.sdk.trace import export +from opentelemetry.sdk.util import ExportResult class MySpanExporter(export.SpanExporter): @@ -28,14 +29,14 @@ def __init__(self, destination, max_export_batch_size=None): self.destination = destination self.max_export_batch_size = max_export_batch_size - def export(self, spans: trace.Span) -> export.SpanExportResult: + def export(self, spans: trace.Span) -> ExportResult: if ( self.max_export_batch_size is not None and len(spans) > self.max_export_batch_size ): raise ValueError("Batch is too big") self.destination.extend(span.name for span in spans) - return export.SpanExportResult.SUCCESS + return ExportResult.SUCCESS class TestSimpleExportSpanProcessor(unittest.TestCase): diff --git a/opentelemetry-sdk/tests/trace/export/test_in_memory_span_exporter.py b/opentelemetry-sdk/tests/trace/export/test_in_memory_span_exporter.py index 01d4487e2ec..a1a285677e6 100644 --- a/opentelemetry-sdk/tests/trace/export/test_in_memory_span_exporter.py +++ b/opentelemetry-sdk/tests/trace/export/test_in_memory_span_exporter.py @@ -88,10 +88,10 @@ def test_return_code(self): memory_exporter = InMemorySpanExporter() ret = memory_exporter.export(span_list) - self.assertEqual(ret, export.SpanExportResult.SUCCESS) + self.assertEqual(ret, ExportResult.SUCCESS) memory_exporter.shutdown() # after shutdown export should fail ret = memory_exporter.export(span_list) - self.assertEqual(ret, export.SpanExportResult.FAILED_NOT_RETRYABLE) + self.assertEqual(ret, ExportResult.FAILED_NOT_RETRYABLE) From 820ae24426acedb6ec54dd814989ef365ae8dae1 Mon Sep 17 00:00:00 2001 From: Leighton Date: Mon, 30 Sep 2019 14:33:30 -0700 Subject: [PATCH 15/22] fix tests --- .../tests/trace/export/test_in_memory_span_exporter.py | 1 + 1 file changed, 1 insertion(+) diff --git a/opentelemetry-sdk/tests/trace/export/test_in_memory_span_exporter.py b/opentelemetry-sdk/tests/trace/export/test_in_memory_span_exporter.py index a1a285677e6..3f16fa0f428 100644 --- a/opentelemetry-sdk/tests/trace/export/test_in_memory_span_exporter.py +++ b/opentelemetry-sdk/tests/trace/export/test_in_memory_span_exporter.py @@ -21,6 +21,7 @@ from opentelemetry.sdk.trace.export.in_memory_span_exporter import ( InMemorySpanExporter, ) +from opentelemetry.sdk.util import ExportResult class TestInMemorySpanExporter(unittest.TestCase): From 4d1f5b9adc4754b8ec91cc008dc3d1a4d42a16c7 Mon Sep 17 00:00:00 2001 From: Leighton Date: Mon, 30 Sep 2019 16:40:32 -0700 Subject: [PATCH 16/22] Console Metrics Exporter --- README.md | 30 ++++++++++ .../examples/metrics.py | 37 ------------ .../ext/azure_monitor/metrics.py | 60 ------------------- .../opentelemetry/ext/azure_monitor/trace.py | 52 ++++++++++++++-- .../opentelemetry/ext/azure_monitor/util.py | 4 -- .../sdk/metrics/export/__init__.py | 13 ++-- .../sdk/trace/export/__init__.py | 12 +++- .../trace/export/in_memory_span_exporter.py | 9 ++- .../src/opentelemetry/sdk/util.py | 6 -- .../tests/trace/export/test_export.py | 5 +- .../export/test_in_memory_span_exporter.py | 5 +- 11 files changed, 103 insertions(+), 130 deletions(-) delete mode 100644 ext/opentelemetry-ext-azure-monitor/examples/metrics.py delete mode 100644 ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/metrics.py diff --git a/README.md b/README.md index 4b4e0f25ab3..c9234acbf77 100644 --- a/README.md +++ b/README.md @@ -39,6 +39,8 @@ pip install -e ./ext/opentelemetry-ext-{integration} ## Quick Start +### Tracing + ```python from opentelemetry import trace from opentelemetry.context import Context @@ -57,6 +59,34 @@ with tracer.start_span('foo'): print(Context) ``` +### Metrics + +```python +from opentelemetry import metrics +from opentelemetry.sdk.metrics import Counter, Meter +from opentelemetry.sdk.metrics.export import ConsoleMetricsExporter + +metrics.set_preferred_meter_implementation(lambda T: Meter()) +meter = metrics.meter() +exporter = ConsoleMetricsExporter() + +counter = meter.create_metric( + "available memory", + "available memory", + "bytes", + int, + Counter, + ("environment",), +) + +label_values = ("staging",) +counter_handle = counter.get_handle(label_values) +counter_handle.add(100) + +exporter.export([(counter, label_values)]) +exporter.shutdown() +``` + See the [API documentation](https://open-telemetry.github.io/opentelemetry-python/) for more detail, and the diff --git a/ext/opentelemetry-ext-azure-monitor/examples/metrics.py b/ext/opentelemetry-ext-azure-monitor/examples/metrics.py deleted file mode 100644 index 869963a0add..00000000000 --- a/ext/opentelemetry-ext-azure-monitor/examples/metrics.py +++ /dev/null @@ -1,37 +0,0 @@ -# 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. - -from opentelemetry import metrics -from opentelemetry.ext.azure_monitor import AzureMonitorMetricsExporter -from opentelemetry.sdk.metrics import Counter, Meter - -metrics.set_preferred_meter_implementation(lambda T: Meter()) -meter = metrics.meter() -exporter = AzureMonitorMetricsExporter() - -counter = meter.create_metric( - "available memory", - "available memory", - "bytes", - int, - Counter, - ("environment",), -) - -label_values = ("staging",) -counter_handle = counter.get_handle(label_values) -counter_handle.add(100) - -exporter.export([(counter, label_values)]) -exporter.shutdown() diff --git a/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/metrics.py b/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/metrics.py deleted file mode 100644 index 0d2624cd5f4..00000000000 --- a/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/metrics.py +++ /dev/null @@ -1,60 +0,0 @@ -# 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 logging - -from opentelemetry.ext.azure_monitor import protocol, transport, util -from opentelemetry.sdk.metrics.export import MetricsExporter -from opentelemetry.sdk.util import ns_to_iso_str - -logger = logging.getLogger(__name__) - - -class AzureMonitorMetricsExporter(MetricsExporter, transport.TransportMixin): - def __init__(self, **options): - self.options = util.Options(**options) - util.validate_key(self.options.instrumentation_key) - - def export(self, metric_tuples): - """Exports metrics with specified label values. - - Args: - metrics_tuples: a sequence of metric to label value pairs. - The metric handles with the corresponding label values for - each metric will be used for exporting. - """ - envelopes = tuple(map(self._metric_tuple_to_envelope, metric_tuples)) - return self._transmit(envelopes) - - def _metric_tuple_to_envelope(self, metric_tuple): - metric = metric_tuple[0] - label_values = metric_tuple[1] - handle = metric.get_handle(label_values) - envelope = protocol.Envelope( - iKey=self.options.instrumentation_key, - tags=dict(util.azure_monitor_context), - time=ns_to_iso_str(handle.last_update_timestamp), - ) - envelope.name = "Microsoft.ApplicationInsights.Metric" - # label_keys and label_values assumed to have the same length - properties = { - metric.label_keys[idx]: label_values[idx] - for idx, value in enumerate(label_values, start=0) - } - data_point = protocol.DataPoint( - ns=metric.name, name=metric.name, value=handle.data - ) - data = protocol.MetricData(metrics=[data_point], properties=properties) - envelope.data = protocol.Data(baseData=data, baseType="MetricData") - return envelope 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 320a0fe12a3..16f9252fd01 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 @@ -16,22 +16,64 @@ import logging from urllib.parse import urlparse -from opentelemetry.ext.azure_monitor import protocol, transport, util -from opentelemetry.sdk.trace.export import SpanExporter +import requests + +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 Span, SpanKind logger = logging.getLogger(__name__) -class AzureMonitorSpanExporter(SpanExporter, transport.TransportMixin): +class AzureMonitorSpanExporter(SpanExporter): def __init__(self, **options): self.options = util.Options(**options) - util.validate_key(self.options.instrumentation_key) + if not self.options.instrumentation_key: + raise ValueError("The instrumentation_key is not provided.") def export(self, spans): envelopes = tuple(map(self.span_to_envelope, spans)) - return self._transmit(envelopes) + + 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 # noqa pylint: disable=unused-variable + try: + text = response.text + except Exception as ex: # noqa pylint: disable=broad-except + logger.warning("Error while reading response body %s.", ex) + else: + try: + data = json.loads(text) # noqa pylint: disable=unused-variable + except Exception: # noqa pylint: disable=broad-except + 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 @staticmethod def ns_to_duration(nanoseconds): 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 3ac3f2599cd..5c244058b8c 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 @@ -41,7 +41,3 @@ class Options(BaseObject): timeout=10.0, # networking timeout in seconds ) - -def validate_key(instrumentation_key): - if not instrumentation_key: - raise ValueError("The instrumentation_key is not provided.") diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/export/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/export/__init__.py index 1e748b83420..51cbe1a18ca 100644 --- a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/export/__init__.py +++ b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/export/__init__.py @@ -16,7 +16,12 @@ from typing import Sequence, Tuple from .. import Metric -from opentelemetry.sdk.util import ExportResult + + +class MetricsExportResult(Enum): + SUCCESS = 0 + FAILED_RETRYABLE = 1 + FAILED_NOT_RETRYABLE = 2 class MetricsExporter: @@ -28,7 +33,7 @@ class MetricsExporter: def export( self, metric_tuples: Sequence[Tuple[Metric, Sequence[str]]] - ) -> "ExportResult": + ) -> "MetricsExportResult": """Exports a batch of telemetry data. Args: @@ -58,7 +63,7 @@ class ConsoleMetricsExporter(MetricsExporter): def export( self, metric_tuples: Sequence[Tuple[Metric, Sequence[str]]] - ) -> "ExportResult": + ) -> "MetricsExportResult": for metric_tuple in metric_tuples: handle = metric_tuple[0].get_handle(metric_tuple[1]) print( @@ -69,4 +74,4 @@ def export( handle, ) ) - return ExportResult.SUCCESS + return MetricsExportResult.SUCCESS diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/trace/export/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/trace/export/__init__.py index 688ec5fff39..2f42c19c393 100644 --- a/opentelemetry-sdk/src/opentelemetry/sdk/trace/export/__init__.py +++ b/opentelemetry-sdk/src/opentelemetry/sdk/trace/export/__init__.py @@ -26,6 +26,12 @@ logger = logging.getLogger(__name__) +class SpanExportResult(Enum): + SUCCESS = 0 + FAILED_RETRYABLE = 1 + FAILED_NOT_RETRYABLE = 2 + + class SpanExporter: """Interface for exporting spans. @@ -36,7 +42,7 @@ class SpanExporter: `SimpleExportSpanProcessor` or a `BatchSpanProcessor`. """ - def export(self, spans: typing.Sequence[Span]) -> "ExportResult": + def export(self, spans: typing.Sequence[Span]) -> "SpanExportResult": """Exports a batch of telemetry data. Args: @@ -211,7 +217,7 @@ class ConsoleSpanExporter(SpanExporter): spans to the console STDOUT. """ - def export(self, spans: typing.Sequence[Span]) -> ExportResult: + def export(self, spans: typing.Sequence[Span]) -> SpanExportResult: for span in spans: print(span) - return util.ExportResult.SUCCESS + return SpanExportResult.SUCCESS diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/trace/export/in_memory_span_exporter.py b/opentelemetry-sdk/src/opentelemetry/sdk/trace/export/in_memory_span_exporter.py index 8c41ffa3cf8..0818805f39c 100644 --- a/opentelemetry-sdk/src/opentelemetry/sdk/trace/export/in_memory_span_exporter.py +++ b/opentelemetry-sdk/src/opentelemetry/sdk/trace/export/in_memory_span_exporter.py @@ -16,8 +16,7 @@ import typing from .. import Span -from . import SpanExporter -from opentelemetry.sdk import util +from . import SpanExporter, SpanExportResult class InMemorySpanExporter(SpanExporter): @@ -43,13 +42,13 @@ def get_finished_spans(self): with self._lock: return tuple(self._finished_spans) - def export(self, spans: typing.Sequence[Span]) -> util.ExportResult: + def export(self, spans: typing.Sequence[Span]) -> SpanExportResult: """Stores a list of spans in memory.""" if self._stopped: - return util.ExportResult.FAILED_NOT_RETRYABLE + return SpanExportResult.FAILED_NOT_RETRYABLE with self._lock: self._finished_spans.extend(spans) - return util.ExportResult.SUCCESS + return SpanExportResult.SUCCESS def shutdown(self): """Shut downs the exporter. diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/util.py b/opentelemetry-sdk/src/opentelemetry/sdk/util.py index 4901b428caf..fc44fea4631 100644 --- a/opentelemetry-sdk/src/opentelemetry/sdk/util.py +++ b/opentelemetry-sdk/src/opentelemetry/sdk/util.py @@ -142,9 +142,3 @@ def from_map(cls, maxlen, mapping): # pylint: disable=protected-access bounded_dict._dict = mapping return bounded_dict - - -class ExportResult(Enum): - SUCCESS = 0 - FAILED_RETRYABLE = 1 - FAILED_NOT_RETRYABLE = 2 \ No newline at end of file diff --git a/opentelemetry-sdk/tests/trace/export/test_export.py b/opentelemetry-sdk/tests/trace/export/test_export.py index c5cd08b8b40..de7a5cd9d7e 100644 --- a/opentelemetry-sdk/tests/trace/export/test_export.py +++ b/opentelemetry-sdk/tests/trace/export/test_export.py @@ -19,7 +19,6 @@ from opentelemetry import trace as trace_api from opentelemetry.sdk import trace from opentelemetry.sdk.trace import export -from opentelemetry.sdk.util import ExportResult class MySpanExporter(export.SpanExporter): @@ -29,14 +28,14 @@ def __init__(self, destination, max_export_batch_size=None): self.destination = destination self.max_export_batch_size = max_export_batch_size - def export(self, spans: trace.Span) -> ExportResult: + def export(self, spans: trace.Span) -> export.SpanExportResult: if ( self.max_export_batch_size is not None and len(spans) > self.max_export_batch_size ): raise ValueError("Batch is too big") self.destination.extend(span.name for span in spans) - return ExportResult.SUCCESS + return export.SpanExportResult.SUCCESS class TestSimpleExportSpanProcessor(unittest.TestCase): diff --git a/opentelemetry-sdk/tests/trace/export/test_in_memory_span_exporter.py b/opentelemetry-sdk/tests/trace/export/test_in_memory_span_exporter.py index 3f16fa0f428..01d4487e2ec 100644 --- a/opentelemetry-sdk/tests/trace/export/test_in_memory_span_exporter.py +++ b/opentelemetry-sdk/tests/trace/export/test_in_memory_span_exporter.py @@ -21,7 +21,6 @@ from opentelemetry.sdk.trace.export.in_memory_span_exporter import ( InMemorySpanExporter, ) -from opentelemetry.sdk.util import ExportResult class TestInMemorySpanExporter(unittest.TestCase): @@ -89,10 +88,10 @@ def test_return_code(self): memory_exporter = InMemorySpanExporter() ret = memory_exporter.export(span_list) - self.assertEqual(ret, ExportResult.SUCCESS) + self.assertEqual(ret, export.SpanExportResult.SUCCESS) memory_exporter.shutdown() # after shutdown export should fail ret = memory_exporter.export(span_list) - self.assertEqual(ret, ExportResult.FAILED_NOT_RETRYABLE) + self.assertEqual(ret, export.SpanExportResult.FAILED_NOT_RETRYABLE) From 185ae6a82e54ef465abf5b980e0617f3ec5f39c1 Mon Sep 17 00:00:00 2001 From: Leighton Date: Mon, 30 Sep 2019 16:46:13 -0700 Subject: [PATCH 17/22] Remove changes --- .../ext/azure_monitor/__init__.py | 3 +- .../ext/azure_monitor/transport.py | 63 ------------------- .../opentelemetry/ext/azure_monitor/util.py | 1 - .../src/opentelemetry/sdk/metrics/__init__.py | 5 +- .../src/opentelemetry/sdk/util.py | 1 - 5 files changed, 4 insertions(+), 69 deletions(-) delete mode 100644 ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/transport.py diff --git a/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/__init__.py b/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/__init__.py index 93cd8e102d9..81222c546eb 100644 --- a/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/__init__.py +++ b/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/__init__.py @@ -17,8 +17,7 @@ Microsoft Azure Monitor. """ -from opentelemetry.ext.azure_monitor.metrics import AzureMonitorMetricsExporter from opentelemetry.ext.azure_monitor.trace import AzureMonitorSpanExporter from opentelemetry.ext.azure_monitor.version import __version__ # noqa -__all__ = ["AzureMonitorMetricsExporter", "AzureMonitorSpanExporter"] +__all__ = ["AzureMonitorSpanExporter"] diff --git a/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/transport.py b/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/transport.py deleted file mode 100644 index 887c284bacd..00000000000 --- a/ext/opentelemetry-ext-azure-monitor/src/opentelemetry/ext/azure_monitor/transport.py +++ /dev/null @@ -1,63 +0,0 @@ -# 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 json -import logging - -import requests - -logger = logging.getLogger(__name__) - - -class TransportMixin: - def _transmit(self, envelopes): - 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 self.export_result_type.FAILED_RETRYABLE - - text = "N/A" - data = None # noqa pylint: disable=unused-variable - try: - text = response.text - except Exception as ex: # noqa pylint: disable=broad-except - logger.warning("Error while reading response body %s.", ex) - else: - try: - data = json.loads(text) # noqa pylint: disable=unused-variable - except Exception: # noqa pylint: disable=broad-except - pass - - if response.status_code == 200: - logger.debug("Transmission succeeded: %s.", text) - return self.export_result_type.SUCCESS - - if response.status_code in ( - 206, # Partial Content - 429, # Too Many Requests - 500, # Internal Server Error - 503, # Service Unavailable - ): - return self.export_result_type.FAILED_RETRYABLE - - return self.export_result_type.FAILED_NOT_RETRYABLE 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 5c244058b8c..f97dbd3e33c 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 @@ -40,4 +40,3 @@ class Options(BaseObject): instrumentation_key=os.getenv("APPINSIGHTS_INSTRUMENTATIONKEY", None), timeout=10.0, # networking timeout in seconds ) - diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py index 2f0d59635fa..3806f30ec4f 100644 --- a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py +++ b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py @@ -56,8 +56,8 @@ def update(self, value: metrics_api.ValueT) -> None: if self.monotonic and value < 0: logger.warning("Monotonic counter cannot descend.") return - self.data += value self.last_update_timestamp = time_ns() + self.data += value def add(self, value: metrics_api.ValueT) -> None: """See `opentelemetry.metrics.CounterHandle._add`.""" @@ -70,8 +70,8 @@ def update(self, value: metrics_api.ValueT) -> None: if self.monotonic and value < self.data: logger.warning("Monotonic gauge cannot descend.") return - self.data = value self.last_update_timestamp = time_ns() + self.data = value def set(self, value: metrics_api.ValueT) -> None: """See `opentelemetry.metrics.GaugeHandle._set`.""" @@ -85,6 +85,7 @@ def update(self, value: metrics_api.ValueT) -> None: logger.warning("Monotonic measure cannot accept negatives.") return self.last_update_timestamp = time_ns() + # TODO: record def record(self, value: metrics_api.ValueT) -> None: """See `opentelemetry.metrics.MeasureHandle._record`.""" diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/util.py b/opentelemetry-sdk/src/opentelemetry/sdk/util.py index fc44fea4631..ede52e83078 100644 --- a/opentelemetry-sdk/src/opentelemetry/sdk/util.py +++ b/opentelemetry-sdk/src/opentelemetry/sdk/util.py @@ -16,7 +16,6 @@ import threading import time from collections import OrderedDict, deque -from enum import Enum try: # pylint: disable=ungrouped-imports From 3458703c4eb3e4a401560c2d2507be28115b9e3b Mon Sep 17 00:00:00 2001 From: Leighton Date: Tue, 29 Oct 2019 15:13:03 -0700 Subject: [PATCH 18/22] add tests --- .../sdk/metrics/export/__init__.py | 10 ++--- .../tests/metrics/export/__init__.py | 13 ++++++ .../tests/metrics/export/test_export.py | 44 +++++++++++++++++++ 3 files changed, 62 insertions(+), 5 deletions(-) create mode 100644 opentelemetry-sdk/tests/metrics/export/__init__.py create mode 100644 opentelemetry-sdk/tests/metrics/export/test_export.py diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/export/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/export/__init__.py index 51cbe1a18ca..12720e1a4a5 100644 --- a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/export/__init__.py +++ b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/export/__init__.py @@ -40,7 +40,7 @@ def export( metric_tuples: A sequence of metric pairs. A metric pair consists of a `Metric` and a sequence of strings. The sequence of strings will be used to get the corresponding `MetricHandle` - from the `Metric` to import. + from the `Metric` to export. Returns: The result of the export @@ -64,13 +64,13 @@ class ConsoleMetricsExporter(MetricsExporter): def export( self, metric_tuples: Sequence[Tuple[Metric, Sequence[str]]] ) -> "MetricsExportResult": - for metric_tuple in metric_tuples: - handle = metric_tuple[0].get_handle(metric_tuple[1]) + for metric, label_values in metric_tuples: + handle = metric.get_handle(label_values) print( '{}(data="{}", label_values="{}", metric_data={})'.format( type(self).__name__, - metric_tuple[0], - metric_tuple[1], + metric, + label_values, handle, ) ) diff --git a/opentelemetry-sdk/tests/metrics/export/__init__.py b/opentelemetry-sdk/tests/metrics/export/__init__.py new file mode 100644 index 00000000000..d853a7bcf65 --- /dev/null +++ b/opentelemetry-sdk/tests/metrics/export/__init__.py @@ -0,0 +1,13 @@ +# 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. diff --git a/opentelemetry-sdk/tests/metrics/export/test_export.py b/opentelemetry-sdk/tests/metrics/export/test_export.py new file mode 100644 index 00000000000..e676565d5b9 --- /dev/null +++ b/opentelemetry-sdk/tests/metrics/export/test_export.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 io +import sys +import unittest + +from opentelemetry.sdk import metrics +from opentelemetry.sdk.metrics.export import ConsoleMetricsExporter + +class TestConsoleMetricsExporter(unittest.TestCase): + def test_export(self): + exporter = ConsoleMetricsExporter() + metric = metrics.Counter( + "available memory", + "available memory", + "bytes", + int, + ("environment",), + ) + label_values = ("staging",) + handle = metric.get_handle(label_values) + result = '{}(data="{}", label_values="{}", metric_data={})\n'.format( + ConsoleMetricsExporter.__name__, + metric, + label_values, + handle, + ) + output = io.StringIO() + sys.stdout = output + exporter.export([(metric, label_values)]) + sys.stdout = sys.__stdout__ + self.assertEqual(len(output.getvalue()), len(result)) From 7501c34d425cde2c99370c342472fc3e3801a4c3 Mon Sep 17 00:00:00 2001 From: Leighton Date: Tue, 29 Oct 2019 15:31:51 -0700 Subject: [PATCH 19/22] lint --- .../sdk/metrics/export/__init__.py | 5 +---- .../tests/metrics/export/test_export.py | 18 ++++++++---------- 2 files changed, 9 insertions(+), 14 deletions(-) diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/export/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/export/__init__.py index 12720e1a4a5..f3b25d75efc 100644 --- a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/export/__init__.py +++ b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/export/__init__.py @@ -68,10 +68,7 @@ def export( handle = metric.get_handle(label_values) print( '{}(data="{}", label_values="{}", metric_data={})'.format( - type(self).__name__, - metric, - label_values, - handle, + type(self).__name__, metric, label_values, handle ) ) return MetricsExportResult.SUCCESS diff --git a/opentelemetry-sdk/tests/metrics/export/test_export.py b/opentelemetry-sdk/tests/metrics/export/test_export.py index e676565d5b9..bef3587f791 100644 --- a/opentelemetry-sdk/tests/metrics/export/test_export.py +++ b/opentelemetry-sdk/tests/metrics/export/test_export.py @@ -19,23 +19,21 @@ from opentelemetry.sdk import metrics from opentelemetry.sdk.metrics.export import ConsoleMetricsExporter + class TestConsoleMetricsExporter(unittest.TestCase): def test_export(self): exporter = ConsoleMetricsExporter() metric = metrics.Counter( - "available memory", - "available memory", - "bytes", - int, - ("environment",), - ) + "available memory", + "available memory", + "bytes", + int, + ("environment",), + ) label_values = ("staging",) handle = metric.get_handle(label_values) result = '{}(data="{}", label_values="{}", metric_data={})\n'.format( - ConsoleMetricsExporter.__name__, - metric, - label_values, - handle, + ConsoleMetricsExporter.__name__, metric, label_values, handle ) output = io.StringIO() sys.stdout = output From f029fb556a5dcc569f6782df9d9995700eac1f9e Mon Sep 17 00:00:00 2001 From: Leighton Date: Fri, 1 Nov 2019 13:45:25 -0700 Subject: [PATCH 20/22] fix tests --- .../opentelemetry/sdk/metrics/export/__init__.py | 3 +-- .../tests/metrics/export/test_export.py | 13 +++++-------- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/export/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/export/__init__.py index f3b25d75efc..b6cb396331a 100644 --- a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/export/__init__.py +++ b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/export/__init__.py @@ -54,8 +54,7 @@ def shutdown(self) -> None: class ConsoleMetricsExporter(MetricsExporter): - """Implementation of :class:`MetricsExporter` that prints metric handles - to the console. + """Implementation of `MetricsExporter` that prints metrics to the console. This class can be used for diagnostic purposes. It prints the exported metric handles to the console STDOUT. diff --git a/opentelemetry-sdk/tests/metrics/export/test_export.py b/opentelemetry-sdk/tests/metrics/export/test_export.py index bef3587f791..2741dd8df36 100644 --- a/opentelemetry-sdk/tests/metrics/export/test_export.py +++ b/opentelemetry-sdk/tests/metrics/export/test_export.py @@ -12,8 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -import io -import sys +import mock import unittest from opentelemetry.sdk import metrics @@ -32,11 +31,9 @@ def test_export(self): ) label_values = ("staging",) handle = metric.get_handle(label_values) - result = '{}(data="{}", label_values="{}", metric_data={})\n'.format( + result = '{}(data="{}", label_values="{}", metric_data={})'.format( ConsoleMetricsExporter.__name__, metric, label_values, handle ) - output = io.StringIO() - sys.stdout = output - exporter.export([(metric, label_values)]) - sys.stdout = sys.__stdout__ - self.assertEqual(len(output.getvalue()), len(result)) + with mock.patch("sys.stdout") as mock_stdout: + exporter.export([(metric, label_values)]) + mock_stdout.write.assert_any_call(result) From a1762b40103f46beb3029d2be6cc9b89d0054c23 Mon Sep 17 00:00:00 2001 From: Leighton Date: Fri, 1 Nov 2019 13:57:15 -0700 Subject: [PATCH 21/22] fix import --- opentelemetry-sdk/tests/metrics/export/test_export.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/opentelemetry-sdk/tests/metrics/export/test_export.py b/opentelemetry-sdk/tests/metrics/export/test_export.py index 2741dd8df36..194b8c264c7 100644 --- a/opentelemetry-sdk/tests/metrics/export/test_export.py +++ b/opentelemetry-sdk/tests/metrics/export/test_export.py @@ -12,8 +12,8 @@ # See the License for the specific language governing permissions and # limitations under the License. -import mock import unittest +from unittest import mock from opentelemetry.sdk import metrics from opentelemetry.sdk.metrics.export import ConsoleMetricsExporter From b6be47d569b4077b6e625d35b78d1c4f0882fd59 Mon Sep 17 00:00:00 2001 From: Leighton Date: Fri, 1 Nov 2019 14:05:43 -0700 Subject: [PATCH 22/22] fix lint --- opentelemetry-sdk/tests/metrics/export/test_export.py | 1 + 1 file changed, 1 insertion(+) diff --git a/opentelemetry-sdk/tests/metrics/export/test_export.py b/opentelemetry-sdk/tests/metrics/export/test_export.py index 194b8c264c7..ca8e8a36311 100644 --- a/opentelemetry-sdk/tests/metrics/export/test_export.py +++ b/opentelemetry-sdk/tests/metrics/export/test_export.py @@ -20,6 +20,7 @@ class TestConsoleMetricsExporter(unittest.TestCase): + # pylint: disable=no-self-use def test_export(self): exporter = ConsoleMetricsExporter() metric = metrics.Counter(