Skip to content

Commit 05a2825

Browse files
Merge branch 'main' into docs/2786-otlp-exporter-api-docs
2 parents 3abc890 + 8c82502 commit 05a2825

37 files changed

Lines changed: 1995 additions & 250 deletions

File tree

.github/workflows/templates/test.yml.j2

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ env:
2020
contains(github.event.pull_request.labels.*.name, 'backport') && github.event.pull_request.base.ref ||
2121
'main'
2222
) || 'main' }}{% endraw %}
23+
WEAVER_VERSION: "v0.22.1"
2324
PIP_EXISTS_ACTION: w
2425
CONTRIB_REPO_UTIL_HTTP: ${% raw %}{{ github.workspace }}{% endraw %}/opentelemetry-python-contrib/util/opentelemetry-util-http
2526
CONTRIB_REPO_INSTRUMENTATION: ${% raw %}{{ github.workspace }}{% endraw %}/opentelemetry-python-contrib/opentelemetry-instrumentation
@@ -51,6 +52,14 @@ jobs:
5152
ref: ${% raw %}{{ env.CONTRIB_REPO_SHA }}{% endraw %}
5253
path: opentelemetry-python-contrib
5354
{%- endif %}
55+
{%- if "test-opentelemetry-test-utils" in job_data.tox_env and job_data.os != "windows-latest" %}
56+
57+
- name: Install weaver
58+
run: |
59+
WEAVER_URL="https://github.com/open-telemetry/weaver/releases/download/${% raw %}{{ env.WEAVER_VERSION }}{% endraw %}/weaver-x86_64-unknown-linux-gnu.tar.xz"
60+
curl -sSL "$WEAVER_URL" | tar -xJ -C /tmp weaver-x86_64-unknown-linux-gnu/weaver
61+
sudo mv /tmp/weaver-x86_64-unknown-linux-gnu/weaver /usr/local/bin/weaver
62+
{%- endif %}
5463

5564
- name: Set up Python {{ job_data.python_version }}
5665
uses: actions/setup-python@v5

.github/workflows/test.yml

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ env:
2020
contains(github.event.pull_request.labels.*.name, 'backport') && github.event.pull_request.base.ref ||
2121
'main'
2222
) || 'main' }}
23+
WEAVER_VERSION: "v0.22.1"
2324
PIP_EXISTS_ACTION: w
2425
CONTRIB_REPO_UTIL_HTTP: ${{ github.workspace }}/opentelemetry-python-contrib/util/opentelemetry-util-http
2526
CONTRIB_REPO_INSTRUMENTATION: ${{ github.workspace }}/opentelemetry-python-contrib/opentelemetry-instrumentation
@@ -2865,6 +2866,12 @@ jobs:
28652866
- name: Checkout repo @ SHA - ${{ github.sha }}
28662867
uses: actions/checkout@v4
28672868

2869+
- name: Install weaver
2870+
run: |
2871+
WEAVER_URL="https://github.com/open-telemetry/weaver/releases/download/${{ env.WEAVER_VERSION }}/weaver-x86_64-unknown-linux-gnu.tar.xz"
2872+
curl -sSL "$WEAVER_URL" | tar -xJ -C /tmp weaver-x86_64-unknown-linux-gnu/weaver
2873+
sudo mv /tmp/weaver-x86_64-unknown-linux-gnu/weaver /usr/local/bin/weaver
2874+
28682875
- name: Set up Python 3.10
28692876
uses: actions/setup-python@v5
28702877
with:
@@ -2884,6 +2891,12 @@ jobs:
28842891
- name: Checkout repo @ SHA - ${{ github.sha }}
28852892
uses: actions/checkout@v4
28862893

2894+
- name: Install weaver
2895+
run: |
2896+
WEAVER_URL="https://github.com/open-telemetry/weaver/releases/download/${{ env.WEAVER_VERSION }}/weaver-x86_64-unknown-linux-gnu.tar.xz"
2897+
curl -sSL "$WEAVER_URL" | tar -xJ -C /tmp weaver-x86_64-unknown-linux-gnu/weaver
2898+
sudo mv /tmp/weaver-x86_64-unknown-linux-gnu/weaver /usr/local/bin/weaver
2899+
28872900
- name: Set up Python 3.11
28882901
uses: actions/setup-python@v5
28892902
with:
@@ -2903,6 +2916,12 @@ jobs:
29032916
- name: Checkout repo @ SHA - ${{ github.sha }}
29042917
uses: actions/checkout@v4
29052918

