Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
([#4973](https://github.com/open-telemetry/opentelemetry-python/pull/4973))
- `opentelemetry-exporter-prometheus`: Fix metric name prefix
([#4895](https://github.com/open-telemetry/opentelemetry-python/pull/4895))
- `opentelemetry-sdk`: Implement experimental Meter configurator
([#4966](https://github.com/open-telemetry/opentelemetry-python/pull/4966))

## Version 1.40.0/0.61b0 (2026-03-04)

Expand Down
47 changes: 47 additions & 0 deletions opentelemetry-sdk/benchmarks/metrics/test_benchmark_metrics.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,17 @@
import pytest

from opentelemetry.sdk.metrics import Counter, MeterProvider
from opentelemetry.sdk.metrics._internal import (
_default_meter_configurator,
_disable_meter_configurator,
_MeterConfig,
_RuleBasedMeterConfigurator,
)
from opentelemetry.sdk.metrics.export import (
AggregationTemporality,
InMemoryMetricReader,
)
from opentelemetry.sdk.util.instrumentation import _scope_name_matches_glob

reader_cumulative = InMemoryMetricReader()
reader_delta = InMemoryMetricReader(
Expand Down Expand Up @@ -77,3 +84,43 @@ def benchmark_up_down_counter_add():
udcounter.add(1, labels)

benchmark(benchmark_up_down_counter_add)


@pytest.fixture(params=[None, 0, 1, 10, 50])
def num_meter_configurator_rules(request):
return request.param


# pylint: disable=protected-access,redefined-outer-name
def test_counter_add_with_meter_configurator_rules(
benchmark, num_meter_configurator_rules
):
def benchmark_counter_add():
counter_cumulative.add(1, {})

if num_meter_configurator_rules is None:
provider_reader_cumulative._set_meter_configurator(
meter_configurator=_disable_meter_configurator
)
else:

def meter_configurator(meter_scope):
return _RuleBasedMeterConfigurator(
rules=[
(
_scope_name_matches_glob(glob_pattern=str(i)),
_MeterConfig(is_enabled=True),
)
for i in range(num_meter_configurator_rules)
],
default_config=_MeterConfig(is_enabled=True),
)(meter_scope)

provider_reader_cumulative._set_meter_configurator(
meter_configurator=meter_configurator
)

benchmark(benchmark_counter_add)
provider_reader_cumulative._set_meter_configurator(
meter_configurator=_default_meter_configurator
)
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,10 @@
TracerProvider,
_default_tracer_configurator,
_RuleBasedTracerConfigurator,
_scope_name_matches_glob,
_TracerConfig,
sampling,
)
from opentelemetry.sdk.util.instrumentation import _scope_name_matches_glob

tracer = TracerProvider(
sampler=sampling.DEFAULT_ON,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,11 +52,13 @@
OTEL_EXPORTER_OTLP_METRICS_PROTOCOL,
OTEL_EXPORTER_OTLP_PROTOCOL,
OTEL_EXPORTER_OTLP_TRACES_PROTOCOL,
OTEL_PYTHON_METER_CONFIGURATOR,
OTEL_PYTHON_TRACER_CONFIGURATOR,
OTEL_TRACES_SAMPLER,
OTEL_TRACES_SAMPLER_ARG,
)
from opentelemetry.sdk.metrics import MeterProvider
from opentelemetry.sdk.metrics._internal import _MeterConfiguratorT
from opentelemetry.sdk.metrics.export import (
MetricExporter,
MetricReader,
Expand Down Expand Up @@ -171,6 +173,10 @@ def _get_tracer_configurator() -> str | None:
return environ.get(OTEL_PYTHON_TRACER_CONFIGURATOR, None)


def _get_meter_configurator() -> str | None:
return environ.get(OTEL_PYTHON_METER_CONFIGURATOR, None)


def _get_exporter_entry_point(
exporter_name: str, signal_type: Literal["traces", "metrics", "logs"]
):
Expand Down Expand Up @@ -267,6 +273,7 @@ def _init_metrics(
],
resource: Resource | None = None,
exporter_args_map: ExporterArgsMap | None = None,
meter_configurator: _MeterConfiguratorT | None = None,
):
metric_readers = []

Expand All @@ -282,7 +289,11 @@ def _init_metrics(
)
)

provider = MeterProvider(resource=resource, metric_readers=metric_readers)
provider = MeterProvider(
resource=resource,
metric_readers=metric_readers,
_meter_configurator=meter_configurator,
)
set_meter_provider(provider)


Expand Down Expand Up @@ -387,6 +398,27 @@ def _import_tracer_configurator(
return tracer_configurator_impl


def _import_meter_configurator(
meter_configurator_name: str | None,
) -> _MeterConfiguratorT | None:
if not meter_configurator_name:
return None

try:
_, meter_configurator_impl = _import_config_components(
[meter_configurator_name.strip()],
"_opentelemetry_meter_configurator",
)[0]
except Exception as exc: # pylint: disable=broad-exception-caught
_logger.warning(
"Using default meter configurator. Failed to load meter configurator, %s: %s",
meter_configurator_name,
exc,
)
return None
return meter_configurator_impl


def _import_exporters(
trace_exporter_names: Sequence[str],
metric_exporter_names: Sequence[str],
Expand Down Expand Up @@ -507,6 +539,7 @@ def _initialize_components(
export_log_record_processor: _ConfigurationExporterLogRecordProcessorT
| None = None,
tracer_configurator: _TracerConfiguratorT | None = None,
meter_configurator: _MeterConfiguratorT | None = None,
):
# pylint: disable=too-many-locals
if trace_exporter_names is None:
Expand Down Expand Up @@ -538,6 +571,11 @@ def _initialize_components(
tracer_configurator = _import_tracer_configurator(
tracer_configurator_name
)
if meter_configurator is None:
meter_configurator_name = _get_meter_configurator()
meter_configurator = _import_meter_configurator(
meter_configurator_name
)

# if env var OTEL_RESOURCE_ATTRIBUTES is given, it will read the service_name
# from the env variable else defaults to "unknown_service"
Expand All @@ -554,7 +592,10 @@ def _initialize_components(
tracer_configurator=tracer_configurator,
)
_init_metrics(
metric_exporters, resource, exporter_args_map=exporter_args_map
exporters_or_readers=metric_exporters,
resource=resource,
exporter_args_map=exporter_args_map,
meter_configurator=meter_configurator,
)
if setup_logging_handler is None:
setup_logging_handler = (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -814,3 +814,15 @@ def channel_credential_provider() -> grpc.ChannelCredentials:
This is an experimental environment variable and the name of this variable and its behavior can
change in a non-backwards compatible way.
"""

OTEL_PYTHON_METER_CONFIGURATOR = "OTEL_PYTHON_METER_CONFIGURATOR"
"""
.. envvar:: OTEL_PYTHON_METER_CONFIGURATOR

The :envvar:`OTEL_PYTHON_METER_CONFIGURATOR` environment variable allows users to set a
custom Meter Configurator function.
Default: opentelemetry.sdk.metrics._internal._default_meter_configurator

This is an experimental environment variable and the name of this variable and its behavior can
change in a non-backwards compatible way.
"""
Loading