Skip to content
Open
Show file tree
Hide file tree
Changes from 17 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## Unreleased

- Add new environment variables to the SDK `OTEL_PYTHON_EXPORTER_OTLP_{METRICS/TRACES/LOGS}_CREDENTIAL_PROVIDER` that can be used to
inject a `requests.Session` or `grpc.ChannelCredentials` object into OTLP exporters created during auto instrumentation [#4689](https://github.com/open-telemetry/opentelemetry-python/pull/4689).

## Version 1.36.0/0.57b0 (2025-07-29)

- Add missing Prometheus exporter documentation
Expand Down
2 changes: 1 addition & 1 deletion dev-requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,4 @@ psutil==5.9.6
GitPython==3.1.41
pre-commit==3.7.0; python_version >= '3.9'
pre-commit==3.5.0; python_version < '3.9'
ruff==0.6.9
ruff==0.6.9
160 changes: 153 additions & 7 deletions opentelemetry-sdk/src/opentelemetry/sdk/_configuration/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,23 @@

from __future__ import annotations

import inspect
import logging
import logging.config
import os
from abc import ABC, abstractmethod
from os import environ
from typing import Any, Callable, Mapping, Sequence, Type, Union
from typing import (
Any,
Callable,
Mapping,
MutableMapping,
Optional,
Sequence,
Type,
TypeVar,
Union,
)

from typing_extensions import Literal

Expand All @@ -46,6 +57,10 @@
OTEL_EXPORTER_OTLP_METRICS_PROTOCOL,
OTEL_EXPORTER_OTLP_PROTOCOL,
OTEL_EXPORTER_OTLP_TRACES_PROTOCOL,
OTEL_PYTHON_EXPORTER_OTLP_CREDENTIAL_PROVIDER,
OTEL_PYTHON_EXPORTER_OTLP_LOGS_CREDENTIAL_PROVIDER,
OTEL_PYTHON_EXPORTER_OTLP_METRICS_CREDENTIAL_PROVIDER,
OTEL_PYTHON_EXPORTER_OTLP_TRACES_CREDENTIAL_PROVIDER,
OTEL_TRACES_SAMPLER,
OTEL_TRACES_SAMPLER_ARG,
)
Expand All @@ -64,6 +79,22 @@
from opentelemetry.trace import set_tracer_provider
from opentelemetry.util._importlib_metadata import entry_points

try:
from grpc import ChannelCredentials

_GRPC_IMPORTED = True
except ImportError:
_GRPC_IMPORTED = False

try:
from requests import Session

_REQUESTS_IMPORTED = True
except ImportError:
_REQUESTS_IMPORTED = False

T = TypeVar("T")

_EXPORTER_OTLP = "otlp"
_EXPORTER_OTLP_PROTO_GRPC = "otlp_proto_grpc"
_EXPORTER_OTLP_PROTO_HTTP = "otlp_proto_http"
Expand All @@ -79,6 +110,12 @@
"logs": OTEL_LOGS_EXPORTER,
}

_EXPORTER_CREDENTIAL_BY_SIGNAL_TYPE = {
"traces": OTEL_PYTHON_EXPORTER_OTLP_TRACES_CREDENTIAL_PROVIDER,
"metrics": OTEL_PYTHON_EXPORTER_OTLP_METRICS_CREDENTIAL_PROVIDER,
"logs": OTEL_PYTHON_EXPORTER_OTLP_LOGS_CREDENTIAL_PROVIDER,
}

_PROTOCOL_ENV_BY_SIGNAL_TYPE = {
"traces": OTEL_EXPORTER_OTLP_TRACES_PROTOCOL,
"metrics": OTEL_EXPORTER_OTLP_METRICS_PROTOCOL,
Expand All @@ -99,10 +136,42 @@
Type[MetricReader],
Type[LogExporter],
],
Mapping[str, Any],
MutableMapping[str, Any],
]


def _load_credential_from_envvar(
environment_variable: str,
) -> Optional[
tuple[
Literal["credentials", "session"],
Union["ChannelCredentials", "Session"],
]
]:
credential_env = os.getenv(environment_variable)
if credential_env:
credentials = _import_config_component(
credential_env, "opentelemetry_otlp_credential_provider"
)()
if _GRPC_IMPORTED and isinstance(credentials, ChannelCredentials): # type: ignore[reportPossiblyUnboundVariable]
return ("credentials", credentials)

