Script 'mail_helper' called by obssrc
Hello community,
here is the log from the commit of package
python-opentelemetry-exporter-otlp-proto-grpc for openSUSE:Factory checked in
at 2026-06-27 18:04:41
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing
/work/SRC/openSUSE:Factory/python-opentelemetry-exporter-otlp-proto-grpc (Old)
and
/work/SRC/openSUSE:Factory/.python-opentelemetry-exporter-otlp-proto-grpc.new.11887
(New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-opentelemetry-exporter-otlp-proto-grpc"
Sat Jun 27 18:04:41 2026 rev:15 rq:1361871 version:1.42.1
Changes:
--------
---
/work/SRC/openSUSE:Factory/python-opentelemetry-exporter-otlp-proto-grpc/python-opentelemetry-exporter-otlp-proto-grpc.changes
2026-04-28 11:57:03.967240125 +0200
+++
/work/SRC/openSUSE:Factory/.python-opentelemetry-exporter-otlp-proto-grpc.new.11887/python-opentelemetry-exporter-otlp-proto-grpc.changes
2026-06-27 18:05:48.527388355 +0200
@@ -1,0 +2,119 @@
+Sat Jun 20 16:00:16 UTC 2026 - Dirk Müller <[email protected]>
+
+- update to 1.42.1:
+ * This is a patch release on the previous 1.42.0/0.63b0
+ release, fixing the issue(s) below.
+ * ### Fixed
+ * Preserve the random trace ID flag when creating child spans
+ instead of always setting the random trace id bit depending
+ on the available trace id generator.
+- update to 1.42.0:
+ * ### Added
+ * `opentelemetry-api`, `opentelemetry-sdk`: add support for
+ 'random-trace-id' flags in W3C traceparent header trace
+ flags. Implementations of `IdGenerator` that do randomly
+ generate the 56 least significant bits, should also implement
+ a `is_trace_id_random` methods that returns `True`.
+ * logs: add exception support to Logger emit and LogRecord
+ attributes
+ * `opentelemetry-exporter-otlp-proto-grpc`: make retryable gRPC
+ error codes configurable for gRPC exporters
+ * `opentelemetry-sdk`: Add
+ `create_logger_provider`/`configure_logger_provider` to
+ declarative file configuration, enabling LoggerProvider
+ instantiation from config files without reading env vars
+ * `opentelemetry-exporter-otlp-json-common`: add
+ 'opentelemetry-exporter-otlp-json-common' package for OTLP
+ JSON exporters
+ * `opentelemetry-sdk`: Add `service` resource detector support
+ to declarative file configuration via
+ `detection_development.detectors[].service`
+ * `opentelemetry-docker-tests`: add docker-tests coverage of
+ `opentelemetry-exporter-otlp-proto-grpc` and `opentelemetry-
+ exporter-otlp-proto-http` metrics export
+ * Add `registry` keyword argument to `PrometheusMetricReader`
+ to allow passing a custom Prometheus registry
+ * Add WeaverLiveCheck test util
+ * `opentelemetry-sdk`: add `load_entry_point` shared utility to
+ declarative file configuration for loading plugins via entry
+ points; refactor propagator loading to use it
+ * `opentelemetry-sdk`: add sampler plugin loading to
+ declarative file configuration via the
+ `opentelemetry_sampler` entry point group, matching the
+ spec's PluginComponentProvider mechanism
+ * `opentelemetry-sdk`: add propagator plugin loading to
+ declarative file configuration via the
+ `opentelemetry_propagator` entry point group, matching the
+ spec's PluginComponentProvider mechanism
+ * `opentelemetry-sdk`: add exporter plugin loading to
+ declarative file configuration for all three signals (traces,
+ metrics, logs) via the `opentelemetry_*_exporter` entry point
+ groups, matching the spec's PluginComponentProvider mechanism
+ * `opentelemetry-sdk`: add generic resource detector plugin
+ loading to declarative file configuration via the
+ `opentelemetry_resource_detector` entry point group, matching
+ the spec's PluginComponentProvider mechanism
+ * `opentelemetry-sdk`: add `additional_properties` support to
+ generated config models via custom `datamodel-codegen`
+ template, enabling plugin/custom component names to flow
+ through typed dataclasses
+ * Add ability to selectively enable exporting of SDK internal
+ metrics with the `OTEL_PYTHON_SDK_INTERNAL_METRICS_ENABLED`
+ environment variable.
+ * ### Changed
+ * `opentelemetry-semantic-conventions`: use `X | Y` union
+ annotation
+ * `opentelemetry-api`: update `EnvironmentGetter` and
+ `EnvironmentSetter` to use normalized environment variable
+ names
+ * Apply fixes for `UP` ruff rule
+ * `opentelemetry-sdk`: only load entrypoints for resource
+ detectors if they are configured via
+ `OTEL_EXPERIMENTAL_RESOURCE_DETECTORS`
+ * ci: wait for tracecontext server readiness instead of a fixed
+ sleep in `scripts/tracecontext-integration-test.sh`
+ * Switch to SPDX license headers and add CI enforcement
+ * `opentelemetry-semantic-conventions`: Bump semantic
+ conventions to 1.41.1, this changes the metrics name of
+ `K8S_CONTAINER_CPU_LIMIT_UTILIZATION` and
+ `K8S_CONTAINER_CPU_REQUEST_UTILIZATION`.
+ * ### Removed
+ * `opentelemetry-api`: remove third-party importlib-metadata in
+ favor of standard library since Python >= 3.10 is now
+ required
+ * Drop Python 3.9 support
+ * ### Fixed
+ * `opentelemetry-sdk`: Allow declarative OTLP HTTP exporters to
+ map `compression: deflate` instead of rejecting it as
+ unsupported.
+ * Fix incorrect code example in `create_tracer()` docstring
+ * `opentelemetry-sdk`: Fix `ProcessResourceDetector` to use
+ `sys.orig_argv` so that `process.command`,
+ `process.command_line`, and `process.command_args` reflect
+ the original invocation for `python -m ` runs (where
+ `sys.argv[0]` is rewritten to the module path)
+ * `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
+ * `opentelemetry-sdk`: Fix mutable attributes reference in
+ metrics, attributes passed to instrument `add`/`record` are
+ now copied so that subsequent mutations do not affect
+ recorded data points
+ * `opentelemetry-sdk`: make resource detector ordering
+ deterministic
+ * Fix incorrect type annotation on `detectors` parameter of
+ `get_aggregated_resources`
+ * `opentelemetry-api`: Enforce W3C Baggage size limits on
+ outbound propagation in `W3CBaggagePropagator.inject()`.
+ Previously only inbound extraction enforced limits; now
+ inject also caps entries at 180, individual pairs at 4096
+ bytes, and total header at 8192 bytes per the W3C Baggage
+ spec. The extract path max_pairs limit now counts all size-
+ valid entries rather than only successfully parsed ones.
+ * `opentelemetry-sdk`: fix multi-processor `force_flush`
+ skipping remaining processors when one returns `None`
+ * `opentelemetry-test-utils`: fix weaver live check hanging
+ when weaver log output fills the pipe buffer
+
+-------------------------------------------------------------------
Old:
----
opentelemetry_exporter_otlp_proto_grpc-1.41.1.tar.gz
New:
----
opentelemetry_exporter_otlp_proto_grpc-1.42.1.tar.gz
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ python-opentelemetry-exporter-otlp-proto-grpc.spec ++++++
--- /var/tmp/diff_new_pack.BwN9cc/_old 2026-06-27 18:05:49.399417588 +0200
+++ /var/tmp/diff_new_pack.BwN9cc/_new 2026-06-27 18:05:49.403417722 +0200
@@ -18,7 +18,7 @@
%{?sle15_python_module_pythons}
Name: python-opentelemetry-exporter-otlp-proto-grpc
-Version: 1.41.1
+Version: 1.42.1
Release: 0
Summary: OpenTelemetry Collector Protobuf over gRPC Exporter
License: Apache-2.0
++++++ opentelemetry_exporter_otlp_proto_grpc-1.41.1.tar.gz ->
opentelemetry_exporter_otlp_proto_grpc-1.42.1.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/opentelemetry_exporter_otlp_proto_grpc-1.41.1/PKG-INFO
new/opentelemetry_exporter_otlp_proto_grpc-1.42.1/PKG-INFO
--- old/opentelemetry_exporter_otlp_proto_grpc-1.41.1/PKG-INFO 2020-02-02
01:00:00.000000000 +0100
+++ new/opentelemetry_exporter_otlp_proto_grpc-1.42.1/PKG-INFO 2020-02-02
01:00:00.000000000 +0100
@@ -1,6 +1,6 @@
Metadata-Version: 2.4
Name: opentelemetry-exporter-otlp-proto-grpc
-Version: 1.41.1
+Version: 1.42.1
Summary: OpenTelemetry Collector Protobuf over gRPC Exporter
Project-URL: Homepage,
https://github.com/open-telemetry/opentelemetry-python/tree/main/exporter/opentelemetry-exporter-otlp-proto-grpc
Project-URL: Repository, https://github.com/open-telemetry/opentelemetry-python
@@ -13,21 +13,20 @@
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3
-Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
-Requires-Python: >=3.9
+Requires-Python: >=3.10
Requires-Dist: googleapis-common-protos~=1.57
Requires-Dist: grpcio<2.0.0,>=1.63.2; python_version < '3.13'
Requires-Dist: grpcio<2.0.0,>=1.66.2; python_version == '3.13'
Requires-Dist: grpcio<2.0.0,>=1.75.1; python_version >= '3.14'
Requires-Dist: opentelemetry-api~=1.15
-Requires-Dist: opentelemetry-exporter-otlp-proto-common==1.41.1
-Requires-Dist: opentelemetry-proto==1.41.1
-Requires-Dist: opentelemetry-sdk~=1.41.1
+Requires-Dist: opentelemetry-exporter-otlp-proto-common==1.42.1
+Requires-Dist: opentelemetry-proto==1.42.1
+Requires-Dist: opentelemetry-sdk~=1.42.1
Requires-Dist: typing-extensions>=4.6.0
Provides-Extra: gcp-auth
Requires-Dist: opentelemetry-exporter-credential-provider-gcp>=0.59b0; extra
== 'gcp-auth'
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/opentelemetry_exporter_otlp_proto_grpc-1.41.1/pyproject.toml
new/opentelemetry_exporter_otlp_proto_grpc-1.42.1/pyproject.toml
--- old/opentelemetry_exporter_otlp_proto_grpc-1.41.1/pyproject.toml
2020-02-02 01:00:00.000000000 +0100
+++ new/opentelemetry_exporter_otlp_proto_grpc-1.42.1/pyproject.toml
2020-02-02 01:00:00.000000000 +0100
@@ -8,7 +8,7 @@
description = "OpenTelemetry Collector Protobuf over gRPC Exporter"
readme = "README.rst"
license = "Apache-2.0"
-requires-python = ">=3.9"
+requires-python = ">=3.10"
authors = [
{ name = "OpenTelemetry Authors", email =
"[email protected]" },
]
@@ -19,7 +19,6 @@
"Intended Audience :: Developers",
"Programming Language :: Python",
"Programming Language :: Python :: 3",
- "Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
@@ -32,9 +31,9 @@
"grpcio >= 1.66.2, < 2.0.0; python_version == '3.13'",
"grpcio >= 1.75.1, < 2.0.0; python_version >= '3.14'",
"opentelemetry-api ~= 1.15",
- "opentelemetry-proto == 1.41.1",
- "opentelemetry-sdk ~= 1.41.1",
- "opentelemetry-exporter-otlp-proto-common == 1.41.1",
+ "opentelemetry-proto == 1.42.1",
+ "opentelemetry-sdk ~= 1.42.1",
+ "opentelemetry-exporter-otlp-proto-common == 1.42.1",
"typing-extensions >= 4.6.0",
]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/opentelemetry_exporter_otlp_proto_grpc-1.41.1/src/opentelemetry/exporter/otlp/proto/grpc/__init__.py
new/opentelemetry_exporter_otlp_proto_grpc-1.42.1/src/opentelemetry/exporter/otlp/proto/grpc/__init__.py
---
old/opentelemetry_exporter_otlp_proto_grpc-1.41.1/src/opentelemetry/exporter/otlp/proto/grpc/__init__.py
2020-02-02 01:00:00.000000000 +0100
+++
new/opentelemetry_exporter_otlp_proto_grpc-1.42.1/src/opentelemetry/exporter/otlp/proto/grpc/__init__.py
2020-02-02 01:00:00.000000000 +0100
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
+# SPDX-License-Identifier: Apache-2.0
"""
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/opentelemetry_exporter_otlp_proto_grpc-1.41.1/src/opentelemetry/exporter/otlp/proto/grpc/_log_exporter/__init__.py
new/opentelemetry_exporter_otlp_proto_grpc-1.42.1/src/opentelemetry/exporter/otlp/proto/grpc/_log_exporter/__init__.py
---
old/opentelemetry_exporter_otlp_proto_grpc-1.41.1/src/opentelemetry/exporter/otlp/proto/grpc/_log_exporter/__init__.py
2020-02-02 01:00:00.000000000 +0100
+++
new/opentelemetry_exporter_otlp_proto_grpc-1.42.1/src/opentelemetry/exporter/otlp/proto/grpc/_log_exporter/__init__.py
2020-02-02 01:00:00.000000000 +0100
@@ -1,27 +1,19 @@
# Copyright The OpenTelemetry Authors
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
+# SPDX-License-Identifier: Apache-2.0
+from collections.abc import Iterable, Sequence
+from collections.abc import Sequence as TypingSequence
from os import environ
-from typing import Dict, Literal, Optional, Sequence, Tuple, Union
-from typing import Sequence as TypingSequence
+from typing import Literal
-from grpc import ChannelCredentials, Compression
+from grpc import ChannelCredentials, Compression, StatusCode
from opentelemetry.exporter.otlp.proto.common._log_encoder import encode_logs
from opentelemetry.exporter.otlp.proto.grpc.exporter import (
OTLPExporterMixin,
_get_credentials,
environ_to_compression,
)
+from opentelemetry.metrics import MeterProvider
from opentelemetry.proto.collector.logs.v1.logs_service_pb2 import (
ExportLogsServiceRequest,
)
@@ -44,6 +36,9 @@
OTEL_EXPORTER_OTLP_LOGS_INSECURE,
OTEL_EXPORTER_OTLP_LOGS_TIMEOUT,
)
+from opentelemetry.semconv._incubating.attributes.otel_attributes import (
+ OtelComponentTypeValues,
+)
class OTLPLogExporter(
@@ -57,15 +52,19 @@
):
def __init__(
self,
- endpoint: Optional[str] = None,
- insecure: Optional[bool] = None,
- credentials: Optional[ChannelCredentials] = None,
- headers: Optional[
- Union[TypingSequence[Tuple[str, str]], Dict[str, str], str]
- ] = None,
- timeout: Optional[float] = None,
- compression: Optional[Compression] = None,
- channel_options: Optional[Tuple[Tuple[str, str]]] = None,
+ endpoint: str | None = None,
+ insecure: bool | None = None,
+ credentials: ChannelCredentials | None = None,
+ headers: TypingSequence[tuple[str, str]]
+ | dict[str, str]
+ | str
+ | None = None,
+ timeout: float | None = None,
+ compression: Compression | None = None,
+ channel_options: tuple[tuple[str, str]] | None = None,
+ retryable_error_codes: Iterable[StatusCode] | None = None,
+ *,
+ meter_provider: MeterProvider | None = None,
):
insecure_logs = environ.get(OTEL_EXPORTER_OTLP_LOGS_INSECURE)
if insecure is None and insecure_logs is not None:
@@ -105,6 +104,10 @@
stub=LogsServiceStub,
result=LogRecordExportResult,
channel_options=channel_options,
+ retryable_error_codes=retryable_error_codes,
+ component_type=OtelComponentTypeValues.OTLP_GRPC_LOG_EXPORTER,
+ signal="logs",
+ meter_provider=meter_provider,
)
def _translate_data(
@@ -112,6 +115,9 @@
) -> ExportLogsServiceRequest:
return encode_logs(data)
+ def _count_data(self, data: Sequence[ReadableLogRecord]):
+ return len(data)
+
def export( # type: ignore [reportIncompatibleMethodOverride]
self,
batch: Sequence[ReadableLogRecord],
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/opentelemetry_exporter_otlp_proto_grpc-1.41.1/src/opentelemetry/exporter/otlp/proto/grpc/exporter.py
new/opentelemetry_exporter_otlp_proto_grpc-1.42.1/src/opentelemetry/exporter/otlp/proto/grpc/exporter.py
---
old/opentelemetry_exporter_otlp_proto_grpc-1.41.1/src/opentelemetry/exporter/otlp/proto/grpc/exporter.py
2020-02-02 01:00:00.000000000 +0100
+++
new/opentelemetry_exporter_otlp_proto_grpc-1.42.1/src/opentelemetry/exporter/otlp/proto/grpc/exporter.py
2020-02-02 01:00:00.000000000 +0100
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
+# SPDX-License-Identifier: Apache-2.0
"""OTLP Exporter
@@ -20,28 +9,28 @@
"""
+import os
import random
import threading
from abc import ABC, abstractmethod
-from collections.abc import Sequence # noqa: F401
+from collections.abc import (
+ Callable,
+ Iterable,
+ Sequence, # noqa: F401
+)
+from collections.abc import Sequence as TypingSequence
from logging import getLogger
from os import environ
from time import time
from typing import ( # noqa: F401
Any,
- Callable,
- Dict,
Generic,
- List,
Literal,
NewType,
Optional,
- Tuple,
- Type,
TypeVar,
Union,
)
-from typing import Sequence as TypingSequence
from urllib.parse import urlparse
from google.rpc.error_details_pb2 import RetryInfo
@@ -56,12 +45,16 @@
secure_channel,
ssl_channel_credentials,
)
+from opentelemetry.exporter.otlp.proto.common._exporter_metrics import (
+ create_exporter_metrics,
+)
from opentelemetry.exporter.otlp.proto.common._internal import (
_get_resource_data,
)
from opentelemetry.exporter.otlp.proto.grpc import (
_OTLP_GRPC_CHANNEL_OPTIONS,
)
+from opentelemetry.metrics import MeterProvider
from opentelemetry.proto.collector.logs.v1.logs_service_pb2 import (
ExportLogsServiceRequest,
)
@@ -91,6 +84,7 @@
from opentelemetry.sdk._shared_internal import DuplicateFilter
from opentelemetry.sdk.environment_variables import (
_OTEL_PYTHON_EXPORTER_OTLP_GRPC_CREDENTIAL_PROVIDER,
+ _OTEL_PYTHON_EXPORTER_OTLP_GRPC_RETRYABLE_ERROR_CODES,
OTEL_EXPORTER_OTLP_CERTIFICATE,
OTEL_EXPORTER_OTLP_CLIENT_CERTIFICATE,
OTEL_EXPORTER_OTLP_CLIENT_KEY,
@@ -99,11 +93,18 @@
OTEL_EXPORTER_OTLP_HEADERS,
OTEL_EXPORTER_OTLP_INSECURE,
OTEL_EXPORTER_OTLP_TIMEOUT,
+ OTEL_PYTHON_SDK_INTERNAL_METRICS_ENABLED,
)
from opentelemetry.sdk.metrics.export import MetricExportResult, MetricsData
from opentelemetry.sdk.resources import Resource as SDKResource
from opentelemetry.sdk.trace import ReadableSpan
from opentelemetry.sdk.trace.export import SpanExportResult
+from opentelemetry.semconv._incubating.attributes.otel_attributes import (
+ OtelComponentTypeValues,
+)
+from opentelemetry.semconv._incubating.attributes.rpc_attributes import (
+ RPC_RESPONSE_STATUS_CODE,
+)
from opentelemetry.util._importlib_metadata import entry_points
from opentelemetry.util.re import parse_env_headers
@@ -159,7 +160,7 @@
)
-def environ_to_compression(environ_key: str) -> Optional[Compression]:
+def environ_to_compression(environ_key: str) -> Compression | None:
environ_value = (
environ[environ_key].lower().strip()
if environ_key in environ
@@ -177,14 +178,14 @@
"Use one of the encoders from opentelemetry-exporter-otlp-proto-common
instead. Deprecated since version 1.18.0.",
)
def get_resource_data(
- sdk_resource_scope_data: Dict[SDKResource, ResourceDataT],
+ sdk_resource_scope_data: dict[SDKResource, ResourceDataT],
resource_class: Callable[..., TypingResourceT],
name: str,
-) -> List[TypingResourceT]:
+) -> list[TypingResourceT]:
return _get_resource_data(sdk_resource_scope_data, resource_class, name)
-def _read_file(file_path: str) -> Optional[bytes]:
+def _read_file(file_path: str) -> bytes | None:
try:
with open(file_path, "rb") as file:
return file.read()
@@ -197,9 +198,9 @@
def _load_credentials(
- certificate_file: Optional[str],
- client_key_file: Optional[str],
- client_certificate_file: Optional[str],
+ certificate_file: str | None,
+ client_key_file: str | None,
+ client_certificate_file: str | None,
) -> ChannelCredentials:
root_certificates = (
_read_file(certificate_file) if certificate_file else None
@@ -219,7 +220,7 @@
def _get_credentials(
- creds: Optional[ChannelCredentials],
+ creds: ChannelCredentials | None,
credential_entry_point_env_key: str,
certificate_file_env_key: str,
client_key_file_env_key: str,
@@ -288,15 +289,21 @@
self,
stub: ExportStubT,
result: ExportResultT,
- endpoint: Optional[str] = None,
- insecure: Optional[bool] = None,
- credentials: Optional[ChannelCredentials] = None,
- headers: Optional[
- Union[TypingSequence[Tuple[str, str]], Dict[str, str], str]
- ] = None,
- timeout: Optional[float] = None,
- compression: Optional[Compression] = None,
- channel_options: Optional[Tuple[Tuple[str, str]]] = None,
+ endpoint: str | None = None,
+ insecure: bool | None = None,
+ credentials: ChannelCredentials | None = None,
+ headers: TypingSequence[tuple[str, str]]
+ | dict[str, str]
+ | str
+ | None = None,
+ timeout: float | None = None,
+ compression: Compression | None = None,
+ channel_options: tuple[tuple[str, str]] | None = None,
+ retryable_error_codes: Iterable[StatusCode] | None = None,
+ *,
+ component_type: OtelComponentTypeValues | None = None,
+ signal: Literal["traces", "metrics", "logs"] = "traces",
+ meter_provider: MeterProvider | None = None,
):
super().__init__()
self._result = result
@@ -355,6 +362,22 @@
else compression
) or Compression.NoCompression
+ self._retryable_error_codes = retryable_error_codes or os.environ.get(
+ _OTEL_PYTHON_EXPORTER_OTLP_GRPC_RETRYABLE_ERROR_CODES
+ )
+ if isinstance(self._retryable_error_codes, str):
+ self._retryable_error_codes = frozenset(
+ StatusCode[code.strip().upper()]
+ for code in self._retryable_error_codes.split(",")
+ if code.strip()
+ )
+ elif self._retryable_error_codes is not None:
+ self._retryable_error_codes = frozenset(
+ self._retryable_error_codes
+ )
+ else:
+ self._retryable_error_codes = _RETRYABLE_ERROR_CODES
+
self._channel = None
self._client = None
@@ -370,6 +393,20 @@
OTEL_EXPORTER_OTLP_CLIENT_CERTIFICATE,
)
+ self._component_type = component_type
+ self._signal: Literal["traces", "metrics", "logs"] = signal
+ self._parsed_url = parsed_url
+ self._metrics = create_exporter_metrics(
+ self._component_type,
+ signal,
+ parsed_url,
+ meter_provider,
+ os.environ.get(OTEL_PYTHON_SDK_INTERNAL_METRICS_ENABLED, "")
+ .strip()
+ .lower()
+ == "true",
+ )
+
self._initialize_channel_and_stub()
def _initialize_channel_and_stub(self):
@@ -402,6 +439,13 @@
) -> ExportServiceRequestT:
pass
+ @abstractmethod
+ def _count_data(
+ self,
+ data: SDKDataT,
+ ) -> int:
+ pass
+
def _export(
self,
data: SDKDataT,
@@ -410,80 +454,88 @@
logger.warning("Exporter already shutdown, ignoring batch")
return self._result.FAILURE # type: ignore [reportReturnType]
- # FIXME remove this check if the export type for traces
- # gets updated to a class that represents the proto
- # TracesData and use the code below instead.
- deadline_sec = time() + self._timeout
- for retry_num in range(_MAX_RETRYS):
- try:
- if self._client is None:
- return self._result.FAILURE
- self._client.Export(
- request=self._translate_data(data),
- metadata=self._headers,
- timeout=deadline_sec - time(),
- )
- return self._result.SUCCESS # type: ignore [reportReturnType]
- except RpcError as error:
- retry_info_bin = dict(error.trailing_metadata()).get( # type:
ignore [reportAttributeAccessIssue]
- "google.rpc.retryinfo-bin" # type: ignore
[reportArgumentType]
- )
- # multiplying by a random number between .8 and 1.2 introduces
a +/20% jitter to each backoff.
- backoff_seconds = 2**retry_num * random.uniform(0.8, 1.2)
- if retry_info_bin is not None:
- retry_info = RetryInfo()
- retry_info.ParseFromString(retry_info_bin)
- backoff_seconds = (
- retry_info.retry_delay.seconds
- + retry_info.retry_delay.nanos / 1.0e9
+ with self._metrics.export_operation(self._count_data(data)) as result:
+ # FIXME remove this check if the export type for traces
+ # gets updated to a class that represents the proto
+ # TracesData and use the code below instead.
+ deadline_sec = time() + self._timeout
+ for retry_num in range(_MAX_RETRYS):
+ try:
+ if self._client is None:
+ return self._result.FAILURE
+ self._client.Export(
+ request=self._translate_data(data),
+ metadata=self._headers,
+ timeout=deadline_sec - time(),
)
-
- # For UNAVAILABLE errors, reinitialize the channel to force
reconnection
- if error.code() == StatusCode.UNAVAILABLE and retry_num == 0:
# type: ignore
- logger.debug(
- "Reinitializing gRPC channel for %s exporter due to
UNAVAILABLE error",
- self._exporting,
+ return self._result.SUCCESS # type: ignore
[reportReturnType]
+ except RpcError as error:
+ retry_info_bin = dict(error.trailing_metadata()).get( #
type: ignore [reportAttributeAccessIssue]
+ "google.rpc.retryinfo-bin" # type: ignore
[reportArgumentType]
)
- try:
- if self._channel:
- self._channel.close()
- except Exception as e:
+ # multiplying by a random number between .8 and 1.2
introduces a +/20% jitter to each backoff.
+ backoff_seconds = 2**retry_num * random.uniform(0.8, 1.2)
+ if retry_info_bin is not None:
+ retry_info = RetryInfo()
+ retry_info.ParseFromString(retry_info_bin)
+ backoff_seconds = (
+ retry_info.retry_delay.seconds
+ + retry_info.retry_delay.nanos / 1.0e9
+ )
+
+ # For UNAVAILABLE errors, reinitialize the channel to
force reconnection
+ if (
+ error.code() == StatusCode.UNAVAILABLE
+ and retry_num == 0
+ ): # type: ignore
logger.debug(
- "Error closing channel for %s exporter to %s: %s",
+ "Reinitializing gRPC channel for %s exporter due
to UNAVAILABLE error",
+ self._exporting,
+ )
+ try:
+ if self._channel:
+ self._channel.close()
+ except Exception as e:
+ logger.debug(
+ "Error closing channel for %s exporter to %s:
%s",
+ self._exporting,
+ self._endpoint,
+ str(e),
+ )
+ # Enable channel reconnection for subsequent calls
+ self._initialize_channel_and_stub()
+
+ if (
+ error.code() not in self._retryable_error_codes #
type: ignore [reportAttributeAccessIssue]
+ or retry_num + 1 == _MAX_RETRYS
+ or backoff_seconds > (deadline_sec - time())
+ or self._shutdown
+ ):
+ logger.error(
+ "Failed to export %s to %s, error code: %s",
self._exporting,
self._endpoint,
- str(e),
+ error.code(), # type: ignore
[reportAttributeAccessIssue]
+ exc_info=error.code() == StatusCode.UNKNOWN, #
type: ignore [reportAttributeAccessIssue]
)
- # Enable channel reconnection for subsequent calls
- self._initialize_channel_and_stub()
-
- if (
- error.code() not in _RETRYABLE_ERROR_CODES # type: ignore
[reportAttributeAccessIssue]
- or retry_num + 1 == _MAX_RETRYS
- or backoff_seconds > (deadline_sec - time())
- or self._shutdown
- ):
- logger.error(
- "Failed to export %s to %s, error code: %s",
+ result.error = error
+ result.error_attrs = {
+ RPC_RESPONSE_STATUS_CODE: error.code().name
+ }
+ return self._result.FAILURE # type: ignore
[reportReturnType]
+ logger.warning(
+ "Transient error %s encountered while exporting %s to
%s, retrying in %.2fs.",
+ error.code(), # type: ignore
[reportAttributeAccessIssue]
self._exporting,
self._endpoint,
- error.code(), # type: ignore
[reportAttributeAccessIssue]
- exc_info=error.code() == StatusCode.UNKNOWN, # type:
ignore [reportAttributeAccessIssue]
+ backoff_seconds,
)
- return self._result.FAILURE # type: ignore
[reportReturnType]
- logger.warning(
- "Transient error %s encountered while exporting %s to %s,
retrying in %.2fs.",
- error.code(), # type: ignore [reportAttributeAccessIssue]
- self._exporting,
- self._endpoint,
- backoff_seconds,
- )
- shutdown = self._shutdown_in_progress.wait(backoff_seconds)
- if shutdown:
- logger.warning("Shutdown in progress, aborting retry.")
- break
- # Not possible to reach here but the linter is complaining.
- return self._result.FAILURE # type: ignore [reportReturnType]
+ shutdown = self._shutdown_in_progress.wait(backoff_seconds)
+ if shutdown:
+ logger.warning("Shutdown in progress, aborting retry.")
+ break
+ # Not possible to reach here but the linter is complaining.
+ return self._result.FAILURE # type: ignore [reportReturnType]
def shutdown(self, timeout_millis: float = 30_000, **kwargs) -> None:
"""
@@ -508,3 +560,15 @@
warning messages.
"""
pass
+
+ def _set_meter_provider(self, meter_provider: MeterProvider) -> None:
+ self._metrics = create_exporter_metrics(
+ self._component_type,
+ self._signal,
+ self._parsed_url,
+ meter_provider,
+ os.environ.get(OTEL_PYTHON_SDK_INTERNAL_METRICS_ENABLED, "")
+ .strip()
+ .lower()
+ == "true",
+ )
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/opentelemetry_exporter_otlp_proto_grpc-1.41.1/src/opentelemetry/exporter/otlp/proto/grpc/metric_exporter/__init__.py
new/opentelemetry_exporter_otlp_proto_grpc-1.42.1/src/opentelemetry/exporter/otlp/proto/grpc/metric_exporter/__init__.py
---
old/opentelemetry_exporter_otlp_proto_grpc-1.41.1/src/opentelemetry/exporter/otlp/proto/grpc/metric_exporter/__init__.py
2020-02-02 01:00:00.000000000 +0100
+++
new/opentelemetry_exporter_otlp_proto_grpc-1.42.1/src/opentelemetry/exporter/otlp/proto/grpc/metric_exporter/__init__.py
2020-02-02 01:00:00.000000000 +0100
@@ -1,25 +1,15 @@
# Copyright The OpenTelemetry Authors
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
+# SPDX-License-Identifier: Apache-2.0
from __future__ import annotations
+from collections.abc import Iterable
+from collections.abc import Sequence as TypingSequence
from dataclasses import replace
from logging import getLogger
from os import environ
-from typing import Iterable, List, Tuple, Union
-from typing import Sequence as TypingSequence
-from grpc import ChannelCredentials, Compression
+from grpc import ChannelCredentials, Compression, StatusCode
from opentelemetry.exporter.otlp.proto.common._internal.metrics_encoder import
(
OTLPMetricExporterMixin,
)
@@ -32,6 +22,7 @@
environ_to_compression,
get_resource_data,
)
+from opentelemetry.metrics import MeterProvider
from opentelemetry.proto.collector.metrics.v1.metrics_service_pb2 import (
ExportMetricsServiceRequest,
)
@@ -72,6 +63,9 @@
from opentelemetry.sdk.metrics.export import ( # noqa: F401
Histogram as HistogramType,
)
+from opentelemetry.semconv._incubating.attributes.otel_attributes import (
+ OtelComponentTypeValues,
+)
_logger = getLogger(__name__)
@@ -100,7 +94,9 @@
endpoint: str | None = None,
insecure: bool | None = None,
credentials: ChannelCredentials | None = None,
- headers: Union[TypingSequence[Tuple[str, str]], dict[str, str], str]
+ headers: TypingSequence[tuple[str, str]]
+ | dict[str, str]
+ | str
| None = None,
timeout: float | None = None,
compression: Compression | None = None,
@@ -108,7 +104,10 @@
| None = None,
preferred_aggregation: dict[type, Aggregation] | None = None,
max_export_batch_size: int | None = None,
- channel_options: Tuple[Tuple[str, str]] | None = None,
+ channel_options: tuple[tuple[str, str]] | None = None,
+ retryable_error_codes: Iterable[StatusCode] | None = None,
+ *,
+ meter_provider: MeterProvider | None = None,
):
insecure_metrics = environ.get(OTEL_EXPORTER_OTLP_METRICS_INSECURE)
if insecure is None and insecure_metrics is not None:
@@ -153,6 +152,10 @@
timeout=timeout or environ_timeout,
compression=compression,
channel_options=channel_options,
+ retryable_error_codes=retryable_error_codes,
+ component_type=OtelComponentTypeValues.OTLP_GRPC_METRIC_EXPORTER,
+ signal="metrics",
+ meter_provider=meter_provider,
)
self._max_export_batch_size: int | None = max_export_batch_size
@@ -162,6 +165,16 @@
) -> ExportMetricsServiceRequest:
return encode_metrics(data)
+ def _count_data(self, data: MetricsData):
+ num_items = 0
+
+ for resource_metrics in data.resource_metrics:
+ for scope_metrics in resource_metrics.scope_metrics:
+ for metric in scope_metrics.metrics:
+ num_items += len(metric.data.data_points)
+
+ return num_items
+
def export(
self,
metrics_data: MetricsData,
@@ -187,10 +200,10 @@
) -> Iterable[MetricsData]:
assert self._max_export_batch_size is not None
batch_size: int = 0
- split_resource_metrics: List[ResourceMetrics] = []
+ split_resource_metrics: list[ResourceMetrics] = []
for resource_metrics in metrics_data.resource_metrics:
- split_scope_metrics: List[ScopeMetrics] = []
+ split_scope_metrics: list[ScopeMetrics] = []
split_resource_metrics.append(
replace(
resource_metrics,
@@ -198,7 +211,7 @@
)
)
for scope_metrics in resource_metrics.scope_metrics:
- split_metrics: List[Metric] = []
+ split_metrics: list[Metric] = []
split_scope_metrics.append(
replace(
scope_metrics,
@@ -206,7 +219,7 @@
)
)
for metric in scope_metrics.metrics:
- split_data_points: List[DataPointT] = []
+ split_data_points: list[DataPointT] = []
split_metrics.append(
replace(
metric,
@@ -268,6 +281,9 @@
def shutdown(self, timeout_millis: float = 30_000, **kwargs) -> None:
OTLPExporterMixin.shutdown(self, timeout_millis=timeout_millis)
+ def set_meter_provider(self, meter_provider: MeterProvider):
+ return self._set_meter_provider(meter_provider)
+
@property
def _exporting(self) -> str:
return "metrics"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/opentelemetry_exporter_otlp_proto_grpc-1.41.1/src/opentelemetry/exporter/otlp/proto/grpc/trace_exporter/__init__.py
new/opentelemetry_exporter_otlp_proto_grpc-1.42.1/src/opentelemetry/exporter/otlp/proto/grpc/trace_exporter/__init__.py
---
old/opentelemetry_exporter_otlp_proto_grpc-1.41.1/src/opentelemetry/exporter/otlp/proto/grpc/trace_exporter/__init__.py
2020-02-02 01:00:00.000000000 +0100
+++
new/opentelemetry_exporter_otlp_proto_grpc-1.42.1/src/opentelemetry/exporter/otlp/proto/grpc/trace_exporter/__init__.py
2020-02-02 01:00:00.000000000 +0100
@@ -1,24 +1,14 @@
# Copyright The OpenTelemetry Authors
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
+# SPDX-License-Identifier: Apache-2.0
"""OTLP Span Exporter"""
import logging
+from collections.abc import Iterable, Sequence
+from collections.abc import Sequence as TypingSequence
from os import environ
-from typing import Dict, Optional, Sequence, Tuple, Union
-from typing import Sequence as TypingSequence
-from grpc import ChannelCredentials, Compression
+from grpc import ChannelCredentials, Compression, StatusCode
from opentelemetry.exporter.otlp.proto.common.trace_encoder import (
encode_spans,
)
@@ -28,6 +18,7 @@
environ_to_compression,
get_resource_data,
)
+from opentelemetry.metrics import MeterProvider
from opentelemetry.proto.collector.trace.v1.trace_service_pb2 import (
ExportTraceServiceRequest,
)
@@ -58,6 +49,9 @@
)
from opentelemetry.sdk.trace import ReadableSpan
from opentelemetry.sdk.trace.export import SpanExporter, SpanExportResult
+from opentelemetry.semconv._incubating.attributes.otel_attributes import (
+ OtelComponentTypeValues,
+)
logger = logging.getLogger(__name__)
@@ -86,15 +80,19 @@
def __init__(
self,
- endpoint: Optional[str] = None,
- insecure: Optional[bool] = None,
- credentials: Optional[ChannelCredentials] = None,
- headers: Optional[
- Union[TypingSequence[Tuple[str, str]], Dict[str, str], str]
- ] = None,
- timeout: Optional[float] = None,
- compression: Optional[Compression] = None,
- channel_options: Optional[Tuple[Tuple[str, str]]] = None,
+ endpoint: str | None = None,
+ insecure: bool | None = None,
+ credentials: ChannelCredentials | None = None,
+ headers: TypingSequence[tuple[str, str]]
+ | dict[str, str]
+ | str
+ | None = None,
+ timeout: float | None = None,
+ compression: Compression | None = None,
+ channel_options: tuple[tuple[str, str]] | None = None,
+ retryable_error_codes: Iterable[StatusCode] | None = None,
+ *,
+ meter_provider: MeterProvider | None = None,
):
insecure_spans = environ.get(OTEL_EXPORTER_OTLP_TRACES_INSECURE)
if insecure is None and insecure_spans is not None:
@@ -135,6 +133,10 @@
timeout=timeout or environ_timeout,
compression=compression,
channel_options=channel_options,
+ retryable_error_codes=retryable_error_codes,
+ component_type=OtelComponentTypeValues.OTLP_GRPC_SPAN_EXPORTER,
+ signal="traces",
+ meter_provider=meter_provider,
)
def _translate_data(
@@ -142,6 +144,9 @@
) -> ExportTraceServiceRequest:
return encode_spans(data)
+ def _count_data(self, data: Sequence[ReadableSpan]):
+ return len(data)
+
def export(self, spans: Sequence[ReadableSpan]) -> SpanExportResult:
return self._export(spans)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/opentelemetry_exporter_otlp_proto_grpc-1.41.1/src/opentelemetry/exporter/otlp/proto/grpc/version/__init__.py
new/opentelemetry_exporter_otlp_proto_grpc-1.42.1/src/opentelemetry/exporter/otlp/proto/grpc/version/__init__.py
---
old/opentelemetry_exporter_otlp_proto_grpc-1.41.1/src/opentelemetry/exporter/otlp/proto/grpc/version/__init__.py
2020-02-02 01:00:00.000000000 +0100
+++
new/opentelemetry_exporter_otlp_proto_grpc-1.42.1/src/opentelemetry/exporter/otlp/proto/grpc/version/__init__.py
2020-02-02 01:00:00.000000000 +0100
@@ -1,15 +1,4 @@
# Copyright The OpenTelemetry Authors
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
+# SPDX-License-Identifier: Apache-2.0
-__version__ = "1.41.1"
+__version__ = "1.42.1"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/opentelemetry_exporter_otlp_proto_grpc-1.41.1/tests/logs/test_otlp_logs_exporter.py
new/opentelemetry_exporter_otlp_proto_grpc-1.42.1/tests/logs/test_otlp_logs_exporter.py
---
old/opentelemetry_exporter_otlp_proto_grpc-1.41.1/tests/logs/test_otlp_logs_exporter.py
2020-02-02 01:00:00.000000000 +0100
+++
new/opentelemetry_exporter_otlp_proto_grpc-1.42.1/tests/logs/test_otlp_logs_exporter.py
2020-02-02 01:00:00.000000000 +0100
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
+# SPDX-License-Identifier: Apache-2.0
# pylint: disable=too-many-lines
@@ -402,6 +391,10 @@
expected, self.exporter._translate_data([self.log_data_1])
)
+ def test_count_log_data(self):
+ # pylint: disable=protected-access
+ self.assertEqual(1, self.exporter._count_data([self.log_data_1]))
+
def test_translate_multiple_logs(self):
expected = ExportLogsServiceRequest(
resource_logs=[
@@ -539,3 +532,12 @@
[self.log_data_1, self.log_data_2, self.log_data_3]
),
)
+
+ def test_count_multiple_logs(self):
+ self.assertEqual(
+ 3,
+ # pylint: disable=protected-access
+ self.exporter._count_data(
+ [self.log_data_1, self.log_data_2, self.log_data_3]
+ ),
+ )
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/opentelemetry_exporter_otlp_proto_grpc-1.41.1/tests/test_otlp_exporter_mixin.py
new/opentelemetry_exporter_otlp_proto_grpc-1.42.1/tests/test_otlp_exporter_mixin.py
---
old/opentelemetry_exporter_otlp_proto_grpc-1.41.1/tests/test_otlp_exporter_mixin.py
2020-02-02 01:00:00.000000000 +0100
+++
new/opentelemetry_exporter_otlp_proto_grpc-1.42.1/tests/test_otlp_exporter_mixin.py
2020-02-02 01:00:00.000000000 +0100
@@ -1,26 +1,16 @@
# Copyright The OpenTelemetry Authors
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
+# SPDX-License-Identifier: Apache-2.0
import threading
import time
import unittest
+from collections.abc import Sequence
from concurrent.futures import ( # pylint: disable=no-name-in-module
ThreadPoolExecutor,
)
from logging import WARNING, getLogger
from platform import system
-from typing import Any, Optional, Sequence
+from typing import Any
from unittest import TestCase
from unittest.mock import Mock, patch
@@ -37,6 +27,7 @@
encode_spans,
)
from opentelemetry.exporter.otlp.proto.grpc.exporter import ( # noqa: F401
+ _RETRYABLE_ERROR_CODES,
InvalidCompressionValueException,
OTLPExporterMixin,
environ_to_compression,
@@ -54,12 +45,18 @@
from opentelemetry.sdk.environment_variables import (
_OTEL_PYTHON_EXPORTER_OTLP_GRPC_CREDENTIAL_PROVIDER,
OTEL_EXPORTER_OTLP_COMPRESSION,
+ OTEL_PYTHON_SDK_INTERNAL_METRICS_ENABLED,
)
+from opentelemetry.sdk.metrics import MeterProvider
+from opentelemetry.sdk.metrics.export import InMemoryMetricReader
from opentelemetry.sdk.trace import ReadableSpan, _Span
from opentelemetry.sdk.trace.export import (
SpanExporter,
SpanExportResult,
)
+from opentelemetry.semconv._incubating.attributes.otel_attributes import (
+ OtelComponentTypeValues,
+)
from opentelemetry.test.mock_test_classes import IterEntryPoint
logger = getLogger(__name__)
@@ -78,13 +75,23 @@
],
):
def __init__(self, **kwargs):
- super().__init__(TraceServiceStub, SpanExportResult, **kwargs)
+ super().__init__(
+ TraceServiceStub,
+ SpanExportResult,
+ component_type=OtelComponentTypeValues.OTLP_GRPC_SPAN_EXPORTER,
+ signal="traces",
+ meter_provider=kwargs.pop("meter_provider", None),
+ **kwargs,
+ )
def _translate_data(
self, data: Sequence[ReadableSpan]
) -> ExportTraceServiceRequest:
return encode_spans(data)
+ def _count_data(self, data: Sequence[ReadableSpan]) -> int:
+ return len(data)
+
def export(self, spans: Sequence[ReadableSpan]) -> SpanExportResult:
return self._export(spans)
@@ -100,8 +107,8 @@
def __init__(
self,
export_result: StatusCode,
- optional_retry_nanos: Optional[int] = None,
- optional_export_sleep: Optional[float] = None,
+ optional_retry_nanos: int | None = None,
+ optional_export_sleep: float | None = None,
):
self.export_result = export_result
self.optional_export_sleep = optional_export_sleep
@@ -149,11 +156,12 @@
# an argument that has a member that points to the thread.
del self._target, self._args, self._kwargs # type: ignore
- def join(self, timeout: Optional[float] = None) -> Any:
+ def join(self, timeout: float | None = None) -> Any:
super().join(timeout=timeout)
return self._return
+# pylint: disable-next=too-many-public-methods
class TestOTLPExporterMixin(TestCase):
def setUp(self):
self.server = server(ThreadPoolExecutor(max_workers=10))
@@ -161,7 +169,14 @@
self.server.add_insecure_port("127.0.0.1:4317")
self.server.start()
- self.exporter = OTLPSpanExporterForTesting(insecure=True)
+
+ self.metric_reader = InMemoryMetricReader()
+ self.meter_provider = MeterProvider(
+ metric_readers=[self.metric_reader]
+ )
+ self.exporter = OTLPSpanExporterForTesting(
+ insecure=True, meter_provider=self.meter_provider
+ )
self.span = _Span(
"a",
context=Mock(
@@ -364,18 +379,44 @@
),
)
+ @patch.dict(
+ "os.environ", {OTEL_PYTHON_SDK_INTERNAL_METRICS_ENABLED: " true "}
+ )
def test_shutdown(self):
add_TraceServiceServicer_to_server(
TraceServiceServicerWithExportParams(StatusCode.OK),
self.server,
)
+ exporter = OTLPSpanExporterForTesting(
+ insecure=True, meter_provider=self.meter_provider
+ )
self.assertEqual(
- self.exporter.export([self.span]), SpanExportResult.SUCCESS
+ exporter.export([self.span]), SpanExportResult.SUCCESS
)
- self.exporter.shutdown()
+ metrics_data = self.metric_reader.get_metrics_data()
+ scope_metrics = metrics_data.resource_metrics[0].scope_metrics[0]
+ self.assertEqual(scope_metrics.scope.name, "opentelemetry-sdk")
+ metrics = sorted(scope_metrics.metrics, key=lambda m: m.name)
+ self.assertEqual(len(metrics), 3)
+ self.assertEqual(
+ metrics[0].name, "otel.sdk.exporter.operation.duration"
+ )
+ self.assert_standard_metric_attrs(
+ metrics[0].data.data_points[0].attributes
+ )
+ self.assertEqual(metrics[1].name, "otel.sdk.exporter.span.exported")
+ self.assert_standard_metric_attrs(
+ metrics[1].data.data_points[0].attributes
+ )
+ self.assertEqual(metrics[2].name, "otel.sdk.exporter.span.inflight")
+ self.assert_standard_metric_attrs(
+ metrics[2].data.data_points[0].attributes
+ )
+
+ exporter.shutdown()
with self.assertLogs(level=WARNING) as warning:
self.assertEqual(
- self.exporter.export([self.span]), SpanExportResult.FAILURE
+ exporter.export([self.span]), SpanExportResult.FAILURE
)
self.assertEqual(
warning.records[0].message,
@@ -437,6 +478,9 @@
system() == "Windows",
"For gRPC + windows there's some added delay in the RPCs which breaks
the assertion over amount of time passed.",
)
+ @patch.dict(
+ "os.environ", {OTEL_PYTHON_SDK_INTERNAL_METRICS_ENABLED: "true"}
+ )
def test_retry_info_is_respected(self):
mock_trace_service = TraceServiceServicerWithExportParams(
StatusCode.UNAVAILABLE,
@@ -446,7 +490,9 @@
mock_trace_service,
self.server,
)
- exporter = OTLPSpanExporterForTesting(insecure=True, timeout=10)
+ exporter = OTLPSpanExporterForTesting(
+ insecure=True, timeout=10, meter_provider=self.meter_provider
+ )
before = time.time()
self.assertEqual(
exporter.export([self.span]),
@@ -457,6 +503,51 @@
# 1 second plus wiggle room so the test passes consistently.
self.assertAlmostEqual(after - before, 1, 1)
+ metrics_data = self.metric_reader.get_metrics_data()
+ scope_metrics = metrics_data.resource_metrics[0].scope_metrics[0]
+ self.assertEqual(scope_metrics.scope.name, "opentelemetry-sdk")
+ metrics = sorted(scope_metrics.metrics, key=lambda m: m.name)
+ self.assertEqual(len(metrics), 3)
+ self.assertEqual(
+ metrics[0].name, "otel.sdk.exporter.operation.duration"
+ )
+ self.assert_standard_metric_attrs(
+ metrics[0].data.data_points[0].attributes
+ )
+ self.assertEqual(
+ metrics[0].data.data_points[0].attributes["error.type"],
+ "_InactiveRpcError",
+ )
+ self.assertEqual(
+ metrics[0]
+ .data.data_points[0]
+ .attributes["rpc.response.status_code"],
+ "UNAVAILABLE",
+ )
+ self.assertEqual(metrics[1].name, "otel.sdk.exporter.span.exported")
+ self.assert_standard_metric_attrs(
+ metrics[1].data.data_points[0].attributes
+ )
+ self.assertEqual(
+ metrics[1].data.data_points[0].attributes["error.type"],
+ "_InactiveRpcError",
+ )
+ self.assertNotIn(
+ "rpc.response.status_code",
+ metrics[1].data.data_points[0].attributes,
+ )
+ self.assertEqual(metrics[2].name, "otel.sdk.exporter.span.inflight")
+ self.assert_standard_metric_attrs(
+ metrics[2].data.data_points[0].attributes
+ )
+ self.assertNotIn(
+ "error.type", metrics[2].data.data_points[0].attributes
+ )
+ self.assertNotIn(
+ "rpc.response.status_code",
+ metrics[2].data.data_points[0].attributes,
+ )
+
@unittest.skipIf(
system() == "Windows",
"For gRPC + windows there's some added delay in the RPCs which breaks
the assertion over amount of time passed.",
@@ -532,7 +623,13 @@
(),
)
+ @patch.dict(
+ "os.environ", {OTEL_PYTHON_SDK_INTERNAL_METRICS_ENABLED: "true"}
+ )
def test_permanent_failure(self):
+ exporter = OTLPSpanExporterForTesting(
+ insecure=True, meter_provider=self.meter_provider
+ )
with self.assertLogs(level=WARNING) as warning:
add_TraceServiceServicer_to_server(
TraceServiceServicerWithExportParams(
@@ -541,13 +638,58 @@
self.server,
)
self.assertEqual(
- self.exporter.export([self.span]), SpanExportResult.FAILURE
+ exporter.export([self.span]), SpanExportResult.FAILURE
)
self.assertEqual(
warning.records[-1].message,
"Failed to export traces to localhost:4317, error code:
StatusCode.ALREADY_EXISTS",
)
+ metrics_data = self.metric_reader.get_metrics_data()
+ scope_metrics = metrics_data.resource_metrics[0].scope_metrics[0]
+ self.assertEqual(scope_metrics.scope.name, "opentelemetry-sdk")
+ metrics = sorted(scope_metrics.metrics, key=lambda m: m.name)
+ self.assertEqual(len(metrics), 3)
+ self.assertEqual(
+ metrics[0].name, "otel.sdk.exporter.operation.duration"
+ )
+ self.assert_standard_metric_attrs(
+ metrics[0].data.data_points[0].attributes
+ )
+ self.assertEqual(
+ metrics[0].data.data_points[0].attributes["error.type"],
+ "_InactiveRpcError",
+ )
+ self.assertEqual(
+ metrics[0]
+ .data.data_points[0]
+ .attributes["rpc.response.status_code"],
+ "ALREADY_EXISTS",
+ )
+ self.assertEqual(metrics[1].name, "otel.sdk.exporter.span.exported")
+ self.assert_standard_metric_attrs(
+ metrics[1].data.data_points[0].attributes
+ )
+ self.assertEqual(
+ metrics[1].data.data_points[0].attributes["error.type"],
+ "_InactiveRpcError",
+ )
+ self.assertNotIn(
+ "rpc.response.status_code",
+ metrics[1].data.data_points[0].attributes,
+ )
+ self.assertEqual(metrics[2].name, "otel.sdk.exporter.span.inflight")
+ self.assert_standard_metric_attrs(
+ metrics[2].data.data_points[0].attributes
+ )
+ self.assertNotIn(
+ "error.type", metrics[2].data.data_points[0].attributes
+ )
+ self.assertNotIn(
+ "rpc.response.status_code",
+ metrics[2].data.data_points[0].attributes,
+ )
+
def test_unavailable_reconnects(self):
"""Test that the exporter reconnects on UNAVAILABLE error"""
add_TraceServiceServicer_to_server(
@@ -570,4 +712,81 @@
# Since the initial channel was created in setUp (unpatched), this call
# must be from the reconnection logic.
self.assertTrue(mock_insecure_channel.called)
- # Verify that reconnection enabled flag is set
+
+ def test_retryable_error_codes_initialization(self):
+ # pylint: disable=protected-access
+ self.assertEqual(
+ self.exporter._retryable_error_codes, _RETRYABLE_ERROR_CODES
+ )
+ custom_codes = [StatusCode.INTERNAL, StatusCode.UNKNOWN]
+ exporter = OTLPSpanExporterForTesting(
+ insecure=True, retryable_error_codes=custom_codes
+ )
+ self.assertEqual(
+ exporter._retryable_error_codes, frozenset(custom_codes)
+ )
+
+ @patch.dict(
+ "os.environ",
+ {
+ "OTEL_PYTHON_EXPORTER_OTLP_GRPC_RETRYABLE_ERROR_CODES":
",INTERNAL, unknown,,,dEAdline_Exceeded "
+ },
+ )
+ def test_retryable_error_codes_initialization_from_env(self):
+ expected_codes = frozenset(
+ {
+ StatusCode.INTERNAL,
+ StatusCode.UNKNOWN,
+ StatusCode.DEADLINE_EXCEEDED,
+ }
+ )
+ exporter = OTLPSpanExporterForTesting()
+ # pylint: disable=protected-access
+ self.assertEqual(exporter._retryable_error_codes, expected_codes)
+
+ @unittest.skipIf(
+ system() == "Windows",
+ "For gRPC + windows there's some added delay in the RPCs which breaks
the assertion over amount of time passed.",
+ )
+ def test_retryable_error_codes_custom(self):
+ # Test that a custom error code is retried if specified
+ custom_codes = [StatusCode.INTERNAL]
+ mock_trace_service = TraceServiceServicerWithExportParams(
+ StatusCode.INTERNAL,
+ optional_retry_nanos=200000000, # .2 seconds
+ )
+ add_TraceServiceServicer_to_server(
+ mock_trace_service,
+ self.server,
+ )
+ exporter = OTLPSpanExporterForTesting(
+ insecure=True, retryable_error_codes=custom_codes, timeout=10
+ )
+
+ self.assertEqual(
+ exporter.export([self.span]),
+ SpanExportResult.FAILURE,
+ )
+
+ self.assertEqual(mock_trace_service.num_requests, 6)
+
+ # Test that a default retryable code is NOT retried if not in
custom_codes
+ mock_trace_service.num_requests = 0
+ mock_trace_service.export_result = StatusCode.UNAVAILABLE
+ self.assertEqual(
+ exporter.export([self.span]),
+ SpanExportResult.FAILURE,
+ )
+ self.assertEqual(mock_trace_service.num_requests, 1)
+
+ def assert_standard_metric_attrs(self, attributes):
+ self.assertEqual(
+ attributes["otel.component.type"], "otlp_grpc_span_exporter"
+ )
+ self.assertTrue(
+ attributes["otel.component.name"].startswith(
+ "otlp_grpc_span_exporter/"
+ )
+ )
+ self.assertEqual(attributes["server.address"], "localhost")
+ self.assertEqual(attributes["server.port"], 4317)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/opentelemetry_exporter_otlp_proto_grpc-1.41.1/tests/test_otlp_metrics_exporter.py
new/opentelemetry_exporter_otlp_proto_grpc-1.42.1/tests/test_otlp_metrics_exporter.py
---
old/opentelemetry_exporter_otlp_proto_grpc-1.41.1/tests/test_otlp_metrics_exporter.py
2020-02-02 01:00:00.000000000 +0100
+++
new/opentelemetry_exporter_otlp_proto_grpc-1.42.1/tests/test_otlp_metrics_exporter.py
2020-02-02 01:00:00.000000000 +0100
@@ -1,22 +1,10 @@
# Copyright The OpenTelemetry Authors
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
+# SPDX-License-Identifier: Apache-2.0
# pylint: disable=too-many-lines
from logging import WARNING
from os import environ
from os.path import dirname
-from typing import List
from unittest import TestCase
from unittest.mock import patch
@@ -345,7 +333,7 @@
]
)
# WHEN
- split_metrics_data: List[MetricsData] = list(
+ split_metrics_data: list[MetricsData] = list(
# pylint: disable=protected-access
OTLPMetricExporter(max_export_batch_size=2)._split_metrics_data(
metrics_data=metrics_data,
@@ -424,7 +412,7 @@
]
)
# WHEN
- split_metrics_data: List[MetricsData] = list(
+ split_metrics_data: list[MetricsData] = list(
# pylint: disable=protected-access
OTLPMetricExporter(max_export_batch_size=3)._split_metrics_data(
metrics_data=metrics_data,
@@ -515,7 +503,7 @@
]
)
# WHEN
- split_metrics_data: List[MetricsData] = list(
+ split_metrics_data: list[MetricsData] = list(
# pylint: disable=protected-access
OTLPMetricExporter(max_export_batch_size=2)._split_metrics_data(
metrics_data=metrics_data,
@@ -590,6 +578,69 @@
split_metrics_data,
)
+ def test_count_metrics_data(self):
+ # GIVEN
+ metrics_data = MetricsData(
+ resource_metrics=[
+ _resource_metrics(
+ index=1,
+ scope_metrics=[
+ _scope_metrics(
+ index=1,
+ metrics=[
+ _gauge(
+ index=1,
+ data_points=[
+ _number_data_point(11),
+ ],
+ ),
+ _gauge(
+ index=2,
+ data_points=[
+ _number_data_point(12),
+ ],
+ ),
+ ],
+ ),
+ _scope_metrics(
+ index=2,
+ metrics=[
+ _gauge(
+ index=3,
+ data_points=[
+ _number_data_point(13),
+ ],
+ ),
+ ],
+ ),
+ ],
+ ),
+ _resource_metrics(
+ index=2,
+ scope_metrics=[
+ _scope_metrics(
+ index=3,
+ metrics=[
+ _gauge(
+ index=4,
+ data_points=[
+ _number_data_point(14),
+ ],
+ ),
+ ],
+ ),
+ ],
+ ),
+ ]
+ )
+ # WHEN
+ # pylint: disable=protected-access
+ count = OTLPMetricExporter(max_export_batch_size=2)._count_data(
+ metrics_data,
+ )
+ # THEN
+ self.assertEqual(count, 4)
+
@patch("opentelemetry.exporter.otlp.proto.grpc.exporter.secure_channel")
def test_insecure_https_endpoint(self, mock_secure_channel):
OTLPMetricExporter(endpoint="https://ab.c:123", insecure=True)
@@ -766,7 +817,7 @@
def _resource_metrics(
- index: int, scope_metrics: List[ScopeMetrics]
+ index: int, scope_metrics: list[ScopeMetrics]
) -> ResourceMetrics:
return ResourceMetrics(
resource=Resource(
@@ -778,7 +829,7 @@
)
-def _scope_metrics(index: int, metrics: List[Metric]) -> ScopeMetrics:
+def _scope_metrics(index: int, metrics: list[Metric]) -> ScopeMetrics:
return ScopeMetrics(
scope=InstrumentationScope(name=f"scope_{index}"),
schema_url=f"scope_url_{index}",
@@ -786,7 +837,7 @@
)
-def _gauge(index: int, data_points: List[NumberDataPoint]) -> Metric:
+def _gauge(index: int, data_points: list[NumberDataPoint]) -> Metric:
return Metric(
name=f"gauge_{index}",
description="description",
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/opentelemetry_exporter_otlp_proto_grpc-1.41.1/tests/test_otlp_trace_exporter.py
new/opentelemetry_exporter_otlp_proto_grpc-1.42.1/tests/test_otlp_trace_exporter.py
---
old/opentelemetry_exporter_otlp_proto_grpc-1.41.1/tests/test_otlp_trace_exporter.py
2020-02-02 01:00:00.000000000 +0100
+++
new/opentelemetry_exporter_otlp_proto_grpc-1.42.1/tests/test_otlp_trace_exporter.py
2020-02-02 01:00:00.000000000 +0100
@@ -1,16 +1,5 @@
# Copyright The OpenTelemetry Authors
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
+# SPDX-License-Identifier: Apache-2.0
# pylint: disable=too-many-lines
@@ -481,6 +470,10 @@
# pylint: disable=protected-access
self.assertEqual(expected, self.exporter._translate_data([self.span]))
+ def test_count_spans(self):
+ # pylint: disable=protected-access
+ self.assertEqual(1, self.exporter._count_data([self.span]))
+
def test_translate_spans_multi(self):
expected = ExportTraceServiceRequest(
resource_spans=[
@@ -660,6 +653,13 @@
self.exporter._translate_data([self.span, self.span2, self.span3]),
)
+ def test_count_spans_multi(self):
+ self.assertEqual(
+ # pylint: disable=protected-access
+ 3,
+ self.exporter._count_data([self.span, self.span2, self.span3]),
+ )
+
def _check_translated_status(
self,
translated: ExportTraceServiceRequest,