Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit b83c2ae

Browse files
authored
Configure auto instrumentation to support metrics (open-telemetry#2705)
* Configure auto instrumentation to support metrics * Add tests * Update test_configurator.py * Add CHANGELOG entry * lint
1 parent 928d333 commit b83c2ae

File tree

3 files changed

+170
-10
lines changed

3 files changed

+170
-10
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2323
([#2728](https://github.com/open-telemetry/opentelemetry-python/pull/2728))
2424
- fix: update entry point object references for metrics
2525
([#2731](https://github.com/open-telemetry/opentelemetry-python/pull/2731))
26+
- Configure auto instrumentation to support metrics
27+
([#2705](https://github.com/open-telemetry/opentelemetry-python/pull/2705))
2628

2729
## [1.12.0rc1-0.31b0](https://github.com/open-telemetry/opentelemetry-python/releases/tag/v1.12.0rc1-0.31b0) - 2022-05-17
2830

opentelemetry-sdk/src/opentelemetry/sdk/_configuration/__init__.py

Lines changed: 55 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,13 @@
2525

2626
from pkg_resources import iter_entry_points
2727

28-
from opentelemetry import trace
2928
from opentelemetry.environment_variables import (
3029
OTEL_LOGS_EXPORTER,
30+
OTEL_METRICS_EXPORTER,
3131
OTEL_PYTHON_ID_GENERATOR,
3232
OTEL_TRACES_EXPORTER,
3333
)
34+
from opentelemetry.metrics import set_meter_provider
3435
from opentelemetry.sdk._logs import (
3536
LogEmitterProvider,
3637
LoggingHandler,
@@ -40,11 +41,17 @@
4041
from opentelemetry.sdk.environment_variables import (
4142
_OTEL_PYTHON_LOGGING_AUTO_INSTRUMENTATION_ENABLED,
4243
)
44+
from opentelemetry.sdk.metrics import MeterProvider
45+
from opentelemetry.sdk.metrics.export import (
46+
MetricExporter,
47+
PeriodicExportingMetricReader,
48+
)
4349
from opentelemetry.sdk.resources import Resource
4450
from opentelemetry.sdk.trace import TracerProvider
4551
from opentelemetry.sdk.trace.export import BatchSpanProcessor, SpanExporter
4652
from opentelemetry.sdk.trace.id_generator import IdGenerator
4753
from opentelemetry.semconv.resource import ResourceAttributes
54+
from opentelemetry.trace import set_tracer_provider
4855

4956
_EXPORTER_OTLP = "otlp"
5057
_EXPORTER_OTLP_PROTO_GRPC = "otlp_proto_grpc"
@@ -87,7 +94,7 @@ def _init_tracing(
8794
id_generator=id_generator(),
8895
resource=Resource.create(auto_resource),
8996
)
90-
trace.set_tracer_provider(provider)
97+
set_tracer_provider(provider)
9198

9299
for _, exporter_class in exporters.items():
93100
exporter_args = {}
@@ -96,6 +103,33 @@ def _init_tracing(
96103
)
97104

98105

106+
def _init_metrics(
107+
exporters: Dict[str, Type[MetricExporter]],
108+
auto_instrumentation_version: Optional[str] = None,
109+
):
110+
# if env var OTEL_RESOURCE_ATTRIBUTES is given, it will read the service_name
111+
# from the env variable else defaults to "unknown_service"
112+
auto_resource = {}
113+
# populate version if using auto-instrumentation
114+
if auto_instrumentation_version:
115+
auto_resource[
116+
ResourceAttributes.TELEMETRY_AUTO_VERSION
117+
] = auto_instrumentation_version
118+
119+
metric_readers = []
120+
121+
for _, exporter_class in exporters.items():
122+
exporter_args = {}
123+
metric_readers.append(
124+
PeriodicExportingMetricReader(exporter_class(**exporter_args))
125+
)
126+
127+
provider = MeterProvider(
128+
resource=Resource.create(auto_resource), metric_readers=metric_readers
129+
)
130+
set_meter_provider(provider)
131+
132+
99133
def _init_logging(
100134
exporters: Dict[str, Type[LogExporter]],
101135
auto_instrumentation_version: Optional[str] = None,
@@ -145,9 +179,15 @@ def _import_config_components(
145179

146180
def _import_exporters(
147181
trace_exporter_names: Sequence[str],
182+
metric_exporter_names: Sequence[str],
148183
log_exporter_names: Sequence[str],
149-
) -> Tuple[Dict[str, Type[SpanExporter]], Dict[str, Type[LogExporter]]]:
184+
) -> Tuple[
185+
Dict[str, Type[SpanExporter]],
186+
Dict[str, Type[MetricExporter]],
187+
Dict[str, Type[LogExporter]],
188+
]:
150189
trace_exporters = {}
190+
metric_exporters = {}
151191
log_exporters = {}
152192

153193
for (exporter_name, exporter_impl,) in _import_config_components(
@@ -158,6 +198,14 @@ def _import_exporters(
158198
else:
159199
raise RuntimeError(f"{exporter_name} is not a trace exporter")
160200

201+
for (exporter_name, exporter_impl,) in _import_config_components(
202+
metric_exporter_names, "opentelemetry_metrics_exporter"
203+
):
204+
if issubclass(exporter_impl, MetricExporter):
205+
metric_exporters[exporter_name] = exporter_impl
206+
else:
207+
raise RuntimeError(f"{exporter_name} is not a metric exporter")
208+
161209
for (exporter_name, exporter_impl,) in _import_config_components(
162210
log_exporter_names, "opentelemetry_logs_exporter"
163211
):
@@ -166,7 +214,7 @@ def _import_exporters(
166214
else:
167215
raise RuntimeError(f"{exporter_name} is not a log exporter")
168216

169-
return trace_exporters, log_exporters
217+
return trace_exporters, metric_exporters, log_exporters
170218

171219

172220
def _import_id_generator(id_generator_name: str) -> IdGenerator:
@@ -182,13 +230,15 @@ def _import_id_generator(id_generator_name: str) -> IdGenerator:
182230

183231

184232
def _initialize_components(auto_instrumentation_version):
185-
trace_exporters, log_exporters = _import_exporters(
233+
trace_exporters, metric_exporters, log_exporters = _import_exporters(
186234
_get_exporter_names(environ.get(OTEL_TRACES_EXPORTER)),
235+
_get_exporter_names(environ.get(OTEL_METRICS_EXPORTER)),
187236
_get_exporter_names(environ.get(OTEL_LOGS_EXPORTER)),
188237
)
189238
id_generator_name = _get_id_generator()
190239
id_generator = _import_id_generator(id_generator_name)
191240
_init_tracing(trace_exporters, id_generator, auto_instrumentation_version)
241+
_init_metrics(metric_exporters, auto_instrumentation_version)
192242
logging_enabled = os.getenv(
193243
_OTEL_PYTHON_LOGGING_AUTO_INSTRUMENTATION_ENABLED, "false"
194244
)

opentelemetry-sdk/tests/test_configurator.py

Lines changed: 113 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,11 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414
# type: ignore
15+
# pylint: skip-file
1516

1617
import logging
1718
from os import environ
19+
from typing import Dict, Iterable, Optional
1820
from unittest import TestCase
1921
from unittest.mock import patch
2022

@@ -28,12 +30,21 @@
2830
_import_exporters,
2931
_import_id_generator,
3032
_init_logging,
33+
_init_metrics,
3134
_init_tracing,
3235
_initialize_components,
3336
)
3437
from opentelemetry.sdk._logs import LoggingHandler
3538
from opentelemetry.sdk._logs.export import ConsoleLogExporter
36-
from opentelemetry.sdk.metrics.export import ConsoleMetricExporter
39+
from opentelemetry.sdk.metrics import MeterProvider
40+
from opentelemetry.sdk.metrics.export import (
41+
AggregationTemporality,
42+
ConsoleMetricExporter,
43+
Metric,
44+
MetricExporter,
45+
MetricReader,
46+
)
47+
from opentelemetry.sdk.metrics.view import Aggregation
3748
from opentelemetry.sdk.resources import SERVICE_NAME, Resource
3849
from opentelemetry.sdk.trace.export import ConsoleSpanExporter
3950
from opentelemetry.sdk.trace.id_generator import IdGenerator, RandomIdGenerator
@@ -61,6 +72,10 @@ def get_log_emitter(self, name):
6172
return DummyLogEmitter(name, self.resource, self.processor)
6273

6374

75+
class DummyMeterProvider(MeterProvider):
76+
pass
77+
78+
6479
class DummyLogEmitter:
6580
def __init__(self, name, resource, processor):
6681
self.name = name
@@ -93,6 +108,44 @@ def __init__(self, exporter):
93108
self.exporter = exporter
94109

95110

111+
class DummyMetricReader(MetricReader):
112+
def __init__(
113+
self,
114+
exporter: MetricExporter,
115+
preferred_temporality: Dict[type, AggregationTemporality] = None,
116+
preferred_aggregation: Dict[type, Aggregation] = None,
117+
export_interval_millis: Optional[float] = None,
118+
export_timeout_millis: Optional[float] = None,
119+
) -> None:
120+
super().__init__(
121+
preferred_temporality=preferred_temporality,
122+
preferred_aggregation=preferred_aggregation,
123+
)
124+
self.exporter = exporter
125+
126+
def _receive_metrics(
127+
self,
128+
metrics: Iterable[Metric],
129+
timeout_millis: float = 10_000,
130+
**kwargs,
131+
) -> None:
132+
self.exporter.export(None)
133+
134+
def shutdown(self, timeout_millis: float = 30_000, **kwargs) -> None:
135+
return True
136+
137+
138+
class DummyOTLPMetricExporter:
139+
def __init__(self, *args, **kwargs):
140+
self.export_called = False
141+
142+
def export(self, batch):
143+
self.export_called = True
144+
145+
def shutdown(self):
146+
pass
147+
148+
96149
class Exporter:
97150
def __init__(self):
98151
tracer_provider = trace.get_tracer_provider()
@@ -148,7 +201,7 @@ def setUp(self):
148201
"opentelemetry.sdk._configuration.BatchSpanProcessor", Processor
149202
)
150203
self.set_provider_patcher = patch(
151-
"opentelemetry.trace.set_tracer_provider"
204+
"opentelemetry.sdk._configuration.set_tracer_provider"
152205
)
153206

154207
self.get_provider_mock = self.get_provider_patcher.start()
@@ -304,6 +357,61 @@ def test_logging_init_enable_env(self, logging_mock, tracing_mock):
304357
self.assertEqual(tracing_mock.call_count, 1)
305358

306359

360+
class TestMetricsInit(TestCase):
361+
def setUp(self):
362+
self.metric_reader_patch = patch(
363+
"opentelemetry.sdk._configuration.PeriodicExportingMetricReader",
364+
DummyMetricReader,
365+
)
366+
self.provider_patch = patch(
367+
"opentelemetry.sdk._configuration.MeterProvider",
368+
DummyMeterProvider,
369+
)
370+
self.set_provider_patch = patch(
371+
"opentelemetry.sdk._configuration.set_meter_provider"
372+
)
373+
374+
self.metric_reader_mock = self.metric_reader_patch.start()
375+
self.provider_mock = self.provider_patch.start()
376+
self.set_provider_mock = self.set_provider_patch.start()
377+
378+
def tearDown(self):
379+
self.metric_reader_patch.stop()
380+
self.set_provider_patch.stop()
381+
self.provider_patch.stop()
382+
383+
def test_metrics_init_empty(self):
384+
_init_metrics({}, "auto-version")
385+
self.assertEqual(self.set_provider_mock.call_count, 1)
386+
provider = self.set_provider_mock.call_args[0][0]
387+
self.assertIsInstance(provider, DummyMeterProvider)
388+
self.assertIsInstance(provider._sdk_config.resource, Resource)
389+
self.assertEqual(
390+
provider._sdk_config.resource.attributes.get(
391+
"telemetry.auto.version"
392+
),
393+
"auto-version",
394+
)
395+
396+
@patch.dict(
397+
environ,
398+
{"OTEL_RESOURCE_ATTRIBUTES": "service.name=otlp-service"},
399+
)
400+
def test_metrics_init_exporter(self):
401+
_init_metrics({"otlp": DummyOTLPMetricExporter})
402+
self.assertEqual(self.set_provider_mock.call_count, 1)
403+
provider = self.set_provider_mock.call_args[0][0]
404+
self.assertIsInstance(provider, DummyMeterProvider)
405+
self.assertIsInstance(provider._sdk_config.resource, Resource)
406+
self.assertEqual(
407+
provider._sdk_config.resource.attributes.get("service.name"),
408+
"otlp-service",
409+
)
410+
reader = provider._sdk_config.metric_readers[0]
411+
self.assertIsInstance(reader, DummyMetricReader)
412+
self.assertIsInstance(reader.exporter, DummyOTLPMetricExporter)
413+
414+
307415
class TestExporterNames(TestCase):
308416
def test_otlp_exporter_overwrite(self):
309417
for exporter in [_EXPORTER_OTLP, _EXPORTER_OTLP_PROTO_GRPC]:
@@ -328,8 +436,8 @@ def test_empty_exporters(self):
328436

329437
class TestImportExporters(TestCase):
330438
def test_console_exporters(self):
331-
trace_exporters, logs_exporters = _import_exporters(
332-
["console"], ["console"]
439+
trace_exporters, metric_exporterts, logs_exporters = _import_exporters(
440+
["console"], ["console"], ["console"]
333441
)
334442
self.assertEqual(
335443
trace_exporters["console"].__class__, ConsoleSpanExporter.__class__
@@ -338,6 +446,6 @@ def test_console_exporters(self):
338446
logs_exporters["console"].__class__, ConsoleLogExporter.__class__
339447
)
340448
self.assertEqual(
341-
logs_exporters["console"].__class__,
449+
metric_exporterts["console"].__class__,
342450
ConsoleMetricExporter.__class__,
343451
)

0 commit comments

Comments
 (0)