if _REQUESTS_IMPORTED and isinstance(credentials, Session): # type: ignore[reportPossiblyUnboundVariable]
return ("session", credentials)
raise RuntimeError(
f"{credential_env} is neither a grpc.ChannelCredentials or requests.Session type."
)
return None


def _import_config_component(
selected_component: str, entry_point_name: str
) -> Type:
return _import_config_components([selected_component], entry_point_name)[
0
][1]


def _import_config_components(
selected_components: Sequence[str], entry_point_name: str
) -> list[tuple[str, Type]]:
Expand Down Expand Up @@ -202,12 +271,50 @@ def _get_exporter_names(
]


def _init_exporter(
signal_type: Literal["traces", "metrics", "logs"],
exporter_args: MutableMapping[str, Any],
exporter_class: Type[T],
otlp_credential_param_for_all_signal_types: Optional[
tuple[
Literal["credentials", "session"],
Union["ChannelCredentials", "Session"],
]
] = None,
) -> T:
# Per signal type envvar should take precedence over all signal type env var.
otlp_credential_param = (
_load_credential_from_envvar(
_EXPORTER_CREDENTIAL_BY_SIGNAL_TYPE[signal_type]
)
or otlp_credential_param_for_all_signal_types
)
if otlp_credential_param:
credential_key, credential = otlp_credential_param
# We only want to inject credentials into the appropriate OTLP HTTP // GRPC exporters.
if credential_key in inspect.signature(
exporter_class.__init__
).parameters and (
"opentelemetry.exporter.otlp.proto.http" in str(exporter_class)
or "opentelemetry.exporter.otlp.proto.grpc" in str(exporter_class)
or "tests.test_configurator" in str(exporter_class)
):
exporter_args[credential_key] = credential
return exporter_class(**exporter_args)


