diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2b6574e9..38116970 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -72,10 +72,11 @@ jobs: py311: "3.11" py312: "3.12" py313: "3.13" + py314: "3.14" strategy: fail-fast: false matrix: - python-version: [py39, py310, py311, py312, py313] + python-version: [py39, py310, py311, py312, py313, py314] steps: - uses: actions/checkout@v6 - name: Set up Python ${{ env[matrix.python-version] }} diff --git a/README.md b/README.md index e7dc47ea..475dc221 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ With EDOT Python you have access to all the features of the OpenTelemetry Python ## Configuration -The distribution supports all the configuration variables from OpenTelemetry Python project version 1.40.0. +The distribution supports all the configuration variables from OpenTelemetry Python project version 1.41.1. See [Configuration](https://www.elastic.co/docs/reference/opentelemetry/edot-sdks/python/configuration.html) for more details. diff --git a/dev-requirements.txt b/dev-requirements.txt index 0e01a561..d2bd571e 100644 --- a/dev-requirements.txt +++ b/dev-requirements.txt @@ -4,9 +4,9 @@ # # pip-compile --extra=dev --output-file=dev-requirements.txt --strip-extras pyproject.toml # -build==1.4.2 +build==1.4.4 # via pip-tools -certifi==2026.2.25 +certifi==2026.4.22 # via requests charset-normalizer==3.4.7 # via requests @@ -14,7 +14,7 @@ click==8.1.8 # via pip-tools exceptiongroup==1.3.1 # via pytest -googleapis-common-protos==1.73.1 +googleapis-common-protos==1.75.0 # via # opentelemetry-exporter-otlp-proto-grpc # opentelemetry-exporter-otlp-proto-http @@ -22,7 +22,7 @@ grpcio==1.80.0 # via # opentelemetry-exporter-otlp-proto-grpc # oteltest -idna==3.11 +idna==3.15 # via requests importlib-metadata==8.7.1 # via @@ -32,7 +32,7 @@ iniconfig==2.1.0 # via pytest leb128==1.0.9 # via elastic-opentelemetry (pyproject.toml) -opentelemetry-api==1.40.0 +opentelemetry-api==1.41.1 # via # elastic-opentelemetry (pyproject.toml) # opentelemetry-exporter-otlp-proto-grpc @@ -44,25 +44,25 @@ opentelemetry-api==1.40.0 # opentelemetry-sdk # opentelemetry-semantic-conventions # oteltest -opentelemetry-exporter-otlp==1.40.0 +opentelemetry-exporter-otlp==1.41.1 # via elastic-opentelemetry (pyproject.toml) -opentelemetry-exporter-otlp-proto-common==1.40.0 +opentelemetry-exporter-otlp-proto-common==1.41.1 # via # opentelemetry-exporter-otlp-proto-grpc # opentelemetry-exporter-otlp-proto-http -opentelemetry-exporter-otlp-proto-grpc==1.40.0 +opentelemetry-exporter-otlp-proto-grpc==1.41.1 # via opentelemetry-exporter-otlp -opentelemetry-exporter-otlp-proto-http==1.40.0 +opentelemetry-exporter-otlp-proto-http==1.41.1 # via opentelemetry-exporter-otlp -opentelemetry-instrumentation==0.61b0 +opentelemetry-instrumentation==0.62b1 # via # elastic-opentelemetry (pyproject.toml) # opentelemetry-instrumentation-system-metrics -opentelemetry-instrumentation-system-metrics==0.61b0 +opentelemetry-instrumentation-system-metrics==0.62b1 # via elastic-opentelemetry (pyproject.toml) opentelemetry-opamp-client==0.2b0 # via elastic-opentelemetry (pyproject.toml) -opentelemetry-proto==1.40.0 +opentelemetry-proto==1.41.1 # via # opentelemetry-exporter-otlp-proto-common # opentelemetry-exporter-otlp-proto-grpc @@ -70,11 +70,11 @@ opentelemetry-proto==1.40.0 # oteltest opentelemetry-resource-detector-azure==0.1.5 # via elastic-opentelemetry (pyproject.toml) -opentelemetry-resource-detector-containerid==0.61b0 +opentelemetry-resource-detector-containerid==0.62b1 # via elastic-opentelemetry (pyproject.toml) opentelemetry-resourcedetector-gcp==1.8.0a0 # via elastic-opentelemetry (pyproject.toml) -opentelemetry-sdk==1.40.0 +opentelemetry-sdk==1.41.1 # via # elastic-opentelemetry (pyproject.toml) # opentelemetry-exporter-otlp-proto-grpc @@ -85,14 +85,14 @@ opentelemetry-sdk==1.40.0 # opentelemetry-sdk-extension-aws opentelemetry-sdk-extension-aws==2.1.0 # via elastic-opentelemetry (pyproject.toml) -opentelemetry-semantic-conventions==0.61b0 +opentelemetry-semantic-conventions==0.62b1 # via # elastic-opentelemetry (pyproject.toml) # opentelemetry-instrumentation # opentelemetry-sdk oteltest==0.24.0 # via elastic-opentelemetry (pyproject.toml) -packaging==26.0 +packaging==26.2 # via # build # elastic-opentelemetry (pyproject.toml) @@ -140,15 +140,15 @@ typing-extensions==4.15.0 # opentelemetry-semantic-conventions urllib3==2.6.3 # via requests -uuid-utils==0.14.1 +uuid-utils==0.15.0 # via # elastic-opentelemetry (pyproject.toml) # opentelemetry-opamp-client -wheel==0.46.3 +wheel==0.47.0 # via pip-tools -wrapt==1.17.3 +wrapt==2.1.2 # via opentelemetry-instrumentation -zipp==3.23.0 +zipp==3.23.1 # via importlib-metadata # The following packages are considered to be unsafe in a requirements file: diff --git a/docs/reference/edot-python/supported-technologies.md b/docs/reference/edot-python/supported-technologies.md index e48ea849..9f096765 100644 --- a/docs/reference/edot-python/supported-technologies.md +++ b/docs/reference/edot-python/supported-technologies.md @@ -72,7 +72,6 @@ Instrumentations are not installed by default. Use the [edot-bootstrap](/referen | [opentelemetry-instrumentation-asyncio](https://github.com/open-telemetry/opentelemetry-python-contrib/tree/main/instrumentation/opentelemetry-instrumentation-asyncio) | `asyncio` | development | `opentelemetry.instrumentation.asyncio` | | [opentelemetry-instrumentation-asyncpg](https://github.com/open-telemetry/opentelemetry-python-contrib/tree/main/instrumentation/opentelemetry-instrumentation-asyncpg) | `asyncpg >= 0.12.0` | development | `opentelemetry.instrumentation.asyncpg` | | [opentelemetry-instrumentation-aws-lambda](https://github.com/open-telemetry/opentelemetry-python-contrib/tree/main/instrumentation/opentelemetry-instrumentation-aws-lambda) | | development | `opentelemetry.instrumentation.aws_lambda` | -| [opentelemetry-instrumentation-boto](https://github.com/open-telemetry/opentelemetry-python-contrib/tree/main/instrumentation/opentelemetry-instrumentation-boto) | `boto~=2.0` | development | `opentelemetry.instrumentation.boto` | | [opentelemetry-instrumentation-boto3sqs](https://github.com/open-telemetry/opentelemetry-python-contrib/tree/main/instrumentation/opentelemetry-instrumentation-boto3sqs) | `boto3 ~= 1.0` | development | `opentelemetry.instrumentation.boto3sqs` | | [opentelemetry-instrumentation-botocore](https://github.com/open-telemetry/opentelemetry-python-contrib/tree/main/instrumentation/opentelemetry-instrumentation-botocore) | `botocore ~= 1.0` | development | `opentelemetry.instrumentation.botocore`, `opentelemetry.instrumentation.botocore.bedrock-runtime`, `opentelemetry.instrumentation.botocore.dynamodb`, `opentelemetry.instrumentation.botocore.lambda`, `opentelemetry.instrumentation.botocore.secreatsmanager`, `opentelemetry.instrumentation.botocore.stepfunctions`, `opentelemetry.instrumentation.botocore.sns`, `opentelemetry.instrumentation.botocore.sqs` | | [opentelemetry-instrumentation-cassandra](https://github.com/open-telemetry/opentelemetry-python-contrib/tree/main/instrumentation/opentelemetry-instrumentation-cassandra) | `cassandra-driver ~= 3.25`,`scylla-driver ~= 3.25` | development | `opentelemetry.instrumentation.cassandra` | diff --git a/operator/requirements.txt b/operator/requirements.txt index 73946239..90c0d8a6 100644 --- a/operator/requirements.txt +++ b/operator/requirements.txt @@ -1,59 +1,58 @@ -opentelemetry-exporter-prometheus==0.61b0 +opentelemetry-exporter-prometheus==0.62b1 opentelemetry-propagator-aws-xray==1.0.2 -opentelemetry-propagator-b3==1.40.0 -opentelemetry-propagator-jaeger==1.40.0 -opentelemetry-propagator-ot-trace==0.61b0 +opentelemetry-propagator-b3==1.41.1 +opentelemetry-propagator-jaeger==1.41.1 +opentelemetry-propagator-ot-trace==0.62b1 -opentelemetry-instrumentation-aio-pika==0.61b0 -opentelemetry-instrumentation-aiohttp-client==0.61b0 -opentelemetry-instrumentation-aiohttp-server==0.61b0 -opentelemetry-instrumentation-aiokafka==0.61b0 -opentelemetry-instrumentation-aiopg==0.61b0 -opentelemetry-instrumentation-asgi==0.61b0 -opentelemetry-instrumentation-asyncclick==0.61b0 -opentelemetry-instrumentation-asyncio==0.61b0 -opentelemetry-instrumentation-asyncpg==0.61b0 -opentelemetry-instrumentation-boto==0.61b0 -opentelemetry-instrumentation-boto3sqs==0.61b0 -opentelemetry-instrumentation-botocore==0.61b0 -opentelemetry-instrumentation-cassandra==0.61b0 -opentelemetry-instrumentation-celery==0.61b0 -opentelemetry-instrumentation-click==0.61b0 -opentelemetry-instrumentation-confluent-kafka==0.61b0 -opentelemetry-instrumentation-dbapi==0.61b0 -opentelemetry-instrumentation-django==0.61b0 -opentelemetry-instrumentation-elasticsearch==0.61b0 -opentelemetry-instrumentation-falcon==0.61b0 -opentelemetry-instrumentation-fastapi==0.61b0 -opentelemetry-instrumentation-flask==0.61b0 -opentelemetry-instrumentation-grpc==0.61b0 -opentelemetry-instrumentation-httpx==0.61b0 -opentelemetry-instrumentation-jinja2==0.61b0 -opentelemetry-instrumentation-kafka-python==0.61b0 -opentelemetry-instrumentation-logging==0.61b0 -opentelemetry-instrumentation-mysql==0.61b0 -opentelemetry-instrumentation-mysqlclient==0.61b0 -opentelemetry-instrumentation-pika==0.61b0 -opentelemetry-instrumentation-psycopg==0.61b0 -opentelemetry-instrumentation-psycopg2==0.61b0 -opentelemetry-instrumentation-pymemcache==0.61b0 -opentelemetry-instrumentation-pymongo==0.61b0 -opentelemetry-instrumentation-pymssql==0.61b0 -opentelemetry-instrumentation-pymysql==0.61b0 -opentelemetry-instrumentation-pyramid==0.61b0 -opentelemetry-instrumentation-redis==0.61b0 -opentelemetry-instrumentation-remoulade==0.61b0 -opentelemetry-instrumentation-requests==0.61b0 -opentelemetry-instrumentation-sqlalchemy==0.61b0 -opentelemetry-instrumentation-sqlite3==0.61b0 -opentelemetry-instrumentation-starlette==0.61b0 -opentelemetry-instrumentation-system-metrics==0.61b0 -opentelemetry-instrumentation-threading==0.61b0 -opentelemetry-instrumentation-tornado==0.61b0 -opentelemetry-instrumentation-tortoiseorm==0.61b0 -opentelemetry-instrumentation-urllib==0.61b0 -opentelemetry-instrumentation-urllib3==0.61b0 -opentelemetry-instrumentation-wsgi==0.61b0 +opentelemetry-instrumentation-aio-pika==0.62b1 +opentelemetry-instrumentation-aiohttp-client==0.62b1 +opentelemetry-instrumentation-aiohttp-server==0.62b1 +opentelemetry-instrumentation-aiokafka==0.62b1 +opentelemetry-instrumentation-aiopg==0.62b1 +opentelemetry-instrumentation-asgi==0.62b1 +opentelemetry-instrumentation-asyncclick==0.62b1 +opentelemetry-instrumentation-asyncio==0.62b1 +opentelemetry-instrumentation-asyncpg==0.62b1 +opentelemetry-instrumentation-boto3sqs==0.62b1 +opentelemetry-instrumentation-botocore==0.62b1 +opentelemetry-instrumentation-cassandra==0.62b1 +opentelemetry-instrumentation-celery==0.62b1 +opentelemetry-instrumentation-click==0.62b1 +opentelemetry-instrumentation-confluent-kafka==0.62b1 +opentelemetry-instrumentation-dbapi==0.62b1 +opentelemetry-instrumentation-django==0.62b1 +opentelemetry-instrumentation-elasticsearch==0.62b1 +opentelemetry-instrumentation-falcon==0.62b1 +opentelemetry-instrumentation-fastapi==0.62b1 +opentelemetry-instrumentation-flask==0.62b1 +opentelemetry-instrumentation-grpc==0.62b1 +opentelemetry-instrumentation-httpx==0.62b1 +opentelemetry-instrumentation-jinja2==0.62b1 +opentelemetry-instrumentation-kafka-python==0.62b1 +opentelemetry-instrumentation-logging==0.62b1 +opentelemetry-instrumentation-mysql==0.62b1 +opentelemetry-instrumentation-mysqlclient==0.62b1 +opentelemetry-instrumentation-pika==0.62b1 +opentelemetry-instrumentation-psycopg==0.62b1 +opentelemetry-instrumentation-psycopg2==0.62b1 +opentelemetry-instrumentation-pymemcache==0.62b1 +opentelemetry-instrumentation-pymongo==0.62b1 +opentelemetry-instrumentation-pymssql==0.62b1 +opentelemetry-instrumentation-pymysql==0.62b1 +opentelemetry-instrumentation-pyramid==0.62b1 +opentelemetry-instrumentation-redis==0.62b1 +opentelemetry-instrumentation-remoulade==0.62b1 +opentelemetry-instrumentation-requests==0.62b1 +opentelemetry-instrumentation-sqlalchemy==0.62b1 +opentelemetry-instrumentation-sqlite3==0.62b1 +opentelemetry-instrumentation-starlette==0.62b1 +opentelemetry-instrumentation-system-metrics==0.62b1 +opentelemetry-instrumentation-threading==0.62b1 +opentelemetry-instrumentation-tornado==0.62b1 +opentelemetry-instrumentation-tortoiseorm==0.62b1 +opentelemetry-instrumentation-urllib==0.62b1 +opentelemetry-instrumentation-urllib3==0.62b1 +opentelemetry-instrumentation-wsgi==0.62b1 elastic-opentelemetry-instrumentation-openai==1.2.0 diff --git a/pyproject.toml b/pyproject.toml index e5e66c7f..7f2596c1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -27,16 +27,16 @@ classifiers = [ ] dependencies = [ - "opentelemetry-api == 1.40.0", - "opentelemetry-exporter-otlp == 1.40.0", - "opentelemetry-instrumentation == 0.61b0", - "opentelemetry-instrumentation-system-metrics == 0.61b0", + "opentelemetry-api == 1.41.1", + "opentelemetry-exporter-otlp == 1.41.1", + "opentelemetry-instrumentation == 0.62b1", + "opentelemetry-instrumentation-system-metrics == 0.62b1", "opentelemetry-resourcedetector-gcp ~= 1.8.0a0", "opentelemetry-resource-detector-azure ~= 0.1.5", - "opentelemetry-resource-detector-containerid == 0.61b0", - "opentelemetry-sdk == 1.40.0", + "opentelemetry-resource-detector-containerid == 0.62b1", + "opentelemetry-sdk == 1.41.1", "opentelemetry-sdk-extension-aws ~= 2.1.0", - "opentelemetry-semantic-conventions == 0.61b0", + "opentelemetry-semantic-conventions == 0.62b1", "opentelemetry-opamp-client == 0.2b0", "packaging", "uuid-utils", diff --git a/src/elasticotel/distro/config.py b/src/elasticotel/distro/config.py index e00fe7c3..887ad013 100644 --- a/src/elasticotel/distro/config.py +++ b/src/elasticotel/distro/config.py @@ -36,7 +36,9 @@ from opentelemetry._opamp.proto import opamp_pb2 as opamp_pb2 from opentelemetry.sdk._logs import LoggingHandler from opentelemetry.sdk.environment_variables import OTEL_LOG_LEVEL, OTEL_TRACES_SAMPLER_ARG -from opentelemetry.sdk.trace import _TracerConfig, _TracerConfiguratorRulesT, _scope_name_matches_glob +from opentelemetry.sdk.trace import _TracerConfig +from opentelemetry.sdk.util._configurator import ConfiguratorRulesT +from opentelemetry.sdk.util.instrumentation import _scope_name_matches_glob logger = logging.getLogger(__name__) @@ -205,7 +207,7 @@ def _handle_sampling_rate(remote_config) -> ConfigUpdate: return ConfigUpdate() -def _rules_from_deactivate_instrumentations(csv: str) -> _TracerConfiguratorRulesT: +def _rules_from_deactivate_instrumentations(csv: str) -> ConfiguratorRulesT: patterns = [pattern.strip() for pattern in csv.split(",") if pattern.strip()] if not patterns: return [] @@ -226,12 +228,14 @@ def _handle_deactivate_instrumentations(remote_config) -> ConfigUpdate: rules = _rules_from_deactivate_instrumentations(config_deactivate_instrumentations) current_tracer_configurator = tracer_configurator._get_tracer_configurator() - rules_updated = current_tracer_configurator.update_rules(rules) + try: + rules_changed = current_tracer_configurator.rules_changed(rules) + except AttributeError: + rules_changed = True + current_tracer_configurator.update_rules(rules) # if the rules did not change we are fine - if not rules_updated: + if not rules_changed: return ConfigUpdate() - # when rules are updated we need to clear the cache of the tracer_configurator function - tracer_configurator._updatable_tracer_configurator.cache_clear() set_tracer_configurator(tracer_configurator=tracer_configurator._updatable_tracer_configurator) logger.debug('Updated deactivate instrumentations to "%s".', config_deactivate_instrumentations) diff --git a/src/elasticotel/sdk/trace/tracer_configurator.py b/src/elasticotel/sdk/trace/tracer_configurator.py index 0dd9651d..ed3f577a 100644 --- a/src/elasticotel/sdk/trace/tracer_configurator.py +++ b/src/elasticotel/sdk/trace/tracer_configurator.py @@ -17,38 +17,25 @@ from __future__ import annotations import inspect -from functools import lru_cache -from opentelemetry.sdk.trace import _TracerConfig, _TracerConfiguratorRulesT, _InstrumentationScopePredicateT -from opentelemetry.sdk.util.instrumentation import InstrumentationScope +from opentelemetry.sdk.trace import _TracerConfig, _RuleBasedTracerConfigurator +from opentelemetry.sdk.util._configurator import ConfiguratorRulesT +from opentelemetry.sdk.util.instrumentation import InstrumentationScope, _InstrumentationScopePredicateT -class _UpdatableRuleBasedTracerConfigurator: - """Updatatable version of what's available upstream""" +class _UpdatableRuleBasedTracerConfigurator(_RuleBasedTracerConfigurator): + """Updatatable version of what's available upstream - def __init__( - self, - *, - rules: _TracerConfiguratorRulesT, - default_config: _TracerConfig, - ): - self._rules = rules - self._default_config = default_config - - def __call__(self, tracer_scope: InstrumentationScope) -> _TracerConfig: - for predicate, tracer_config in list(self._rules): - if predicate(tracer_scope): - return tracer_config - - # if no rule matched return the default config - return self._default_config + This provides a method to know if the rules changed or not so that + we can avoid updating them at every configuration update if they haven't + changed""" @property def rules(self): return self._rules def _comparable_rules( - self, rules: _TracerConfiguratorRulesT + self, rules: ConfiguratorRulesT ) -> list[tuple[str | _InstrumentationScopePredicateT, _TracerConfig]]: """Transform the rules to be comparable""" @@ -62,26 +49,20 @@ def unpack_pattern(predicate) -> str | _InstrumentationScopePredicateT: comparable_rules = [(unpack_pattern(predicate), config) for predicate, config in rules] return comparable_rules - def update_rules(self, rules: _TracerConfiguratorRulesT) -> bool: - """Updates rules if they are different than the current ones""" - if self._comparable_rules(rules) == self._comparable_rules(self.rules): - return False - - self._rules = rules - return True + def rules_changed(self, rules: ConfiguratorRulesT) -> bool: + return self._comparable_rules(rules) != self._comparable_rules(self.rules) _tracer_configurator = _UpdatableRuleBasedTracerConfigurator(rules=[], default_config=_TracerConfig(is_enabled=True)) -def _get_tracer_configurator(): +def _get_tracer_configurator() -> _UpdatableRuleBasedTracerConfigurator: global _tracer_configurator return _tracer_configurator -@lru_cache def _updatable_tracer_configurator( tracer_scope: InstrumentationScope, ) -> _TracerConfig: tracer_configurator = _get_tracer_configurator() - return tracer_configurator(tracer_scope=tracer_scope) + return tracer_configurator(tracer_scope) diff --git a/tests/distro/test_distro.py b/tests/distro/test_distro.py index 106d3b3c..3ace960e 100644 --- a/tests/distro/test_distro.py +++ b/tests/distro/test_distro.py @@ -50,7 +50,8 @@ OTEL_TRACES_SAMPLER, OTEL_TRACES_SAMPLER_ARG, ) -from opentelemetry.sdk.trace import _TracerConfig, _scope_name_matches_glob, sampling +from opentelemetry.sdk.trace import _TracerConfig, sampling +from opentelemetry.sdk.util.instrumentation import _scope_name_matches_glob from opentelemetry.util._once import Once @@ -897,7 +898,6 @@ def test_sets_deactivate_instrumentations( client.build_full_state_message.assert_not_called() get_tracer_provider_mock.return_value._set_tracer_configurator.assert_called_once() self.assertEqual(len(rule_based_tracer_configurator.rules), 1) - _updatable_tracer_configurator.cache_clear() @mock.patch("elasticotel.distro.config._get_config") @mock.patch("elasticotel.sdk.trace.tracer_configurator._get_tracer_configurator") @@ -940,7 +940,6 @@ def test_sets_deactivate_instrumentations_with_same_rules( client.build_full_state_message.assert_not_called() get_tracer_provider_mock.return_value._set_tracer_configurator.assert_not_called() self.assertEqual(len(rule_based_tracer_configurator.rules), 1) - _updatable_tracer_configurator.cache_clear() @mock.patch("elasticotel.distro.config._get_config") @mock.patch("elasticotel.sdk.trace.tracer_configurator._get_tracer_configurator") diff --git a/tests/integration/test_integration.py b/tests/integration/test_integration.py index bd1f8bce..af2bc49b 100644 --- a/tests/integration/test_integration.py +++ b/tests/integration/test_integration.py @@ -89,8 +89,11 @@ def test_metrics_default_does_not_contain_system_metrics(self): self.assertEqual( metrics_names, [ + "otel.sdk.processor.log.queue.capacity", "otel.sdk.span.started", "otel.sdk.span.live", + "otel.sdk.processor.span.queue.size", + "otel.sdk.processor.log.queue.size", "process.runtime.cpython.memory", "process.runtime.cpython.cpu_time", "process.runtime.cpython.gc_count", @@ -119,8 +122,11 @@ def test_metrics_with_system_metrics(self): self.assertEqual( metrics_names, [ + "otel.sdk.processor.log.queue.capacity", "otel.sdk.span.started", "otel.sdk.span.live", + "otel.sdk.processor.span.queue.size", + "otel.sdk.processor.log.queue.size", "system.cpu.time", "system.cpu.utilization", "system.memory.usage",