Skip to content

Commit 750f344

Browse files
authored
Merge branch 'main' into fix/count-logs-dropped-by-recursion-guard
2 parents 0bca60b + 5f851d2 commit 750f344

14 files changed

Lines changed: 172 additions & 83 deletions

File tree

.changelog/5668.fixed

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
`opentelemetry-exporter-otlp-proto-grpc`: Fix incorrect default port for OTLP gRPC exporter self-metrics

docs/getting_started/tests/requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ wrapt==1.15.0
2222
-e opentelemetry-semantic-conventions
2323
-e opentelemetry-proto
2424
-e exporter/opentelemetry-exporter-otlp-proto-common
25+
-e exporter/opentelemetry-exporter-otlp-common
2526
-e exporter/opentelemetry-exporter-otlp-proto-grpc
2627
-e opentelemetry-api
2728
-e opentelemetry-sdk

exporter/opentelemetry-exporter-otlp-proto-common/src/opentelemetry/exporter/otlp/proto/common/_exporter_metrics.py renamed to exporter/opentelemetry-exporter-otlp-common/src/opentelemetry/exporter/otlp/common/_exporter_metrics.py

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from dataclasses import dataclass
1010
from time import perf_counter
1111
from typing import TYPE_CHECKING, Protocol
12+
from urllib.parse import urlparse
1213