2919+
- name: Install weaver
2920+
run: |
2921+
WEAVER_URL="https://github.com/open-telemetry/weaver/releases/download/${{ env.WEAVER_VERSION }}/weaver-x86_64-unknown-linux-gnu.tar.xz"
2922+
curl -sSL "$WEAVER_URL" | tar -xJ -C /tmp weaver-x86_64-unknown-linux-gnu/weaver
2923+
sudo mv /tmp/weaver-x86_64-unknown-linux-gnu/weaver /usr/local/bin/weaver
2924+
29062925
- name: Set up Python 3.12
29072926
uses: actions/setup-python@v5
29082927
with:
@@ -2922,6 +2941,12 @@ jobs:
29222941
- name: Checkout repo @ SHA - ${{ github.sha }}
29232942
uses: actions/checkout@v4
29242943

2944+
- name: Install weaver
2945+
run: |
2946+
WEAVER_URL="https://github.com/open-telemetry/weaver/releases/download/${{ env.WEAVER_VERSION }}/weaver-x86_64-unknown-linux-gnu.tar.xz"
2947+
curl -sSL "$WEAVER_URL" | tar -xJ -C /tmp weaver-x86_64-unknown-linux-gnu/weaver
2948+
sudo mv /tmp/weaver-x86_64-unknown-linux-gnu/weaver /usr/local/bin/weaver
2949+
29252950
- name: Set up Python 3.13
29262951
uses: actions/setup-python@v5
29272952
with:
@@ -2941,6 +2966,12 @@ jobs:
29412966
- name: Checkout repo @ SHA - ${{ github.sha }}
29422967
uses: actions/checkout@v4
29432968

2969+
- name: Install weaver
2970+
run: |
2971+
WEAVER_URL="https://github.com/open-telemetry/weaver/releases/download/${{ env.WEAVER_VERSION }}/weaver-x86_64-unknown-linux-gnu.tar.xz"
2972+
curl -sSL "$WEAVER_URL" | tar -xJ -C /tmp weaver-x86_64-unknown-linux-gnu/weaver
2973+
sudo mv /tmp/weaver-x86_64-unknown-linux-gnu/weaver /usr/local/bin/weaver
2974+
29442975
- name: Set up Python 3.14
29452976
uses: actions/setup-python@v5
29462977
with:
@@ -2960,6 +2991,12 @@ jobs:
29602991
- name: Checkout repo @ SHA - ${{ github.sha }}
29612992
uses: actions/checkout@v4
29622993

2994+
- name: Install weaver
2995+
run: |
2996+
WEAVER_URL="https://github.com/open-telemetry/weaver/releases/download/${{ env.WEAVER_VERSION }}/weaver-x86_64-unknown-linux-gnu.tar.xz"
2997+
curl -sSL "$WEAVER_URL" | tar -xJ -C /tmp weaver-x86_64-unknown-linux-gnu/weaver
2998+
sudo mv /tmp/weaver-x86_64-unknown-linux-gnu/weaver /usr/local/bin/weaver
2999+
29633000
- name: Set up Python 3.14t
29643001
uses: actions/setup-python@v5
29653002
with:
@@ -2979,6 +3016,12 @@ jobs:
29793016
- name: Checkout repo @ SHA - ${{ github.sha }}
29803017
uses: actions/checkout@v4
29813018

3019+
- name: Install weaver
3020+
run: |
3021+
WEAVER_URL="https://github.com/open-telemetry/weaver/releases/download/${{ env.WEAVER_VERSION }}/weaver-x86_64-unknown-linux-gnu.tar.xz"
3022+
curl -sSL "$WEAVER_URL" | tar -xJ -C /tmp weaver-x86_64-unknown-linux-gnu/weaver
3023+
sudo mv /tmp/weaver-x86_64-unknown-linux-gnu/weaver /usr/local/bin/weaver
3024+
29823025
- name: Set up Python pypy-3.10
29833026
uses: actions/setup-python@v5
29843027
with:

CHANGELOG.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1414