def _init_tracing(
exporters: dict[str, Type[SpanExporter]],
id_generator: IdGenerator | None = None,
sampler: Sampler | None = None,
resource: Resource | None = None,
exporter_args_map: ExporterArgsMap | None = None,
otlp_credential_param: Optional[
tuple[
Literal["credentials", "session"],
Union["ChannelCredentials", "Session"],
]
] = None,
):
provider = TracerProvider(
id_generator=id_generator,
Expand All @@ -220,7 +327,14 @@ def _init_tracing(
for _, exporter_class in exporters.items():
exporter_args = exporter_args_map.get(exporter_class, {})
provider.add_span_processor(
BatchSpanProcessor(exporter_class(**exporter_args))
BatchSpanProcessor(
_init_exporter(
"traces",
exporter_args,
exporter_class,
otlp_credential_param,
)
)
)


Expand All @@ -230,6 +344,12 @@ def _init_metrics(
],
resource: Resource | None = None,
exporter_args_map: ExporterArgsMap | None = None,
otlp_credential_param: Optional[
tuple[
Literal["credentials", "session"],
Union["ChannelCredentials", "Session"],
]
] = None,
):
metric_readers = []

Expand All @@ -241,7 +361,12 @@ def _init_metrics(
else:
metric_readers.append(
PeriodicExportingMetricReader(
exporter_or_reader_class(**exporter_args)
_init_exporter(
"metrics",
exporter_args,
exporter_or_reader_class,
otlp_credential_param,
)
)
)

Expand All @@ -254,6 +379,12 @@ def _init_logging(
resource: Resource | None = None,
setup_logging_handler: bool = True,
exporter_args_map: ExporterArgsMap | None = None,
otlp_credential_param: Optional[
tuple[
Literal["credentials", "session"],
Union["ChannelCredentials", "Session"],
]
] = None,
):
provider = LoggerProvider(resource=resource)
set_logger_provider(provider)
Expand All @@ -262,7 +393,14 @@ def _init_logging(
for _, exporter_class in exporters.items():
exporter_args = exporter_args_map.get(exporter_class, {})
provider.add_log_record_processor(
BatchLogRecordProcessor(exporter_class(**exporter_args))
BatchLogRecordProcessor(
_init_exporter(
"logs",
exporter_args,
exporter_class,
otlp_credential_param,
)
)
)

event_logger_provider = EventLoggerProvider(logger_provider=provider)
Expand Down Expand Up @@ -406,7 +544,7 @@ def _import_id_generator(id_generator_name: str) -> IdGenerator:
raise RuntimeError(f"{id_generator_name} is not an IdGenerator")


def _initialize_components(
def _initialize_components( # pylint: disable=too-many-locals
auto_instrumentation_version: str | None = None,
trace_exporter_names: list[str] | None = None,
metric_exporter_names: list[str] | None = None,
Expand Down Expand Up @@ -445,15 +583,22 @@ def _initialize_components(
# from the env variable else defaults to "unknown_service"
resource = Resource.create(resource_attributes)

otlp_credential_param = _load_credential_from_envvar(
OTEL_PYTHON_EXPORTER_OTLP_CREDENTIAL_PROVIDER
)
_init_tracing(
exporters=span_exporters,
id_generator=id_generator,
sampler=sampler,
resource=resource,
otlp_credential_param=otlp_credential_param,
exporter_args_map=exporter_args_map,
)
_init_metrics(
metric_exporters, resource, exporter_args_map=exporter_args_map
metric_exporters,
resource,
otlp_credential_param=otlp_credential_param,
exporter_args_map=exporter_args_map,
)
if setup_logging_handler is None:
setup_logging_handler = (
Expand All @@ -468,6 +613,7 @@ def _initialize_components(
log_exporters,
resource,
setup_logging_handler,
otlp_credential_param=otlp_credential_param,
exporter_args_map=exporter_args_map,
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -394,6 +394,43 @@
A scheme of https indicates a secure connection and takes precedence over this configuration setting.
"""

OTEL_PYTHON_EXPORTER_OTLP_LOGS_CREDENTIAL_PROVIDER = (
"OTEL_PYTHON_EXPORTER_OTLP_LOGS_CREDENTIAL_PROVIDER"
)
"""
.. envvar:: OTEL_PYTHON_EXPORTER_OTLP_LOGS_CREDENTIAL_PROVIDER

The :envvar:`OTEL_PYTHON_EXPORTER_OTLP_LOGS_CREDENTIAL_PROVIDER` provides either ChannelCredentials for grpc OTLP Log exporters,
or request.Session for HTTP Log exporters.
"""
OTEL_PYTHON_EXPORTER_OTLP_CREDENTIAL_PROVIDER = (
"OTEL_PYTHON_EXPORTER_OTLP_CREDENTIAL_PROVIDER"
)
"""
.. envvar:: OTEL_PYTHON_EXPORTER_OTLP_CREDENTIAL_PROVIDER

The :envvar:`OTEL_PYTHON_EXPORTER_OTLP_CREDENTIAL_PROVIDER` provides either ChannelCredentials for all grpc OTLP exporters,
or request.Session for HTTP exporters.
"""
OTEL_PYTHON_EXPORTER_OTLP_TRACES_CREDENTIAL_PROVIDER = (
"OTEL_PYTHON_EXPORTER_OTLP_TRACES_CREDENTIAL_PROVIDER"
)
"""
.. envvar:: OTEL_PYTHON_EXPORTER_OTLP_TRACES_CREDENTIAL_PROVIDER

The :envvar:`OTEL_PYTHON_EXPORTER_OTLP_TRACES_CREDENTIAL_PROVIDER` provides either ChannelCredentials for grpc OTLP Span exporters,
or request.Session for HTTP Span exporters.
"""
OTEL_PYTHON_EXPORTER_OTLP_METRICS_CREDENTIAL_PROVIDER = (
"OTEL_PYTHON_EXPORTER_OTLP_METRICS_CREDENTIAL_PROVIDER"
)
"""
.. envvar:: OTEL_PYTHON_EXPORTER_OTLP_METRICS_CREDENTIAL_PROVIDER

The :envvar:`OTEL_PYTHON_EXPORTER_OTLP_METRICS_CREDENTIAL_PROVIDER` provides either ChannelCredentials for grpc OTLP Metric exporters,
or request.Session for HTTP Metric exporters.
"""

OTEL_EXPORTER_OTLP_TRACES_CERTIFICATE = "OTEL_EXPORTER_OTLP_TRACES_CERTIFICATE"
"""
.. envvar:: OTEL_EXPORTER_OTLP_TRACES_CERTIFICATE
Expand Down
2 changes: 2 additions & 0 deletions opentelemetry-sdk/test-requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ py-cpuinfo==9.0.0
pytest==7.4.4
tomli==2.0.1
typing_extensions==4.10.0
grpcio==1.66.2
requests==2.32.3
wrapt==1.16.0
zipp==3.19.2
-e tests/opentelemetry-test-utils
Expand Down
Loading
Loading