1314
from opentelemetry.metrics import MeterProvider, get_meter_provider
1415
from opentelemetry.semconv._incubating.attributes.otel_attributes import (
@@ -33,12 +34,19 @@
3334

3435
if TYPE_CHECKING:
3536
from typing import Literal
36-
from urllib.parse import ParseResult as UrlParseResult
3737

3838
from opentelemetry.util.types import AnyValue, Attributes
3939

4040
_component_counter = Counter()
4141

42+
_GRPC_COMPONENT_TYPES = frozenset(
43+
{
44+
OtelComponentTypeValues.OTLP_GRPC_SPAN_EXPORTER,
45+
OtelComponentTypeValues.OTLP_GRPC_LOG_EXPORTER,
46+
OtelComponentTypeValues.OTLP_GRPC_METRIC_EXPORTER,
47+
}
48+
)
49+
4250

4351
@dataclass
4452
class ExportResult:
@@ -52,6 +60,7 @@ def export_operation(self, num_items: int) -> AbstractContextManager[ExportResul
5260

5361
class NoOpExporterMetrics:
5462
@contextmanager
63+
# pylint: disable-next=no-self-use
5564
def export_operation(self, num_items: int) -> Iterator[ExportResult]:
5665
yield ExportResult()
5766

@@ -61,7 +70,7 @@ def __init__(
6170
self,
6271
component_type: OtelComponentTypeValues | None,
6372
signal: Literal["traces", "metrics", "logs"],
64-
endpoint: UrlParseResult,
73+
endpoint: str,
6574
meter_provider: MeterProvider | None,
6675
) -> None:
6776
if signal == "traces":
@@ -74,11 +83,15 @@ def __init__(
7483
create_exported = create_otel_sdk_exporter_metric_data_point_exported
7584
create_inflight = create_otel_sdk_exporter_metric_data_point_inflight
7685

77-
port = endpoint.port
86+
if not endpoint.startswith("//") and "://" not in endpoint:
87+
endpoint = f"//{endpoint}"
88+
parsed_endpoint = urlparse(endpoint)
89+
90+
port = parsed_endpoint.port
7891
if port is None:
79-
if endpoint.scheme == "https":
92+
if component_type in _GRPC_COMPONENT_TYPES or parsed_endpoint.scheme == "https":
8093
port = 443
81-
elif endpoint.scheme == "http":
94+
elif parsed_endpoint.scheme == "http":
8295
port = 80
8396

8497
component_type_value = component_type.value if component_type else "unknown_otlp_exporter"
@@ -88,8 +101,8 @@ def __init__(
88101
OTEL_COMPONENT_TYPE: component_type_value,
89102
OTEL_COMPONENT_NAME: f"{component_type_value}/{count}",
90103
}
91-
if endpoint.hostname:
92-
self._standard_attrs[SERVER_ADDRESS] = endpoint.hostname
104+
if parsed_endpoint.hostname:
105+
self._standard_attrs[SERVER_ADDRESS] = parsed_endpoint.hostname
93106
if port is not None:
94107
self._standard_attrs[SERVER_PORT] = port
95108

@@ -124,7 +137,7 @@ def export_operation(self, num_items: int) -> Iterator[ExportResult]:
124137
def create_exporter_metrics(
125138
component_type: OtelComponentTypeValues | None,
126139
signal: Literal["traces", "metrics", "logs"],
127-
endpoint: UrlParseResult,
140+
endpoint: str,
128141
meter_provider: MeterProvider | None,
129142
enabled: bool,
130143
) -> ExporterMetricsT:
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
# Copyright The OpenTelemetry Authors
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
from __future__ import annotations
5+
6+
import unittest
7+
from unittest.mock import Mock, patch
8+
9+
from opentelemetry.exporter.otlp.common._exporter_metrics import (
10+
ExporterMetrics,
11+
NoOpExporterMetrics,
12+
create_exporter_metrics,
13+
)
14+
from opentelemetry.semconv._incubating.attributes.otel_attributes import (
15+
OtelComponentTypeValues,
16+
)
17+
from opentelemetry.semconv.attributes.server_attributes import (
18+
SERVER_ADDRESS,
19+
SERVER_PORT,
20+
)
21+
22+
23+
class TestExporterMetrics(unittest.TestCase):
24+
def test_factory_returns_noop_when_disabled(self):
25+
meter_provider = Mock()
26+
27+
with patch("opentelemetry.exporter.otlp.common._exporter_metrics.get_meter_provider") as get_meter_provider:
28+
metrics = create_exporter_metrics(
29+
OtelComponentTypeValues.OTLP_HTTP_SPAN_EXPORTER,
30+
"traces",
31+
"http://localhost:4318/v1/traces",
32+
meter_provider,
33+
False,
34+
)
35+
36+
self.assertIsInstance(metrics, NoOpExporterMetrics)
37+
meter_provider.get_meter.assert_not_called()
38+
get_meter_provider.assert_not_called()
39+
40+
def test_factory_returns_exporter_metrics_when_enabled(self):
41+
meter_provider = Mock()
42+
meter_provider.get_meter.return_value = Mock()
43+
44+
metrics = create_exporter_metrics(
45+
OtelComponentTypeValues.OTLP_HTTP_SPAN_EXPORTER,
46+
"traces",
47+
"http://localhost:4318/v1/traces",
48+
meter_provider,
49+
True,
50+
)
51+
52+
self.assertIsInstance(metrics, ExporterMetrics)
53+
meter_provider.get_meter.assert_called_once_with("opentelemetry-sdk")
54+
55+
def test_noop_export_operation_yields_result(self):
56+
metrics = NoOpExporterMetrics()
57+
58+
with metrics.export_operation(1) as result:
59+
result.error = RuntimeError("error")
60+
61+
self.assertIsInstance(result.error, RuntimeError)
62+
63+
def test_grpc_uses_static_port(self):
64+
meter_provider = Mock()
65+
meter_provider.get_meter.return_value = Mock()
66+
67+
cases: list[tuple[OtelComponentTypeValues, str, str, int]] = [
68+
(
69+
OtelComponentTypeValues.OTLP_GRPC_SPAN_EXPORTER,
70+
"http://otlp.example.com",
71+
"otlp.example.com",
72+
443,
73+
),
74+
(
75+
OtelComponentTypeValues.OTLP_GRPC_LOG_EXPORTER,
76+
"https://otlp.example.com",
77+
"otlp.example.com",
78+
443,
79+
),
80+
(
81+
OtelComponentTypeValues.OTLP_GRPC_METRIC_EXPORTER,
82+
"otlp.example.com:4317",
83+
"otlp.example.com",
84+
4317,
85+
),
86+
(
87+
OtelComponentTypeValues.OTLP_GRPC_SPAN_EXPORTER,
88+
"http://localhost:4317",
89+
"localhost",
90+
4317,
91+
),
92+
]
93+
94+
for component_type, endpoint, expected_address, expected_port in cases:
95+
with self.subTest(component_type=component_type, endpoint=endpoint):
96+
metrics = ExporterMetrics(component_type, "traces", endpoint, meter_provider)
97+
# pylint: disable-next=protected-access
98+
self.assertEqual(metrics._standard_attrs[SERVER_ADDRESS], expected_address)
99+
# pylint: disable-next=protected-access
100+
self.assertEqual(metrics._standard_attrs[SERVER_PORT], expected_port)
101+
102+
def test_http_port_defaults_by_scheme(self):
103+
meter_provider = Mock()
104+
meter_provider.get_meter.return_value = Mock()
105+
106+
cases: list[tuple[str, int]] = [
107+
("http://otlp.example.com", 80),
108+
("https://otlp.example.com", 443),
109+
]
110+
111+
for endpoint, expected_port in cases:
112+
with self.subTest(endpoint=endpoint):
113+
metrics = ExporterMetrics(
114+
OtelComponentTypeValues.OTLP_HTTP_SPAN_EXPORTER,
115+
"traces",
116+
endpoint,
117+
meter_provider,
118+
)
119+
# pylint: disable-next=protected-access
120+
self.assertEqual(metrics._standard_attrs[SERVER_ADDRESS], "otlp.example.com")
121+
# pylint: disable-next=protected-access
122+
self.assertEqual(metrics._standard_attrs[SERVER_PORT], expected_port)

exporter/opentelemetry-exporter-otlp-proto-common/tests/test_exporter_metrics.py

Lines changed: 0 additions & 58 deletions
This file was deleted.

exporter/opentelemetry-exporter-otlp-proto-grpc/pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ dependencies = [
3434
"opentelemetry-proto == 1.45.0.dev",
3535
"opentelemetry-sdk ~= 1.45.0.dev",
3636
"opentelemetry-exporter-otlp-proto-common == 1.45.0.dev",
37+
"opentelemetry-exporter-otlp-common == 0.66b0.dev",
3738
"typing-extensions >= 4.6.0",
3839
]
3940

exporter/opentelemetry-exporter-otlp-proto-grpc/src/opentelemetry/exporter/otlp/proto/grpc/exporter.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@
4444
secure_channel,
4545
ssl_channel_credentials,
4646
)
47-
from opentelemetry.exporter.otlp.proto.common._exporter_metrics import (
47+
from opentelemetry.exporter.otlp.common._exporter_metrics import (
4848
create_exporter_metrics,
4949
)
5050
from opentelemetry.exporter.otlp.proto.common._internal import (
@@ -358,11 +358,10 @@ def __init__(
358358

359359
self._component_type = component_type
360360
self._signal: Literal["traces", "metrics", "logs"] = signal
361-
self._parsed_url = parsed_url
362361
self._metrics = create_exporter_metrics(
363362
self._component_type,
364363
signal,
365-
parsed_url,
364+
self._endpoint,
366365
meter_provider,
367366
os.environ.get(OTEL_PYTHON_SDK_INTERNAL_METRICS_ENABLED, "").strip().lower() == "true",
368367
)
@@ -518,7 +517,7 @@ def _set_meter_provider(self, meter_provider: MeterProvider) -> None:
518517
self._metrics = create_exporter_metrics(
519518
self._component_type,
520519
self._signal,
521-
self._parsed_url,
520+
self._endpoint,
522521
meter_provider,
523522
os.environ.get(OTEL_PYTHON_SDK_INTERNAL_METRICS_ENABLED, "").strip().lower() == "true",
524523
)

exporter/opentelemetry-exporter-otlp-proto-grpc/test-requirements.in

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ pytest>=9.0.3
77
-e opentelemetry-api
88
-e tests/opentelemetry-test-utils
99
-e exporter/opentelemetry-exporter-otlp-proto-common
10+
-e exporter/opentelemetry-exporter-otlp-common
1011
-e opentelemetry-proto
1112
-e opentelemetry-sdk
1213
-e opentelemetry-semantic-conventions

exporter/opentelemetry-exporter-otlp-proto-grpc/test-requirements.latest.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# This file was autogenerated by uv via the following command:
22
# uv pip compile --python-version=3.10 --universal --constraints=dev-requirements.txt exporter/opentelemetry-exporter-otlp-proto-grpc/test-requirements.in --output-file=exporter/opentelemetry-exporter-otlp-proto-grpc/test-requirements.latest.txt
3+
-e exporter/opentelemetry-exporter-otlp-common
4+
# via
5+
# -r exporter/opentelemetry-exporter-otlp-proto-grpc/test-requirements.in
6+
# opentelemetry-exporter-otlp-proto-grpc
37
-e exporter/opentelemetry-exporter-otlp-proto-common
48
# via
59
# -r exporter/opentelemetry-exporter-otlp-proto-grpc/test-requirements.in
@@ -21,6 +25,7 @@
2125
-e opentelemetry-sdk
2226
# via
2327
# -r exporter/opentelemetry-exporter-otlp-proto-grpc/test-requirements.in
28+
# opentelemetry-exporter-otlp-common
2429
# opentelemetry-exporter-otlp-proto-grpc
2530
# opentelemetry-test-utils
2631
-e opentelemetry-semantic-conventions

exporter/opentelemetry-exporter-otlp-proto-grpc/test-requirements.oldest.txt

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# This file was autogenerated by uv via the following command:
22
# uv pip compile --python-version=3.10 --universal --resolution=lowest --constraints=dev-requirements.txt exporter/opentelemetry-exporter-otlp-proto-grpc/test-requirements.in --output-file=exporter/opentelemetry-exporter-otlp-proto-grpc/test-requirements.oldest.txt
3+
-e exporter/opentelemetry-exporter-otlp-common
4+
# via
5+
# -r exporter/opentelemetry-exporter-otlp-proto-grpc/test-requirements.in
6+
# opentelemetry-exporter-otlp-proto-grpc
37
-e exporter/opentelemetry-exporter-otlp-proto-common
48
# via
59
# -r exporter/opentelemetry-exporter-otlp-proto-grpc/test-requirements.in
@@ -21,6 +25,7 @@
2125
-e opentelemetry-sdk
2226
# via
2327
# -r exporter/opentelemetry-exporter-otlp-proto-grpc/test-requirements.in
28+
# opentelemetry-exporter-otlp-common
2429
# opentelemetry-exporter-otlp-proto-grpc
2530
# opentelemetry-test-utils
2631
-e opentelemetry-semantic-conventions
@@ -33,7 +38,7 @@ asgiref==3.7.2
3338
# via
3439
# -c dev-requirements.txt
3540
# opentelemetry-test-utils
36-
certifi==2017.4.17
41+
certifi==2023.5.7
3742
# via requests
3843
charset-normalizer==2.0.0
3944
# via requests
@@ -76,7 +81,7 @@ pytest==9.0.3
7681
# via
7782
# -c dev-requirements.txt
7883
# -r exporter/opentelemetry-exporter-otlp-proto-grpc/test-requirements.in
79-
requests==2.32.3
84+
requests==2.33.0
8085
# via
8186
# -c dev-requirements.txt
8287
# opentelemetry-test-utils
@@ -96,5 +101,5 @@ typing-extensions==4.12.0 ; python_full_version >= '3.14'
96101
# opentelemetry-exporter-otlp-proto-grpc
97102
# opentelemetry-sdk
98103
# opentelemetry-semantic-conventions
99-
urllib3==1.21.1
104+
urllib3==1.26.0
100105
# via requests

0 commit comments

Comments
 (0)