1515
- `opentelemetry-exporter-otlp-proto-grpc`, `opentelemetry-exporter-otlp-proto-http`: add `:members:` directives to submodule `automodule` entries in `docs/exporter/otlp/otlp.rst` so that exporter classes appear in the generated API documentation
1616
([#5124](https://github.com/open-telemetry/opentelemetry-python/pull/5124))
17+
- Fix incorrect code example in `create_tracer()` docstring
18+
([#5072](https://github.com/open-telemetry/opentelemetry-python/issues/5072))
1719
- `opentelemetry-sdk`: add `load_entry_point` shared utility to declarative file configuration for loading plugins via entry points; refactor propagator loading to use it
1820
([#5093](https://github.com/open-telemetry/opentelemetry-python/pull/5093))
1921
- `opentelemetry-sdk`: fix YAML structure injection via environment variable substitution in declarative file configuration; values containing newlines are now emitted as quoted YAML scalars per spec requirement
@@ -28,7 +30,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2830
([#5076](https://github.com/open-telemetry/opentelemetry-python/pull/5076))
2931
- `opentelemetry-semantic-conventions`: use `X | Y` union annotation
3032
([#5096](https://github.com/open-telemetry/opentelemetry-python/pull/5096))
31-
33+
- Add WeaverLiveCheck test util
34+
([#5088](https://github.com/open-telemetry/opentelemetry-python/pull/5088))
3235

3336
## Version 1.41.0/0.62b0 (2026-04-09)
3437

@@ -70,6 +73,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7073
([#5012](https://github.com/open-telemetry/opentelemetry-python/pull/5012))
7174
- `opentelemetry-sdk`: upgrade vendored OTel configuration schema from v1.0.0-rc.3 to v1.0.0
7275
([#4965](https://github.com/open-telemetry/opentelemetry-python/pull/4965))
76+
- `opentelemetry-sdk`: implement exporter metrics
77+
([#4976](https://github.com/open-telemetry/opentelemetry-python/pull/4976))
7378
- improve check-links ci job
7479
([#4978](https://github.com/open-telemetry/opentelemetry-python/pull/4978))
7580
- Resolve some Pyright type errors in Span/ReadableSpan and utility stubs
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
# Copyright The OpenTelemetry Authors
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
from __future__ import annotations
16+
17+
from collections import Counter
18+
from contextlib import contextmanager
19+
from dataclasses import dataclass
20+
from time import perf_counter
21+
from typing import TYPE_CHECKING, Iterator
22+
23+
from opentelemetry.metrics import MeterProvider, get_meter_provider
24+
from opentelemetry.semconv._incubating.attributes.otel_attributes import (
25+
OTEL_COMPONENT_NAME,
26+
OTEL_COMPONENT_TYPE,
27+
OtelComponentTypeValues,
28+
)
29+
from opentelemetry.semconv._incubating.metrics.otel_metrics import (
30+
create_otel_sdk_exporter_log_exported,
31+
create_otel_sdk_exporter_log_inflight,
32+
create_otel_sdk_exporter_metric_data_point_exported,
33+
create_otel_sdk_exporter_metric_data_point_inflight,
34+
create_otel_sdk_exporter_operation_duration,
35+
create_otel_sdk_exporter_span_exported,
36+
create_otel_sdk_exporter_span_inflight,
37+
)
38+
from opentelemetry.semconv.attributes.error_attributes import ERROR_TYPE
39+
from opentelemetry.semconv.attributes.server_attributes import (
40+
SERVER_ADDRESS,
41+
SERVER_PORT,
42+
)
43+
44+
if TYPE_CHECKING:
45+
from typing import Literal
46+
from urllib.parse import ParseResult as UrlParseResult
47+
48+
from opentelemetry.util.types import Attributes, AttributeValue
49+
50+
_component_counter = Counter()
51+
52+
53+
@dataclass
54+
class ExportResult:
55+
error: Exception | None = None
56+
error_attrs: Attributes = None
57+
58+
59+
class ExporterMetrics:
60+
def __init__(
61+
self,
62+
component_type: OtelComponentTypeValues | None,
63+
signal: Literal["traces", "metrics", "logs"],
64+
endpoint: UrlParseResult,
65+
meter_provider: MeterProvider | None,
66+
) -> None:
67+
if signal == "traces":
68+
create_exported = create_otel_sdk_exporter_span_exported
69+
create_inflight = create_otel_sdk_exporter_span_inflight
70+
elif signal == "logs":
71+
create_exported = create_otel_sdk_exporter_log_exported
72+
create_inflight = create_otel_sdk_exporter_log_inflight
73+
else:
74+
create_exported = (
75+
create_otel_sdk_exporter_metric_data_point_exported
76+
)
77+
create_inflight = (
78+
create_otel_sdk_exporter_metric_data_point_inflight
79+
)
80+
81+
port = endpoint.port
82+
if port is None:
83+
if endpoint.scheme == "https":
84+
port = 443
85+
elif endpoint.scheme == "http":
86+
port = 80
87+
88+
component_type = (
89+
component_type or OtelComponentTypeValues("unknown_otlp_exporter")
90+
).value
91+
count = _component_counter[component_type]
92+
_component_counter[component_type] = count + 1
93+
self._standard_attrs: dict[str, AttributeValue] = {
94+
OTEL_COMPONENT_TYPE: component_type,
95+
OTEL_COMPONENT_NAME: f"{component_type}/{count}",
96+
}
97+
if endpoint.hostname:
98+
self._standard_attrs[SERVER_ADDRESS] = endpoint.hostname
99+
if port is not None:
100+
self._standard_attrs[SERVER_PORT] = port
101+
102+
meter_provider = meter_provider or get_meter_provider()
103+
meter = meter_provider.get_meter("opentelemetry-sdk")
104+
self._inflight = create_inflight(meter)
105+
self._exported = create_exported(meter)
106+
self._duration = create_otel_sdk_exporter_operation_duration(meter)
107+
108+
@contextmanager
109+
def export_operation(self, num_items: int) -> Iterator[ExportResult]:
110+
start_time = perf_counter()
111+
self._inflight.add(num_items, self._standard_attrs)
112+
113+
result = ExportResult()
114+
try:
115+
yield result
116+
finally:
117+
error = result.error
118+
error_attrs = result.error_attrs
119+
120+
end_time = perf_counter()
121+
self._inflight.add(-num_items, self._standard_attrs)
122+
exported_attrs = (
123+
{**self._standard_attrs, ERROR_TYPE: type(error).__qualname__}
124+
if error
125+
else self._standard_attrs
126+
)
127+
self._exported.add(num_items, exported_attrs)
128+
duration_attrs = (
129+
{**exported_attrs, **error_attrs}
130+
if error_attrs
131+
else exported_attrs
132+
)
133+
self._duration.record(end_time - start_time, duration_attrs)

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

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
_get_credentials,
2323
environ_to_compression,
2424
)
25+
from opentelemetry.metrics import MeterProvider
2526
from opentelemetry.proto.collector.logs.v1.logs_service_pb2 import (
2627
ExportLogsServiceRequest,
2728
)
@@ -44,6 +45,9 @@
4445
OTEL_EXPORTER_OTLP_LOGS_INSECURE,
4546
OTEL_EXPORTER_OTLP_LOGS_TIMEOUT,
4647
)
48+
from opentelemetry.semconv._incubating.attributes.otel_attributes import (
49+
OtelComponentTypeValues,
50+
)
4751

4852

4953
class OTLPLogExporter(
@@ -66,6 +70,8 @@ def __init__(
6670
timeout: Optional[float] = None,
6771
compression: Optional[Compression] = None,
6872
channel_options: Optional[Tuple[Tuple[str, str]]] = None,
73+
*,
74+
meter_provider: Optional[MeterProvider] = None,
6975
):
7076
insecure_logs = environ.get(OTEL_EXPORTER_OTLP_LOGS_INSECURE)
7177
if insecure is None and insecure_logs is not None:
@@ -105,13 +111,19 @@ def __init__(
105111
stub=LogsServiceStub,
106112
result=LogRecordExportResult,
107113
channel_options=channel_options,
114+
component_type=OtelComponentTypeValues.OTLP_GRPC_LOG_EXPORTER,
115+
signal="logs",
116+
meter_provider=meter_provider,
108117
)
109118

110119
def _translate_data(
111120
self, data: Sequence[ReadableLogRecord]
112121
) -> ExportLogsServiceRequest:
113122
return encode_logs(data)
114123

124+
def _count_data(self, data: Sequence[ReadableLogRecord]):
125+
return len(data)
126+
115127
def export( # type: ignore [reportIncompatibleMethodOverride]
116128
self,
117129
batch: Sequence[ReadableLogRecord],

0 commit comments

Comments
 (0)