From 36210f35700b4d304ecf2449d109b02eaf8a7a95 Mon Sep 17 00:00:00 2001 From: Srikanth Chekuri Date: Fri, 20 May 2022 14:28:02 +0530 Subject: [PATCH 1/5] Configure auto instrumentation to support metrics --- .../sdk/_configuration/__init__.py | 38 ++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/_configuration/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/_configuration/__init__.py index efa6f31f894..73bea4cb7c9 100644 --- a/opentelemetry-sdk/src/opentelemetry/sdk/_configuration/__init__.py +++ b/opentelemetry-sdk/src/opentelemetry/sdk/_configuration/__init__.py @@ -27,15 +27,22 @@ from opentelemetry import trace from opentelemetry.environment_variables import ( OTEL_LOGS_EXPORTER, + OTEL_METRICS_EXPORTER, OTEL_PYTHON_ID_GENERATOR, OTEL_TRACES_EXPORTER, ) +from opentelemetry.metrics import set_meter_provider from opentelemetry.sdk._logs import ( LogEmitterProvider, LoggingHandler, set_log_emitter_provider, ) from opentelemetry.sdk._logs.export import BatchLogProcessor, LogExporter +from opentelemetry.sdk.metrics import MeterProvider +from opentelemetry.sdk.metrics.export import ( + MetricExporter, + PeriodicExportingMetricReader, +) from opentelemetry.sdk.resources import Resource from opentelemetry.sdk.trace import TracerProvider from opentelemetry.sdk.trace.export import BatchSpanProcessor, SpanExporter @@ -92,6 +99,33 @@ def _init_tracing( ) +def _init_metrics( + exporters: Dict[str, Type[MetricExporter]], + auto_instrumentation_version: Optional[str] = None, +): + # if env var OTEL_RESOURCE_ATTRIBUTES is given, it will read the service_name + # from the env variable else defaults to "unknown_service" + auto_resource = {} + # populate version if using auto-instrumentation + if auto_instrumentation_version: + auto_resource[ + ResourceAttributes.TELEMETRY_AUTO_VERSION + ] = auto_instrumentation_version + + metric_readers = [] + + for _, exporter_class in exporters.items(): + exporter_args = {} + metric_readers.append( + PeriodicExportingMetricReader(exporter_class(**exporter_args)) + ) + + provider = MeterProvider( + resource=Resource.create(auto_resource), metric_readers=metric_readers + ) + set_meter_provider(provider) + + def _init_logging( exporters: Dict[str, Type[LogExporter]], auto_instrumentation_version: Optional[str] = None, @@ -178,13 +212,15 @@ def _import_id_generator(id_generator_name: str) -> IdGenerator: def _initialize_components(auto_instrumentation_version): - trace_exporters, log_exporters = _import_exporters( + trace_exporters, metric_exporters, log_exporters = _import_exporters( _get_exporter_names(environ.get(OTEL_TRACES_EXPORTER)), + _get_exporter_names(environ.get(OTEL_METRICS_EXPORTER)), _get_exporter_names(environ.get(OTEL_LOGS_EXPORTER)), ) id_generator_name = _get_id_generator() id_generator = _import_id_generator(id_generator_name) _init_tracing(trace_exporters, id_generator, auto_instrumentation_version) + _init_metrics(metric_exporters, auto_instrumentation_version) _init_logging(log_exporters, auto_instrumentation_version) From ff3d6b503d81c1948b8c745d7f56bb21360d0930 Mon Sep 17 00:00:00 2001 From: Srikanth Chekuri Date: Thu, 2 Jun 2022 16:46:42 +0530 Subject: [PATCH 2/5] Add tests --- .../sdk/_configuration/__init__.py | 12 +- opentelemetry-sdk/tests/test_configurator.py | 116 +++++++++++++++++- 2 files changed, 123 insertions(+), 5 deletions(-) diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/_configuration/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/_configuration/__init__.py index 73bea4cb7c9..81d497bf949 100644 --- a/opentelemetry-sdk/src/opentelemetry/sdk/_configuration/__init__.py +++ b/opentelemetry-sdk/src/opentelemetry/sdk/_configuration/__init__.py @@ -175,9 +175,11 @@ def _import_config_components( def _import_exporters( trace_exporter_names: Sequence[str], + metric_exporter_names: Sequence[str], log_exporter_names: Sequence[str], ) -> Tuple[Dict[str, Type[SpanExporter]], Dict[str, Type[LogExporter]]]: trace_exporters = {} + metric_exporters = {} log_exporters = {} for (exporter_name, exporter_impl,) in _import_config_components( @@ -188,6 +190,14 @@ def _import_exporters( else: raise RuntimeError(f"{exporter_name} is not a trace exporter") + for (exporter_name, exporter_impl,) in _import_config_components( + metric_exporter_names, "opentelemetry_metrics_exporter" + ): + if issubclass(exporter_impl, MetricExporter): + metric_exporters[exporter_name] = exporter_impl + else: + raise RuntimeError(f"{exporter_name} is not a metric exporter") + for (exporter_name, exporter_impl,) in _import_config_components( log_exporter_names, "opentelemetry_logs_exporter" ): @@ -196,7 +206,7 @@ def _import_exporters( else: raise RuntimeError(f"{exporter_name} is not a log exporter") - return trace_exporters, log_exporters + return trace_exporters, metric_exporters, log_exporters def _import_id_generator(id_generator_name: str) -> IdGenerator: diff --git a/opentelemetry-sdk/tests/test_configurator.py b/opentelemetry-sdk/tests/test_configurator.py index 1905e6fc384..27a017ab45b 100644 --- a/opentelemetry-sdk/tests/test_configurator.py +++ b/opentelemetry-sdk/tests/test_configurator.py @@ -15,6 +15,7 @@ import logging from os import environ +from typing import Dict, Iterable, Optional from unittest import TestCase from unittest.mock import patch @@ -28,11 +29,19 @@ _import_exporters, _import_id_generator, _init_logging, + _init_metrics, _init_tracing, ) from opentelemetry.sdk._logs import LoggingHandler from opentelemetry.sdk._logs.export import ConsoleLogExporter -from opentelemetry.sdk.metrics.export import ConsoleMetricExporter +from opentelemetry.sdk.metrics import MeterProvider +from opentelemetry.sdk.metrics.export import ( + AggregationTemporality, + ConsoleMetricExporter, + Metric, + MetricExporter, + MetricReader, +) from opentelemetry.sdk.resources import SERVICE_NAME, Resource from opentelemetry.sdk.trace.export import ConsoleSpanExporter from opentelemetry.sdk.trace.id_generator import IdGenerator, RandomIdGenerator @@ -60,6 +69,10 @@ def get_log_emitter(self, name): return DummyLogEmitter(name, self.resource, self.processor) +class DummyMeterProvider(MeterProvider): + pass + + class DummyLogEmitter: def __init__(self, name, resource, processor): self.name = name @@ -92,6 +105,46 @@ def __init__(self, exporter): self.exporter = exporter +class DummyMetricReader(MetricReader): + def __init__( + self, + exporter: MetricExporter, + preferred_temporality: Dict[type, AggregationTemporality] = None, + preferred_aggregation: Dict[ + type, "opentelemetry.sdk.metrics.view.Aggregation" + ] = None, + export_interval_millis: Optional[float] = None, + export_timeout_millis: Optional[float] = None, + ) -> None: + super().__init__( + preferred_temporality=preferred_temporality, + preferred_aggregation=preferred_aggregation, + ) + self.exporter = exporter + + def _receive_metrics( + self, + metrics: Iterable[Metric], + timeout_millis: float = 10_000, + **kwargs, + ) -> None: + self.exporter.export(None) + + def shutdown(self, timeout_millis: float = 30_000, **kwargs) -> None: + return True + + +class DummyOTLPMetricExporter: + def __init__(self, *args, **kwargs): + self.export_called = False + + def export(self, batch): + self.export_called = True + + def shutdown(self): + pass + + class Exporter: def __init__(self): tracer_provider = trace.get_tracer_provider() @@ -278,6 +331,61 @@ def test_logging_init_exporter(self): self.assertTrue(provider.processor.exporter.export_called) +class TestMetricsInit(TestCase): + def setUp(self): + self.metric_reader_patch = patch( + "opentelemetry.sdk._configuration.PeriodicExportingMetricReader", + DummyMetricReader, + ) + self.provider_patch = patch( + "opentelemetry.sdk._configuration.MeterProvider", + DummyMeterProvider, + ) + self.set_provider_patch = patch( + "opentelemetry.sdk._configuration.set_meter_provider" + ) + + self.metric_reader_mock = self.metric_reader_patch.start() + self.provider_mock = self.provider_patch.start() + self.set_provider_mock = self.set_provider_patch.start() + + def tearDown(self): + self.metric_reader_patch.stop() + self.set_provider_patch.stop() + self.provider_patch.stop() + + def test_metrics_init_empty(self): + _init_metrics({}, "auto-version") + self.assertEqual(self.set_provider_mock.call_count, 1) + provider = self.set_provider_mock.call_args[0][0] + self.assertIsInstance(provider, DummyMeterProvider) + self.assertIsInstance(provider._sdk_config.resource, Resource) + self.assertEqual( + provider._sdk_config.resource.attributes.get( + "telemetry.auto.version" + ), + "auto-version", + ) + + @patch.dict( + environ, + {"OTEL_RESOURCE_ATTRIBUTES": "service.name=otlp-service"}, + ) + def test_metrics_init_exporter(self): + _init_metrics({"otlp": DummyOTLPMetricExporter}) + self.assertEqual(self.set_provider_mock.call_count, 1) + provider = self.set_provider_mock.call_args[0][0] + self.assertIsInstance(provider, DummyMeterProvider) + self.assertIsInstance(provider._sdk_config.resource, Resource) + self.assertEqual( + provider._sdk_config.resource.attributes.get("service.name"), + "otlp-service", + ) + reader = provider._sdk_config.metric_readers[0] + self.assertIsInstance(reader, DummyMetricReader) + self.assertIsInstance(reader.exporter, DummyOTLPMetricExporter) + + class TestExporterNames(TestCase): def test_otlp_exporter_overwrite(self): for exporter in [_EXPORTER_OTLP, _EXPORTER_OTLP_PROTO_GRPC]: @@ -302,8 +410,8 @@ def test_empty_exporters(self): class TestImportExporters(TestCase): def test_console_exporters(self): - trace_exporters, logs_exporters = _import_exporters( - ["console"], ["console"] + trace_exporters, metric_exporterts, logs_exporters = _import_exporters( + ["console"], ["console"], ["console"] ) self.assertEqual( trace_exporters["console"].__class__, ConsoleSpanExporter.__class__ @@ -312,6 +420,6 @@ def test_console_exporters(self): logs_exporters["console"].__class__, ConsoleLogExporter.__class__ ) self.assertEqual( - logs_exporters["console"].__class__, + metric_exporterts["console"].__class__, ConsoleMetricExporter.__class__, ) From e26ad12842e52622469bd3f0edc43ac854d4de3b Mon Sep 17 00:00:00 2001 From: Srikanth Chekuri Date: Thu, 2 Jun 2022 22:59:08 +0530 Subject: [PATCH 3/5] Update test_configurator.py --- opentelemetry-sdk/tests/test_configurator.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/opentelemetry-sdk/tests/test_configurator.py b/opentelemetry-sdk/tests/test_configurator.py index 27a017ab45b..1770161873f 100644 --- a/opentelemetry-sdk/tests/test_configurator.py +++ b/opentelemetry-sdk/tests/test_configurator.py @@ -42,6 +42,7 @@ MetricExporter, MetricReader, ) +from opentelemetry.sdk.metrics.view import Aggregation from opentelemetry.sdk.resources import SERVICE_NAME, Resource from opentelemetry.sdk.trace.export import ConsoleSpanExporter from opentelemetry.sdk.trace.id_generator import IdGenerator, RandomIdGenerator @@ -110,9 +111,7 @@ def __init__( self, exporter: MetricExporter, preferred_temporality: Dict[type, AggregationTemporality] = None, - preferred_aggregation: Dict[ - type, "opentelemetry.sdk.metrics.view.Aggregation" - ] = None, + preferred_aggregation: Dict[type, Aggregation] = None, export_interval_millis: Optional[float] = None, export_timeout_millis: Optional[float] = None, ) -> None: From a4f9cc498f5c0a8d89f20ec589a7d41b07983f01 Mon Sep 17 00:00:00 2001 From: Srikanth Chekuri Date: Thu, 2 Jun 2022 23:00:29 +0530 Subject: [PATCH 4/5] Add CHANGELOG entry --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5807688a184..f84463c40fd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ([#2727](https://github.com/open-telemetry/opentelemetry-python/pull/2727)) - fix: update entry point object references for metrics ([#2731](https://github.com/open-telemetry/opentelemetry-python/pull/2731)) +- Configure auto instrumentation to support metrics + ([#2705](https://github.com/open-telemetry/opentelemetry-python/pull/2705)) ## [1.12.0rc1-0.31b0](https://github.com/open-telemetry/opentelemetry-python/releases/tag/v1.12.0rc1-0.31b0) - 2022-05-17 From 18fbb39db40c802921d2bf2f3c77f8440ea5e021 Mon Sep 17 00:00:00 2001 From: Srikanth Chekuri Date: Fri, 3 Jun 2022 12:25:15 +0530 Subject: [PATCH 5/5] lint --- opentelemetry-sdk/tests/test_configurator.py | 1 + 1 file changed, 1 insertion(+) diff --git a/opentelemetry-sdk/tests/test_configurator.py b/opentelemetry-sdk/tests/test_configurator.py index 1770161873f..4b97a4d9015 100644 --- a/opentelemetry-sdk/tests/test_configurator.py +++ b/opentelemetry-sdk/tests/test_configurator.py @@ -12,6 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # type: ignore +# pylint: skip-file import logging from os import environ