Skip to content

Commit f0c9c79

Browse files
committed
Add and use missing metrics environment variables
Fixes #2967
1 parent 321f90f commit f0c9c79

File tree

5 files changed

+183
-4
lines changed

5 files changed

+183
-4
lines changed

Diff for: CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased](https://github.com/open-telemetry/opentelemetry-python/compare/v1.13.0...HEAD)
99

10+
- Add and use missing metrics environment variables
11+
([#2968](https://github.com/open-telemetry/opentelemetry-python/pull/2968))
1012
- Update explicit histogram bucket boundaries
1113
([#2947](https://github.com/open-telemetry/opentelemetry-python/pull/2947))
1214
- `exporter-otlp-proto-http`: add user agent string

Diff for: exporter/opentelemetry-exporter-otlp-proto-grpc/src/opentelemetry/exporter/otlp/proto/grpc/metric_exporter/__init__.py

+32-3
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
from opentelemetry.exporter.otlp.proto.grpc.exporter import (
2121
OTLPExporterMixin,
2222
get_resource_data,
23+
_get_credentials,
24+
environ_to_compression,
2325
)
2426
from opentelemetry.proto.collector.metrics.v1.metrics_service_pb2 import (
2527
ExportMetricsServiceRequest,
@@ -30,7 +32,12 @@
3032
from opentelemetry.proto.common.v1.common_pb2 import InstrumentationScope
3133
from opentelemetry.proto.metrics.v1 import metrics_pb2 as pb2
3234
from opentelemetry.sdk.environment_variables import (
35+
OTEL_EXPORTER_OTLP_METRICS_CERTIFICATE,
36+
OTEL_EXPORTER_OTLP_METRICS_COMPRESSION,
37+
OTEL_EXPORTER_OTLP_METRICS_ENDPOINT,
38+
OTEL_EXPORTER_OTLP_METRICS_HEADERS,
3339
OTEL_EXPORTER_OTLP_METRICS_INSECURE,
40+
OTEL_EXPORTER_OTLP_METRICS_TIMEOUT,
3441
OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE,
3542
)
3643
from opentelemetry.sdk.metrics import (
@@ -65,6 +72,7 @@ class OTLPMetricExporter(
6572
"""OTLP metric exporter
6673
6774
Args:
75+
endpoint: Target URL to which the exporter is going to send metrics
6876
max_export_batch_size: Maximum number of data points to export in a single request. This is to deal with
6977
gRPC's 4MB message size limit. If not set there is no limit to the number of data points in a request.
7078
If it is set and the number of data points exceeds the max, the request will be split.
@@ -91,6 +99,25 @@ def __init__(
9199
if insecure is not None:
92100
insecure = insecure.lower() == "true"
93101

102+
if (
103+
not insecure
104+
and environ.get(OTEL_EXPORTER_OTLP_METRICS_CERTIFICATE) is not None
105+
):
106+
credentials = _get_credentials(
107+
credentials, OTEL_EXPORTER_OTLP_METRICS_CERTIFICATE
108+
)
109+
110+
environ_timeout = environ.get(OTEL_EXPORTER_OTLP_METRICS_TIMEOUT)
111+
environ_timeout = (
112+
int(environ_timeout) if environ_timeout is not None else None
113+
)
114+
115+
compression = (
116+
environ_to_compression(OTEL_EXPORTER_OTLP_METRICS_COMPRESSION)
117+
if compression is None
118+
else compression
119+
)
120+
94121
instrument_class_temporality = {}
95122
if (
96123
environ.get(
@@ -125,13 +152,15 @@ def __init__(
125152
preferred_temporality=instrument_class_temporality,
126153
preferred_aggregation=preferred_aggregation,
127154
)
155+
128156
OTLPExporterMixin.__init__(
129157
self,
130-
endpoint=endpoint,
158+
endpoint=endpoint
159+
or environ.get(OTEL_EXPORTER_OTLP_METRICS_ENDPOINT),
131160
insecure=insecure,
132161
credentials=credentials,
133-
headers=headers,
134-
timeout=timeout,
162+
headers=headers or environ.get(OTEL_EXPORTER_OTLP_METRICS_HEADERS),
163+
timeout=timeout or environ_timeout,
135164
compression=compression,
136165
)
137166

Diff for: exporter/opentelemetry-exporter-otlp-proto-grpc/tests/metrics/__init__.py

Whitespace-only changes.

Diff for: exporter/opentelemetry-exporter-otlp-proto-grpc/tests/metrics/test_otlp_metrics_exporter.py renamed to exporter/opentelemetry-exporter-otlp-proto-grpc/tests/test_otlp_metrics_exporter.py

+105-1
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,14 @@
1414

1515
# pylint: disable=too-many-lines
1616
from concurrent.futures import ThreadPoolExecutor
17+
from os.path import dirname
1718
from typing import List
1819
from unittest import TestCase
1920
from unittest.mock import patch
2021

2122
from google.protobuf.duration_pb2 import Duration
2223
from google.rpc.error_details_pb2 import RetryInfo
23-
from grpc import StatusCode, server
24+
from grpc import ChannelCredentials, Compression, StatusCode, server
2425

2526
from opentelemetry.exporter.otlp.proto.grpc.metric_exporter import (
2627
OTLPMetricExporter,
@@ -43,8 +44,14 @@
4344
Resource as OTLPResource,
4445
)
4546
from opentelemetry.sdk.environment_variables import (
47+
OTEL_EXPORTER_OTLP_COMPRESSION,
48+
OTEL_EXPORTER_OTLP_METRICS_CERTIFICATE,
49+
OTEL_EXPORTER_OTLP_METRICS_COMPRESSION,
50+
OTEL_EXPORTER_OTLP_METRICS_ENDPOINT,
51+
OTEL_EXPORTER_OTLP_METRICS_HEADERS,
4652
OTEL_EXPORTER_OTLP_METRICS_INSECURE,
4753
OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE,
54+
OTEL_EXPORTER_OTLP_METRICS_TIMEOUT,
4855
)
4956
from opentelemetry.sdk.metrics import (
5057
Counter,
@@ -71,6 +78,8 @@
7178
)
7279
from opentelemetry.test.metrictestutil import _generate_gauge, _generate_sum
7380

81+
THIS_DIR = dirname(__file__)
82+
7483

7584
class MetricsServiceServicerUNAVAILABLEDelay(MetricsServiceServicer):
7685
# pylint: disable=invalid-name,unused-argument,no-self-use
@@ -119,6 +128,8 @@ def Export(self, request, context):
119128

120129

121130
class TestOTLPMetricExporter(TestCase):
131+
# pylint: disable=too-many-public-methods
132+
122133
def setUp(self):
123134

124135
self.exporter = OTLPMetricExporter()
@@ -348,6 +359,33 @@ def test_preferred_temporality(self):
348359
AggregationTemporality.CUMULATIVE,
349360
)
350361

362+
@patch.dict(
363+
"os.environ",
364+
{
365+
OTEL_EXPORTER_OTLP_METRICS_ENDPOINT: "collector:4317",
366+
OTEL_EXPORTER_OTLP_METRICS_CERTIFICATE: THIS_DIR
367+
+ "/fixtures/test.cert",
368+
OTEL_EXPORTER_OTLP_METRICS_HEADERS: " key1=value1,KEY2 = value=2",
369+
OTEL_EXPORTER_OTLP_METRICS_TIMEOUT: "10",
370+
OTEL_EXPORTER_OTLP_METRICS_COMPRESSION: "gzip",
371+
},
372+
)
373+
@patch(
374+
"opentelemetry.exporter.otlp.proto.grpc.exporter.OTLPExporterMixin.__init__"
375+
)
376+
def test_env_variables(self, mock_exporter_mixin):
377+
OTLPMetricExporter()
378+
379+
self.assertTrue(len(mock_exporter_mixin.call_args_list) == 1)
380+
_, kwargs = mock_exporter_mixin.call_args_list[0]
381+
382+
self.assertEqual(kwargs["endpoint"], "collector:4317")
383+
self.assertEqual(kwargs["headers"], " key1=value1,KEY2 = value=2")
384+
self.assertEqual(kwargs["timeout"], 10)
385+
self.assertEqual(kwargs["compression"], Compression.Gzip)
386+
self.assertIsNotNone(kwargs["credentials"])
387+
self.assertIsInstance(kwargs["credentials"], ChannelCredentials)
388+
351389
@patch(
352390
"opentelemetry.exporter.otlp.proto.grpc.exporter.ssl_channel_credentials"
353391
)
@@ -362,6 +400,36 @@ def test_no_credentials_error(
362400
OTLPMetricExporter(insecure=False)
363401
self.assertTrue(mock_ssl_channel.called)
364402

403+
@patch.dict(
404+
"os.environ",
405+
{OTEL_EXPORTER_OTLP_METRICS_HEADERS: " key1=value1,KEY2 = VALUE=2 "},
406+
)
407+
@patch(
408+
"opentelemetry.exporter.otlp.proto.grpc.exporter.ssl_channel_credentials"
409+
)
410+
@patch("opentelemetry.exporter.otlp.proto.grpc.exporter.secure_channel")
411+
# pylint: disable=unused-argument
412+
def test_otlp_headers_from_env(self, mock_ssl_channel, mock_secure):
413+
exporter = OTLPMetricExporter()
414+
# pylint: disable=protected-access
415+
self.assertEqual(
416+
exporter._headers, (("key1", "value1"), ("key2", "VALUE=2"))
417+
)
418+
exporter = OTLPMetricExporter(
419+
headers=(("key3", "value3"), ("key4", "value4"))
420+
)
421+
# pylint: disable=protected-access
422+
self.assertEqual(
423+
exporter._headers, (("key3", "value3"), ("key4", "value4"))
424+
)
425+
exporter = OTLPMetricExporter(
426+
headers={"key5": "value5", "key6": "value6"}
427+
)
428+
# pylint: disable=protected-access
429+
self.assertEqual(
430+
exporter._headers, (("key5", "value5"), ("key6", "value6"))
431+
)
432+
365433
@patch.dict(
366434
"os.environ",
367435
{OTEL_EXPORTER_OTLP_METRICS_INSECURE: "True"},
@@ -449,6 +517,42 @@ def test_otlp_exporter_endpoint(self, mock_secure, mock_insecure):
449517
)
450518
mock_method.reset_mock()
451519

520+
# pylint: disable=no-self-use
521+
@patch("opentelemetry.exporter.otlp.proto.grpc.exporter.insecure_channel")
522+
@patch.dict("os.environ", {OTEL_EXPORTER_OTLP_COMPRESSION: "gzip"})
523+
def test_otlp_exporter_otlp_compression_envvar(
524+
self, mock_insecure_channel
525+
):
526+
"""Just OTEL_EXPORTER_OTLP_COMPRESSION should work"""
527+
OTLPMetricExporter(insecure=True)
528+
mock_insecure_channel.assert_called_once_with(
529+
"localhost:4317", compression=Compression.Gzip
530+
)
531+
532+
# pylint: disable=no-self-use
533+
@patch("opentelemetry.exporter.otlp.proto.grpc.exporter.insecure_channel")
534+
@patch.dict("os.environ", {OTEL_EXPORTER_OTLP_COMPRESSION: "gzip"})
535+
def test_otlp_exporter_otlp_compression_kwarg(self, mock_insecure_channel):
536+
"""Specifying kwarg should take precedence over env"""
537+
OTLPMetricExporter(
538+
insecure=True, compression=Compression.NoCompression
539+
)
540+
mock_insecure_channel.assert_called_once_with(
541+
"localhost:4317", compression=Compression.NoCompression
542+
)
543+
544+
# pylint: disable=no-self-use
545+
@patch("opentelemetry.exporter.otlp.proto.grpc.exporter.insecure_channel")
546+
@patch.dict("os.environ", {})
547+
def test_otlp_exporter_otlp_compression_unspecified(
548+
self, mock_insecure_channel
549+
):
550+
"""No env or kwarg should be NoCompression"""
551+
OTLPMetricExporter(insecure=True)
552+
mock_insecure_channel.assert_called_once_with(
553+
"localhost:4317", compression=Compression.NoCompression
554+
)
555+
452556
@patch("opentelemetry.exporter.otlp.proto.grpc.exporter.expo")
453557
@patch("opentelemetry.exporter.otlp.proto.grpc.exporter.sleep")
454558
def test_unavailable(self, mock_sleep, mock_expo):

Diff for: opentelemetry-sdk/src/opentelemetry/sdk/environment_variables.py

+44
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,15 @@
335335
A scheme of https indicates a secure connection and takes precedence over this configuration setting.
336336
"""
337337

338+
OTEL_EXPORTER_OTLP_METRICS_ENDPOINT = "OTEL_EXPORTER_OTLP_METRICS_ENDPOINT"
339+
"""
340+
.. envvar:: OTEL_EXPORTER_OTLP_METRICS_ENDPOINT
341+
342+
The :envvar:`OTEL_EXPORTER_OTLP_METRICS_ENDPOINT` target to which the metrics exporter is going to send metrics.
343+
The endpoint MUST be a valid URL host, and MAY contain a scheme (http or https), port and path.
344+
A scheme of https indicates a secure connection and takes precedence over this configuration setting.
345+
"""
346+
338347
OTEL_EXPORTER_OTLP_TRACES_CERTIFICATE = "OTEL_EXPORTER_OTLP_TRACES_CERTIFICATE"
339348
"""
340349
.. envvar:: OTEL_EXPORTER_OTLP_TRACES_CERTIFICATE
@@ -343,6 +352,16 @@
343352
TLS credentials of gRPC client for traces. Should only be used for a secure connection for tracing.
344353
"""
345354

355+
OTEL_EXPORTER_OTLP_METRICS_CERTIFICATE = (
356+
"OTEL_EXPORTER_OTLP_METRICS_CERTIFICATE"
357+
)
358+
"""
359+
.. envvar:: OTEL_EXPORTER_OTLP_METRICS_CERTIFICATE
360+
361+
The :envvar:`OTEL_EXPORTER_OTLP_METRICS_CERTIFICATE` stores the path to the certificate file for
362+
TLS credentials of gRPC client for metrics. Should only be used for a secure connection for exporting metrics.
363+
"""
364+
346365
OTEL_EXPORTER_OTLP_TRACES_HEADERS = "OTEL_EXPORTER_OTLP_TRACES_HEADERS"
347366
"""
348367
.. envvar:: OTEL_EXPORTER_OTLP_TRACES_HEADERS
@@ -351,6 +370,13 @@
351370
associated with gRPC or HTTP requests.
352371
"""
353372

373+
OTEL_EXPORTER_OTLP_METRICS_HEADERS = "OTEL_EXPORTER_OTLP_METRICS_HEADERS"
374+
"""
375+
.. envvar:: OTEL_EXPORTER_OTLP_METRICS_HEADERS
376+
377+
The :envvar:`OTEL_EXPORTER_OTLP_METRICS_HEADERS` contains the key-value pairs to be used as headers for metrics
378+
associated with gRPC or HTTP requests.
379+
"""
354380

355381
OTEL_EXPORTER_OTLP_TRACES_COMPRESSION = "OTEL_EXPORTER_OTLP_TRACES_COMPRESSION"
356382
"""
@@ -360,6 +386,16 @@
360386
exporter. If both are present, this takes higher precedence.
361387
"""
362388

389+
OTEL_EXPORTER_OTLP_METRICS_COMPRESSION = (
390+
"OTEL_EXPORTER_OTLP_METRICS_COMPRESSION"
391+
)
392+
"""
393+
.. envvar:: OTEL_EXPORTER_OTLP_METRICS_COMPRESSION
394+
395+
Same as :envvar:`OTEL_EXPORTER_OTLP_COMPRESSION` but only for the metric
396+
exporter. If both are present, this takes higher precedence.
397+
"""
398+
363399
OTEL_EXPORTER_OTLP_TRACES_TIMEOUT = "OTEL_EXPORTER_OTLP_TRACES_TIMEOUT"
364400
"""
365401
.. envvar:: OTEL_EXPORTER_OTLP_TRACES_TIMEOUT
@@ -368,6 +404,14 @@
368404
wait for each batch export for spans.
369405
"""
370406

407+
OTEL_EXPORTER_OTLP_METRICS_TIMEOUT = "OTEL_EXPORTER_OTLP_METRICS_TIMEOUT"
408+
"""
409+
.. envvar:: OTEL_EXPORTER_OTLP_METRICS_TIMEOUT
410+
411+
The :envvar:`OTEL_EXPORTER_OTLP_METRICS_TIMEOUT` is the maximum time the OTLP exporter will
412+
wait for each batch export for metrics.
413+
"""
414+
371415
OTEL_EXPORTER_OTLP_METRICS_INSECURE = "OTEL_EXPORTER_OTLP_METRICS_INSECURE"
372416
"""
373417
.. envvar:: OTEL_EXPORTER_OTLP_METRICS_INSECURE

0 commit comments

Comments
 (0)