diff --git a/.github/workflows/generate_workflows_lib/src/generate_workflows_lib/__init__.py b/.github/workflows/generate_workflows_lib/src/generate_workflows_lib/__init__.py index 1d61a3daf..d322f02b8 100644 --- a/.github/workflows/generate_workflows_lib/src/generate_workflows_lib/__init__.py +++ b/.github/workflows/generate_workflows_lib/src/generate_workflows_lib/__init__.py @@ -143,7 +143,7 @@ def get_contrib_job_datas(tox_envs: list) -> list: contrib_job_datas.append( { - "ui_name": (f"{groups['name']}" f"{contrib_requirements}"), + "ui_name": (f"{groups['name']}{contrib_requirements}"), "tox_env": tox_env, } ) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 5b9bf9973..01486fbda 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,7 +1,7 @@ repos: - repo: https://github.com/astral-sh/ruff-pre-commit # Ruff version. - rev: v0.6.9 + rev: v0.14.1 hooks: # Run the linter. - id: ruff diff --git a/CHANGELOG-loongsuite.md b/CHANGELOG-loongsuite.md new file mode 100644 index 000000000..2795145f0 --- /dev/null +++ b/CHANGELOG-loongsuite.md @@ -0,0 +1,17 @@ +# Changelog for LoongSuite + +All notable changes to loongsuite components will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +> [!NOTE] +> The following components are released independently and maintain individual CHANGELOG files. +> Use [this search for a list of all CHANGELOG-loongsuite.md files in this repo](https://github.com/search?q=repo%3Aalibaba%2Floongsuite-python-agent+path%3A**%2FCHANGELOG-loongsuite.md&type=code). + +## Unreleased + +# Added + +- `loongsuite-instrumentation-mem0`: add support for mem0 + ([#67](https://github.com/alibaba/loongsuite-python-agent/pull/67)) diff --git a/CHANGELOG-upstream.md b/CHANGELOG-upstream.md deleted file mode 100644 index b36b689b6..000000000 --- a/CHANGELOG-upstream.md +++ /dev/null @@ -1,2197 +0,0 @@ -# Changelog - -All notable changes to this project will be documented in this file. - -The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), -and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). - -> [!NOTE] -> The following components are released independently and maintain individual CHANGELOG files. -> Use [this search for a list of all CHANGELOG.md files in this repo](https://github.com/search?q=repo%3Aopen-telemetry%2Fopentelemetry-python-contrib+path%3A**%2FCHANGELOG.md&type=code). - -## Unreleased - -### Added - -- `opentelemetry-instrumentation-aiohttp-client`: add support for url exclusions via `OTEL_PYTHON_EXCLUDED_URLS` / `OTEL_PYTHON_AIOHTTP_CLIENT_EXCLUDED_URLS` - ([#3850](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3850)) -- `opentelemetry-instrumentation-httpx`: add support for url exclusions via `OTEL_PYTHON_EXCLUDED_URLS` / `OTEL_PYTHON_HTTPX_EXCLUDED_URLS` - ([#3837](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3837)) -- `opentelemetry-instrumentation-flask`: improve readthedocs for sqlcommenter configuration. - ([#3883](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3883)) -- `opentelemetry-instrumentation-sqlalchemy`: improve readthedocs for sqlcommenter configuration. - ([#3886](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3886)) -- `opentelemetry-instrumentation-mysql`, `opentelemetry-instrumentation-mysqlclient`, `opentelemetry-instrumentation-pymysql`: improve readthedocs for sqlcommenter configuration. - ([#3885](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3885)) -- `opentelemetry-instrumentation-django`: improve readthedocs for sqlcommenter configuration. - ([#3884](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3884)) -- `opentelemetry-instrumentation-aiohttp-server`: add support for custom header captures via `OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_REQUEST` and `OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_RESPONSE` - ([#3916](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3916)) - -### Fixed - -- `opentelemetry-instrumentation-botocore`: bedrock: only decode JSON input buffer in Anthropic Claude streaming - ([#3875](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3875)) -- `opentelemetry-instrumentation-aiohttp-client`, `opentelemetry-instrumentation-aiohttp-server`: Fix readme links and text - ([#3902](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3902)) -- `opentelemetry-instrumentation-aws-lambda`: Fix ImportError with slash-delimited handler paths - ([#3894](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3894)) -- `opentelemetry-exporter-richconsole`: Prevent deadlock when parent span is not part of the batch - ([#3900](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3900)) -- `opentelemetry-instrumentation-psycopg2`, `opentelemetry-instrumentation-psycopg`: improve readthedocs for sqlcommenter configuration. - ([#3882](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3882)) -- `opentelemetry-instrumentation-aiohttp-server`: delay initialization of tracer, meter and excluded urls to instrumentation for testability - ([#3836](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3836)) -- `opentelemetry-instrumentation-elasticsearch`: Enhance elasticsearch query body sanitization - ([#3919](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3919)) - - -## Version 1.38.0/0.59b0 (2025-10-16) - -### Fixed - -- `opentelemetry-instrumentation-flask`: Do not record `http.server.duration` metrics for excluded URLs. - ([#3794](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3794)) -- `opentelemetry-instrumentation-botocore`: migrate off the deprecated events API to use the logs API - ([#3624](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3624)) -- `opentelemetry-instrumentation-dbapi`: fix crash retrieving libpq version when enabling commenter with psycopg - ([#3796](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3796)) -- `opentelemetry-instrumentation-fastapi`: Fix handling of APIRoute subclasses - ([#3681](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3681)) - -### Added - -- `opentelemetry-instrumentation-botocore`: Add support for AWS Secrets Manager semantic convention attribute - ([#3765](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3765)) -- `opentelemetry-instrumentation-dbapi`: Add support for `commenter_options` in `trace_integration` function to control SQLCommenter behavior - ([#3743](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3743)) -- Add `rstcheck` to pre-commit to stop introducing invalid RST - ([#3777](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3777)) -- `opentelemetry-exporter-credential-provider-gcp`: create this package which provides support for supplying your machine's Application Default - Credentials (https://cloud.google.com/docs/authentication/application-default-credentials) to the OTLP Exporters created automatically by OpenTelemetry Python's auto instrumentation. These credentials authorize OTLP traces to be sent to `telemetry.googleapis.com`. [#3766](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3766). -- `opentelemetry-instrumentation-psycopg`: Add missing parameter `capture_parameters` to instrumentor. - ([#3676](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3676)) -- `opentelemetry-instrumentation-dbapi`: Adds sqlcommenter to documentation. - ([#3720](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3720)) - -## Version 1.37.0/0.58b0 (2025-09-11) - -### Fixed - -- `opentelemetry-instrumentation-fastapi`: Fix middleware ordering to cover all exception handling use cases. - ([#3664](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3664)) -- `opentelemetry-instrumentation-asgi`: Make all user hooks failsafe and record exceptions in hooks. - ([#3664](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3664)) -- `opentelemetry-instrumentation-fastapi`: Fix memory leak in `uninstrument_app()` by properly removing apps from the tracking set - ([#3688](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3688)) -- `opentelemetry-instrumentation-tornado` Fix server (request) duration metric calculation - ([#3679](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3679)) -- `opentelemetry-instrumentation-tornado`: Fix to properly skip all server telemetry when URL excluded. - ([#3680](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3680)) -- `opentelemetry-instrumentation`: Avoid calls to `context.detach` with `None` token. - ([#3673](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3673)) -- `opentelemetry-instrumentation-starlette`/`opentelemetry-instrumentation-fastapi`: Fixes a crash when host-based routing is used - ([#3507](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3507)) -- Fix documentation order of sections and headers for Django, Flask, MySQL, mysqlclient, psycopg, psycopg2, pymysql, sqlalchemy instrumentations. - ([#3719](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3719)) -- `opentelemetry-instrumentation-asgi` Fixed an issue where FastAPI reports IP instead of URL. - ([#3670](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3670)) -- `opentelemetry-instrumentation-httpx`: fix missing metric response attributes when tracing is disabled - ([#3615](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3615)) -- `opentelemetry-instrumentation-fastapi`: Don't pass bounded server_request_hook when using `FastAPIInstrumentor.instrument()` - ([#3701](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3701)) - -### Added - -- `opentelemetry-instrumentation-confluent-kafka` Add support for confluent-kafka <=2.11.0 - ([#3685](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3685)) -- `opentelemetry-instrumentation-system-metrics`: Add `cpython.gc.collected_objects` and `cpython.gc.uncollectable_objects` metrics - ([#3666](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3666)) -- `opentelemetry-sdk-extension-aws` Add AWS X-Ray Remote Sampler with initial Rules Poller implementation - ([#3366](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3366)) -- `opentelemetry-instrumentation`: add support for `OTEL_PYTHON_AUTO_INSTRUMENTATION_EXPERIMENTAL_GEVENT_PATCH` to inform opentelemetry-instrument about gevent monkeypatching - ([#3699](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3699)) -- `opentelemetry-instrumentation`: botocore: Add support for AWS Step Functions semantic convention attributes - ([#3737](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3737)) -- `opentelemetry-instrumentation-botocore`: Add support for SNS semantic convention attribute aws.sns.topic.arn - ([#3734](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3734)) -- `opentelemetry-instrumentation`: botocore: upgrade moto package from 5.0.9 to 5.1.11 - ([#3736](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3736)) - -## Version 1.36.0/0.57b0 (2025-07-29) - -### Fixed - -- `opentelemetry-instrumentation`: Fix dependency conflict detection when instrumented packages are not installed by moving check back to before instrumentors are loaded. Add "instruments-any" feature for instrumentations that target multiple packages. - ([#3610](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3610)) -- infra(ci): Fix git pull failures in core contrib test - ([#3357](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3357)) - -### Added - -- `opentelemetry-instrumentation-psycopg2` Utilize instruments-any functionality. - ([#3610](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3610)) -- `opentelemetry-instrumentation-kafka-python` Utilize instruments-any functionality. - ([#3610](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3610)) -- `opentelemetry-instrumentation-system-metrics`: Add `cpython.gc.collections` metrics with collection unit is specified in semconv ([3617](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3617)) - -## Version 1.35.0/0.56b0 (2025-07-11) - -### Added - -- `opentelemetry-instrumentation-pika` Added instrumentation for All `SelectConnection` adapters - ([#3584](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3584)) -- `opentelemetry-instrumentation-tornado` Add support for `WebSocketHandler` instrumentation - ([#3498](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3498)) -- `opentelemetry-util-http` Added support for redacting specific url query string values and url credentials in instrumentations - ([#3508](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3508)) -- `opentelemetry-instrumentation-pymongo` `aggregate` and `getMore` capture statements support - ([#3601](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3601)) - -### Fixed - -- `opentelemetry-instrumentation-asgi`: fix excluded_urls in instrumentation-asgi - ([#3567](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3567)) -- `opentelemetry-resource-detector-containerid`: make it more quiet on platforms without cgroups - ([#3579](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3579)) - -## Version 1.34.0/0.55b0 (2025-06-04) - -### Fixed - -- `opentelemetry-instrumentation-system-metrics`: fix loading on Google Cloud Run - ([#3533](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3533)) -- `opentelemetry-instrumentation-fastapi`: fix wrapping of middlewares - ([#3012](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3012)) -- `opentelemetry-instrumentation-starlette` Remove max version constraint on starlette - ([#3456](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3456)) -- `opentelemetry-instrumentation-starlette` Fix memory leak and double middleware - ([#3529](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3529)) -- `opentelemetry-instrumentation-urllib3`: proper bucket boundaries in stable semconv http duration metrics - ([#3518](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3518)) -- `opentelemetry-instrumentation-urllib`: proper bucket boundaries in stable semconv http duration metrics - ([#3519](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3519)) -- `opentelemetry-instrumentation-falcon`: proper bucket boundaries in stable semconv http duration - ([#3525](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3525)) -- `opentelemetry-instrumentation-wsgi`: add explicit http duration buckets for stable semconv - ([#3527](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3527)) -- `opentelemetry-instrumentation-asgi`: add explicit http duration buckets for stable semconv - ([#3526](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3526)) -- `opentelemetry-instrumentation-flask`: proper bucket boundaries in stable semconv http duration - ([#3523](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3523)) -- `opentelemetry-instrumentation-django`: proper bucket boundaries in stable semconv http duration - ([#3524](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3524)) -- `opentelemetry-instrumentation-grpc`: support non-list interceptors - ([#3520](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3520)) -- `opentelemetry-instrumentation-botocore` Ensure spans end on early stream closure for Bedrock Streaming APIs - ([#3481](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3481)) -- `opentelemetry-instrumentation-sqlalchemy` Respect suppress_instrumentation functionality - ([#3477](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3477)) -- `opentelemetry-instrumentation-botocore`: fix handling of tool input in Bedrock ConverseStream - ([#3544](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3544)) -- `opentelemetry-instrumentation-botocore` Add type check when extracting tool use from Bedrock request message content - ([#3548](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3548)) -- `opentelemetry-instrumentation-dbapi` Respect suppress_instrumentation functionality ([#3460](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3460)) -- `opentelemetry-resource-detector-container` Correctly parse container id when using systemd and cgroupsv1 - ([#3429](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3429)) - -### Breaking changes - -- `opentelemetry-instrumentation-botocore` Use `cloud.region` instead of `aws.region` span attribute as per semantic conventions. - ([#3474](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3474)) -- `opentelemetry-instrumentation-fastapi`: Drop support for FastAPI versions earlier than `0.92` - ([#3012](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3012)) -- `opentelemetry-resource-detector-container`: rename package name to `opentelemetry-resource-detector-containerid` - ([#3536](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3536)) - -### Added - -- `opentelemetry-instrumentation-aiohttp-client` Add support for HTTP metrics - ([#3517](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3517)) -- `opentelemetry-instrumentation-httpx` Add support for HTTP metrics - ([#3513](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3513)) -- `opentelemetry-instrumentation` Allow re-raising exception when instrumentation fails - ([#3545](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3545)) -- `opentelemetry-instrumentation-aiokafka` Add instrumentation of `consumer.getmany` (batch) - ([#3257](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3257)) - -### Deprecated - -- Drop support for Python 3.8, bump baseline to Python 3.9. - ([#3399](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3399)) - -## Version 1.33.0/0.54b0 (2025-05-09) - -### Added - -- `opentelemetry-instrumentation-requests` Support explicit_bucket_boundaries_advisory in duration metrics - ([#3464](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3464)) -- `opentelemetry-instrumentation-redis` Add support for redis client-specific instrumentation. - ([#3143](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3143)) - -### Fixed - -- `opentelemetry-instrumentation` Catch `ModuleNotFoundError` when the library is not installed - and log as debug instead of exception - ([#3423](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3423)) -- `opentelemetry-instrumentation-asyncio` Fix duplicate instrumentation - ([#3383](https://github.com/open-telemetry/opentelemetry-python-contrib/issues/3383)) -- `opentelemetry-instrumentation-botocore` Add GenAI instrumentation for additional Bedrock models for InvokeModel API - ([#3419](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3419)) -- `opentelemetry-instrumentation` don't print duplicated conflict log error message - ([#3432](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3432)) -- `opentelemetry-instrumentation-grpc` Check for None result in gRPC - ([#3380](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3381)) -- `opentelemetry-instrumentation-[asynclick/click]` Add missing opentelemetry-instrumentation dep - ([#3447](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3447)) -- `opentelemetry-instrumentation-botocore` Capture server attributes for botocore API calls - ([#3448](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3448)) - -## Version 1.32.0/0.53b0 (2025-04-10) - -### Added - -- `opentelemetry-instrumentation-asyncclick`: new instrumentation to trace asyncclick commands - ([#3319](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3319)) -- `opentelemetry-instrumentation-botocore` Add support for GenAI tool events using Amazon Nova models and `InvokeModel*` APIs - ([#3385](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3385)) -- `opentelemetry-instrumentation` Make auto instrumentation use the same dependency resolver as manual instrumentation does - ([#3202](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3202)) - -### Fixed - -- `opentelemetry-instrumentation` Fix client address is set to server address in new semconv - ([#3354](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3354)) -- `opentelemetry-instrumentation-dbapi`, `opentelemetry-instrumentation-django`, - `opentelemetry-instrumentation-sqlalchemy`: Fix sqlcomment for non string query and composable object. - ([#3113](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3113)) -- `opentelemetry-instrumentation-grpc` Fix error when using gprc versions <= 1.50.0 with unix sockets. - ([[#3393](https://github.com/open-telemetry/opentelemetry-python-contrib/issues/3393)]) -- `opentelemetry-instrumentation-asyncio` Fix duplicate instrumentation. - ([[#3383](https://github.com/open-telemetry/opentelemetry-python-contrib/issues/3383)]) -- `opentelemetry-instrumentation-aiokafka` Fix send_and_wait method no headers kwargs error. - ([[#3332](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3332)]) - -## Version 1.31.0/0.52b0 (2025-03-12) - -### Added - -- `opentelemetry-instrumentation-openai-v2` Update doc for OpenAI Instrumentation to support OpenAI Compatible Platforms - ([#3279](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3279)) -- `opentelemetry-instrumentation-system-metrics` Add `process` metrics and deprecated `process.runtime` prefixed ones - ([#3250](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3250)) -- `opentelemetry-instrumentation-botocore` Add support for GenAI user events and lazy initialize tracer - ([#3258](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3258)) -- `opentelemetry-instrumentation-botocore` Add support for GenAI system events - ([#3266](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3266)) -- `opentelemetry-instrumentation-botocore` Add support for GenAI choice events - ([#3275](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3275)) -- `opentelemetry-instrumentation-botocore` Add support for GenAI tool events - ([#3302](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3302)) -- `opentelemetry-instrumentation-botocore` Add support for GenAI metrics - ([#3326](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3326)) -- `opentelemetry-instrumentation` make it simpler to initialize auto-instrumentation programmatically - ([#3273](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3273)) -- Add `opentelemetry-instrumentation-vertexai>=2.0b0` to `opentelemetry-bootstrap` - ([#3307](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3307)) -- Loosen `opentelemetry-instrumentation-starlette[instruments]` specifier - ([#3304](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3304)) - -### Fixed - -- `opentelemetry-instrumentation-redis` Add missing entry in doc string for `def _instrument` - ([#3247](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3247)) -- `opentelemetry-instrumentation-botocore` sns-extension: Change destination name attribute - to match topic ARN and redact phone number from attributes - ([#3249](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3249)) -- `opentelemetry-instrumentation-asyncpg` Fix fallback for empty queries. - ([#3253](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3253)) -- `opentelemetry-instrumentation` Fix a traceback in sqlcommenter when psycopg connection pooling is enabled. - ([#3309](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3309)) -- `opentelemetry-instrumentation-threading` Fix broken context typehints - ([#3322](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3322)) -- `opentelemetry-instrumentation-requests` always record span status code in duration metric - ([#3323](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3323)) - -## Version 1.30.0/0.51b0 (2025-02-03) - -### Added - -- `opentelemetry-instrumentation-confluent-kafka` Add support for confluent-kafka <=2.7.0 - ([#3100](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3100)) -- Add support to database stability opt-in in `_semconv` utilities and add tests - ([#3111](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3111)) -- `opentelemetry-instrumentation-urllib` Add `py.typed` file to enable PEP 561 - ([#3131](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3131)) -- `opentelemetry-opentelemetry-pymongo` Add `py.typed` file to enable PEP 561 - ([#3136](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3136)) -- `opentelemetry-opentelemetry-requests` Add `py.typed` file to enable PEP 561 - ([#3135](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3135)) -- `opentelemetry-instrumentation-system-metrics` Add `py.typed` file to enable PEP 561 - ([#3132](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3132)) -- `opentelemetry-opentelemetry-sqlite3` Add `py.typed` file to enable PEP 561 - ([#3133](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3133)) -- `opentelemetry-instrumentation-falcon` add support version to v4 - ([#3086](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3086)) -- `opentelemetry-instrumentation-falcon` Implement new HTTP semantic convention opt-in for Falcon - ([#2790](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2790)) -- `opentelemetry-instrumentation-wsgi` always record span status code to have it available in metrics - ([#3148](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3148)) -- add support to Python 3.13 - ([#3134](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3134)) -- `opentelemetry-opentelemetry-wsgi` Add `py.typed` file to enable PEP 561 - ([#3129](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3129)) -- `opentelemetry-util-http` Add `py.typed` file to enable PEP 561 - ([#3127](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3127)) -- `opentelemetry-instrumentation-psycopg2` Add support for psycopg2-binary - ([#3186](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3186)) -- `opentelemetry-opentelemetry-botocore` Add basic support for GenAI attributes for AWS Bedrock Converse API - ([#3161](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3161)) -- `opentelemetry-opentelemetry-botocore` Add basic support for GenAI attributes for AWS Bedrock InvokeModel API - ([#3200](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3200)) -- `opentelemetry-opentelemetry-botocore` Add basic support for GenAI attributes for AWS Bedrock ConverseStream API - ([#3204](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3204)) -- `opentelemetry-opentelemetry-botocore` Add basic support for GenAI attributes for AWS Bedrock InvokeModelWithStreamResponse API - ([#3206](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3206)) -- `opentelemetry-instrumentation-pymssql` Add pymssql instrumentation - ([#394](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/394)) -- `opentelemetry-instrumentation-mysql` Add sqlcommenter support - ([#3163](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3163)) - -### Fixed - -- `opentelemetry-instrumentation-httpx` Fix `RequestInfo`/`ResponseInfo` type hints - ([#3105](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3105)) -- `opentelemetry-instrumentation-dbapi` Move `TracedCursorProxy` and `TracedConnectionProxy` to the module level - ([#3068](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3068)) -- `opentelemetry-instrumentation-click` Disable tracing of well-known server click commands - ([#3174](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3174)) -- `opentelemetry-instrumentation` Fix `get_dist_dependency_conflicts` if no distribution requires - ([#3168](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3168)) - -### Breaking changes - -- `opentelemetry-exporter-prometheus-remote-write` updated protobuf required version from 4.21 to 5.26 and regenerated protobufs - ([#3219](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3219)) -- `opentelemetry-instrumentation-sqlalchemy` including sqlcomment in `db.statement` span attribute value is now opt-in - ([#3112](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3112)) -- `opentelemetry-instrumentation-dbapi` including sqlcomment in `db.statement` span attribute value is now opt-in - ([#3115](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3115)) -- `opentelemetry-instrumentation-psycopg2`, `opentelemetry-instrumentation-psycopg`, `opentelemetry-instrumentation-mysqlclient`, `opentelemetry-instrumentation-pymysql`: including sqlcomment in `db.statement` span attribute value is now opt-in - ([#3121](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3121)) - -## Version 1.29.0/0.50b0 (2024-12-11) - -### Added - -- `opentelemetry-instrumentation-starlette` Add type hints to the instrumentation - ([#3045](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3045)) -- `opentelemetry-distro` default to OTLP log exporter. - ([#3042](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3042)) -- `opentelemetry-instrumentation-sqlalchemy` Update unit tests to run with SQLALchemy 2 - ([#2976](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2976)) -- Add `opentelemetry-instrumentation-openai-v2` to `opentelemetry-bootstrap` - ([#2996](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2996)) -- `opentelemetry-instrumentation-sqlalchemy` Add sqlcomment to `db.statement` attribute - ([#2937](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2937)) -- `opentelemetry-instrumentation-dbapi` Add sqlcomment to `db.statement` attribute - ([#2935](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2935)) -- `opentelemetry-instrumentation-dbapi` instrument_connection accepts optional connect_module - ([#3027](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3027)) -- `opentelemetry-instrumentation-mysqlclient` Add sqlcommenter support - ([#2941](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2941)) -- `opentelemetry-instrumentation-pymysql` Add sqlcommenter support - ([#2942](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2942)) -- `opentelemetry-instrumentation-click`: new instrumentation to trace click commands - ([#2994](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2994)) - -### Fixed - -- `opentelemetry-instrumentation-starlette`: Retrieve `meter_provider` key instead of `_meter_provider` on `_instrument` - ([#3048](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3048)) -- `opentelemetry-instrumentation-httpx`: instrument_client is a static method again - ([#3003](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3003)) -- `opentelemetry-instrumentation-system_metrics`: fix callbacks reading wrong config - ([#3025](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3025)) -- `opentelemetry-instrumentation-httpx`: Check if mount transport is none before wrap it - ([#3022](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3022)) -- Replace all instrumentor unit test `assertEqualSpanInstrumentationInfo` calls with `assertEqualSpanInstrumentationScope` calls - ([#3037](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3037)) -- `opentelemetry-instrumentation-sqlalchemy` Fixes engines from `sqlalchemy.engine_from_config` not being fully instrumented - ([#2816](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2816)) -- `opentelemetry-instrumentation-sqlalchemy`: Fix a remaining memory leak in EngineTracer - ([#3053](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3053)) -- `opentelemetry-instrumentation-sqlite3`: Update documentation on explicit cursor support of tracing - ([#3088](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3088)) - -### Breaking changes - -- `opentelemetry-instrumentation-sqlalchemy` teach instruments version - ([#2971](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2971)) -- Drop `opentelemetry-instrumentation-test` package from default instrumentation list - ([#2969](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2969)) -- `opentelemetry-instrumentation-httpx`: remove private unused `_InstrumentedClient` and `_InstrumentedAsyncClient` classes - ([#3036](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3036)) - -## Version 1.28.0/0.49b0 (2024-11-05) - -### Added - -- `opentelemetry-instrumentation-openai-v2` Instrumentation for OpenAI >= 0.27.0 - ([#2759](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2759)) -- `opentelemetry-instrumentation-fastapi` Add autoinstrumentation mechanism tests. - ([#2860](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2860)) -- `opentelemetry-instrumentation-aiokafka` Add instrumentor and auto instrumentation support for aiokafka - ([#2082](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2082)) -- `opentelemetry-instrumentation-redis` Add additional attributes for methods create_index and search, rename those spans - ([#2635](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2635)) -- `opentelemetry-instrumentation` Add support for string based dotted module paths in unwrap - ([#2919](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2919)) - -### Fixed - -- `opentelemetry-instrumentation-aiokafka` Wrap `AIOKafkaConsumer.getone()` instead of `AIOKafkaConsumer.__anext__` - ([#2874](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2874)) -- `opentelemetry-instrumentation-confluent-kafka` Fix to allow `topic` to be extracted from `kwargs` in `produce()` - ([#2901])(https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2901) -- `opentelemetry-instrumentation-system-metrics` Update metric units to conform to UCUM conventions. - ([#2922](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2922)) -- `opentelemetry-instrumentation-celery` Don't detach context without a None token - ([#2927](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2927)) -- `opentelemetry-exporter-prometheus-remote-write`: sort labels before exporting - ([#2940](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2940)) -- `opentelemetry-instrumentation-dbapi` sqlcommenter key values created from PostgreSQL, MySQL systems - ([#2897](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2897)) -- `opentelemetry-instrumentation-system-metrics`: don't report open file descriptors on Windows - ([#2946](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2946)) - -### Breaking changes - -- Deprecation of pkg_resource in favor of importlib.metadata - ([#2871](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2871)) -- `opentelemetry-instrumentation` Don't fail distro loading if instrumentor raises ImportError, instead skip them - ([#2923](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2923)) -- `opentelemetry-instrumentation-httpx` Rewrote instrumentation to use wrapt instead of subclassing - ([#2909](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2909)) - -## Version 1.27.0/0.48b0 (2024-08-28) - -### Added - -- `opentelemetry-instrumentation-kafka-python` Instrument temporary fork, kafka-python-ng inside kafka-python's instrumentation - ([#2537](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2537)) -- `opentelemetry-instrumentation-asgi`, `opentelemetry-instrumentation-fastapi` Add ability to disable internal HTTP send and receive spans - ([#2802](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2802)) -- `opentelemetry-instrumentation-asgi` Add fallback decoding for ASGI headers - ([#2837](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2837)) - -### Breaking changes - -- `opentelemetry-bootstrap` Remove `opentelemetry-instrumentation-aws-lambda` from the defaults instrumentations - ([#2786](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2786)) - -### Fixed - -- `opentelemetry-instrumentation-httpx` fix handling of async hooks - ([#2823](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2823)) -- `opentelemetry-instrumentation-system-metrics` fix `process.runtime.cpu.utilization` values to be shown in range of 0 to 1 - ([#2812](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2812)) -- `opentelemetry-instrumentation-fastapi` fix `fastapi` auto-instrumentation by removing `fastapi-slim` support, `fastapi-slim` itself is discontinued from maintainers - ([#2783](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2783)) -- `opentelemetry-instrumentation-aws-lambda` Avoid exception when a handler is not present. - ([#2750](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2750)) -- `opentelemetry-instrumentation-django` Fix regression - `http.target` re-added back to old semconv duration metrics - ([#2746](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2746)) -- `opentelemetry-instrumentation-asgi` do not set `url.full` attribute for server spans - ([#2735](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2735)) -- `opentelemetry-instrumentation-grpc` Fixes the issue with the gRPC instrumentation not working with the 1.63.0 and higher version of gRPC - ([#2483](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2484)) -- `opentelemetry-instrumentation-aws-lambda` Fixing w3c baggage support - ([#2589](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2589)) -- `opentelemetry-instrumentation-celery` propagates baggage - ([#2385](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2385)) -- `opentelemetry-instrumentation-asyncio` Fixes async generator coroutines not being awaited - ([#2792](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2792)) -- `opentelemetry-instrumentation-tornado` Handle http client exception and record exception info into span - ([#2563](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2563)) -- `opentelemetry-instrumentation` fix `http.host` new http semantic convention mapping to depend on `kind` of span - ([#2814](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2814)) -- `opentelemetry-instrumentation` Fix the description of `http.server.duration` and `http.server.request.duration` - ([#2753](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2753)) -- `opentelemetry-instrumentation-grpc` Fix grpc supported version - ([#2845](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2845)) -- `opentelemetry-instrumentation-asyncio` fix `AttributeError` in - `AsyncioInstrumentor.trace_to_thread` when `func` is a `functools.partial` instance - ([#2911](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2911)) - -## Version 1.26.0/0.47b0 (2024-07-23) - -### Added - -- `opentelemetry-instrumentation-flask` Add `http.route` and `http.target` to metric attributes - ([#2621](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2621)) -- `opentelemetry-instrumentation-aws-lambda` Enable global propagator for AWS instrumentation - ([#2708](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2708)) -- `opentelemetry-instrumentation-sklearn` Deprecated the sklearn instrumentation - ([#2708](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2708)) -- `opentelemetry-instrumentation-pyramid` Record exceptions raised when serving a request - ([#2622](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2622)) -- `opentelemetry-sdk-extension-aws` Add AwsXrayLambdaPropagator - ([#2573](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2573)) -- `opentelemetry-instrumentation-confluent-kafka` Add support for version 2.4.0 of confluent_kafka - ([#2616](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2616)) -- `opentelemetry-instrumentation-asyncpg` Add instrumentation to cursor based queries - ([#2501](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2501)) -- `opentelemetry-instrumentation-confluent-kafka` Add support for produce purge - ([#2638](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2638)) -- `opentelemetry-instrumentation-asgi` Implement new semantic convention opt-in with stable http semantic conventions - ([#2610](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2610)) -- `opentelemetry-instrumentation-fastapi` Implement new semantic convention opt-in with stable http semantic conventions - ([#2682](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2682)) -- `opentelemetry-instrumentation-httpx` Implement new semantic convention opt-in migration with stable http semantic conventions - ([#2631](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2631)) -- `opentelemetry-instrumentation-system-metrics` Permit to use psutil 6.0+. - ([#2630](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2630)) -- `opentelemetry-instrumentation-system-metrics` Add support for capture open file descriptors - ([#2652](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2652)) -- `opentelemetry-instrumentation-httpx` Add support for instrument client with proxy - ([#2664](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2664)) -- `opentelemetry-instrumentation-aiohttp-client` Implement new semantic convention opt-in migration - ([#2673](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2673)) -- `opentelemetry-instrumentation-django` Add `http.target` to Django duration metric attributes - ([#2624](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2624)) -- `opentelemetry-instrumentation-urllib3` Implement new semantic convention opt-in migration - ([#2715](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2715)) -- `opentelemetry-instrumentation-django` Implement new semantic convention opt-in with stable http semantic conventions - ([#2714](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2714)) -- `opentelemetry-instrumentation-urllib` Implement new semantic convention opt-in migration - ([#2736](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2736)) - -### Breaking changes - -- `opentelemetry-instrumentation-asgi`, `opentelemetry-instrumentation-fastapi`, `opentelemetry-instrumentation-starlette` Use `tracer` and `meter` of originating components instead of one from `asgi` middleware - ([#2580](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2580)) -- Populate `{method}` as `HTTP` on `_OTHER` methods from scope for `asgi` middleware - ([#2610](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2610)) -- Populate `{method}` as `HTTP` on `_OTHER` methods from scope for `fastapi` middleware - ([#2682](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2682)) -- `opentelemetry-instrumentation-urllib3` Populate `{method}` as `HTTP` on `_OTHER` methods for span name - ([#2715](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2715)) -- Populate `{method}` as `HTTP` on `_OTHER` methods from scope for `fastapi` instrumentation - ([#2682](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2682)) -- Populate `{method}` as `HTTP` on `_OTHER` methods from scope for `django` middleware - ([#2714](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2714)) -- Populate `{method}` as `HTTP` on `_OTHER` methods from scope for `urllib` instrumentation - ([#2736](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2736)) -- `opentelemetry-instrumentation-httpx`, `opentelemetry-instrumentation-aiohttp-client`, - `opentelemetry-instrumentation-requests` Populate `{method}` as `HTTP` on `_OTHER` methods - ([#2726](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2726)) -- `opentelemetry-instrumentation-fastapi` Add dependency support for fastapi-slim - ([#2702](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2702)) -- `opentelemetry-instrumentation-urllib3` improve request_hook, replacing `headers` and `body` parameters with a single `request_info: RequestInfo` parameter that now contains the `method` and `url` ([#2711](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2711)) - -### Fixed - -- Handle `redis.exceptions.WatchError` as a non-error event in redis instrumentation - ([#2668](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2668)) -- `opentelemetry-instrumentation-httpx` Ensure httpx.get or httpx.request like methods are instrumented - ([#2538](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2538)) -- Add Python 3.12 support - ([#2572](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2572)) -- `opentelemetry-instrumentation-aiohttp-server`, `opentelemetry-instrumentation-httpx` Ensure consistently use of suppress_instrumentation utils - ([#2590](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2590)) -- Reference symbols from generated semantic conventions - ([#2611](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2611)) -- `opentelemetry-instrumentation-psycopg` Bugfix: Handle empty statement. - ([#2644](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2644)) -- `opentelemetry-instrumentation-confluent-kafka` Confluent Kafka: Ensure consume span is ended when consumer is closed - ([#2640](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2640)) -- `opentelemetry-instrumentation-asgi` Fix generation of `http.target` and `http.url` attributes for ASGI apps - using sub apps - ([#2477](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2477)) -- `opentelemetry-instrumentation-aws-lambda` Bugfix: AWS Lambda event source key incorrect for SNS in instrumentation library. - ([#2612](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2612)) -- `opentelemetry-instrumentation-asyncio` instrumented `asyncio.wait_for` properly raises `asyncio.TimeoutError` as expected - ([#2637](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2637)) -- `opentelemetry-instrumentation-django` Handle exceptions from request/response hooks - ([#2153](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2153)) -- `opentelemetry-instrumentation-asgi` Removed `NET_HOST_NAME` AND `NET_HOST_PORT` from active requests count attribute - ([#2610](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2610)) -- `opentelemetry-instrumentation-asgi` Bugfix: Middleware did not set status code attribute on duration metrics for non-recording spans. - ([#2627](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2627)) -- `opentelemetry-instrumentation-mysql` Add support for `mysql-connector-python` v9 - ([#2751](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2751)) - -## Version 1.25.0/0.46b0 (2024-05-31) - -### Breaking changes - -- Add return statement to Confluent kafka Producer poll() and flush() calls when instrumented by ConfluentKafkaInstrumentor().instrument_producer() ([#2527](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2527)) -- Rename `type` attribute to `asgi.event.type` in `opentelemetry-instrumentation-asgi` - ([#2300](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2300)) -- Rename AwsLambdaInstrumentor span attributes `faas.id` to `cloud.resource_id`, `faas.execution` to `faas.invocation_id` - ([#2372](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2372)) -- Drop support for instrumenting elasticsearch client < 6 - ([#2422](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2422)) -- `opentelemetry-instrumentation-wsgi` Add `http.method` to `span.name` - ([#2425](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2425)) -- `opentelemetry-instrumentation-flask` Add `http.method` to `span.name` - ([#2454](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2454)) -- Record repeated HTTP headers in lists, rather than a comma separate strings for ASGI based web frameworks - ([#2361](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2361)) -- ASGI, FastAPI, Starlette: provide both send and receive hooks with `scope` and `message` for internal spans -- ([#2546](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2546)) - -### Added - -- `opentelemetry-sdk-extension-aws` Register AWS resource detectors under the - `opentelemetry_resource_detector` entry point - ([#2382](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2382)) -- `opentelemetry-instrumentation-wsgi` Implement new semantic convention opt-in with stable http semantic conventions - ([#2425](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2425)) -- `opentelemetry-instrumentation-flask` Implement new semantic convention opt-in with stable http semantic conventions - ([#2454](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2454)) -- `opentelemetry-instrumentation-threading` Initial release for threading - ([#2253](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2253)) -- `opentelemetry-instrumentation-pika` Instrumentation for `channel.consume()` (supported - only for global, non channel specific instrumentation) - ([#2397](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2397)) -- `opentelemetry-processor-baggage` Initial release - ([#2436](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2436)) -- `opentelemetry-processor-baggage` Add baggage key predicate - ([#2535](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2535)) - -### Fixed - -- `opentelemetry-instrumentation-dbapi` Fix compatibility with Psycopg3 to extract libpq build version - ([#2500](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2500)) -- `opentelemetry-instrumentation-grpc` AioClientInterceptor should propagate with a Metadata object - ([#2363](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2363)) -- `opentelemetry-instrumentation-boto3sqs` Instrument Session and resource - ([#2161](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2161)) -- `opentelemetry-instrumentation-aws-lambda` Fix exception handling for events with requestContext - ([#2418](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2418)) -- Use sqlalchemy version in sqlalchemy commenter instead of opentelemetry library version - ([#2404](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2404)) -- `opentelemetry-instrumentation-asyncio` Check for cancelledException in the future - ([#2461](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2461)) -- Remove SDK dependency from opentelemetry-instrumentation-grpc - ([#2474](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2474)) -- `opentelemetry-instrumentation-elasticsearch` Improved support for version 8 - ([#2420](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2420)) -- `opentelemetry-instrumentation-elasticsearch` Disabling instrumentation with native OTel support enabled - ([#2524](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2524)) -- `opentelemetry-instrumentation-asyncio` Check for **name** attribute in the coroutine - ([#2521](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2521)) -- `opentelemetry-instrumentation-requests` Fix wrong time unit for duration histogram - ([#2553](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2553)) -- `opentelemetry-util-http` Preserve brackets around literal IPv6 hosts ([#2552](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2552)) -- `opentelemetry-util-redis` Fix net peer attribute for unix socket connection ([#2493](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2493)) - -## Version 1.24.0/0.45b0 (2024-03-28) - -### Added - -- `opentelemetry-instrumentation-psycopg` Async Instrumentation for psycopg 3.x - ([#2146](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2146)) - -### Fixed - -- `opentelemetry-instrumentation-celery` Allow Celery instrumentation to be installed multiple times - ([#2342](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2342)) -- Align gRPC span status codes to OTEL specification - ([#1756](https://github.com/open-telemetry/opentelemetry-python-contrib/issues/1756)) -- `opentelemetry-instrumentation-flask` Add importlib metadata default for deprecation warning flask version - ([#2297](https://github.com/open-telemetry/opentelemetry-python-contrib/issues/2297)) -- Ensure all http.server.duration metrics have the same description - ([#2151](https://github.com/open-telemetry/opentelemetry-python-contrib/issues/2298)) -- Fix regression in httpx `request.url` not being of type `httpx.URL` after `0.44b0` - ([#2359](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2359)) -- Avoid losing repeated HTTP headers - ([#2266](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2266)) -- `opentelemetry-instrumentation-elasticsearch` Don't send bulk request body as db statement - ([#2355](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2355)) -- AwsLambdaInstrumentor sets `cloud.account.id` span attribute - ([#2367](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2367)) - -### Added - -- `opentelemetry-instrumentation-fastapi` Add support for configuring header extraction via runtime constructor parameters - ([#2241](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2241)) - -## Version 1.23.0/0.44b0 (2024-02-23) - -- Drop support for 3.7 - ([#2151](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2151)) -- `opentelemetry-resource-detector-azure` Added 10s timeout to VM Resource Detector - ([#2119](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2119)) -- `opentelemetry-instrumentation-asyncpg` Allow AsyncPGInstrumentor to be instantiated multiple times - ([#1791](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1791)) -- `opentelemetry-instrumentation-confluent-kafka` Add support for higher versions until 2.3.0 of confluent_kafka - ([#2132](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2132)) -- `opentelemetry-resource-detector-azure` Changed timeout to 4 seconds due to [timeout bug](https://github.com/open-telemetry/opentelemetry-python/issues/3644) - ([#2136](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2136)) -- `opentelemetry-resource-detector-azure` Suppress instrumentation for `urllib` call - ([#2178](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2178)) -- AwsLambdaInstrumentor handles and re-raises function exception - ([#2245](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2245)) - -### Added - -- `opentelemetry-instrumentation-psycopg` Initial release for psycopg 3.x -- `opentelemetry-instrumentation-asgi` Add support for configuring ASGI middleware header extraction via runtime constructor parameters - ([#2026](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2026)) - -## Version 1.22.0/0.43b0 (2023-12-14) - -### Added - -- `opentelemetry-instrumentation-asyncio` Add support for asyncio - ([#1919](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1943)) -- `opentelemetry-instrumentation` Added Otel semantic convention opt-in mechanism - ([#1987](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1987)) -- `opentelemetry-instrumentation-httpx` Fix mixing async and non async hooks - ([#1920](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1920)) -- `opentelemetry-instrumentation-requests` Implement new semantic convention opt-in with stable http semantic conventions - ([#2002](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2002)) -- `opentelemetry-instrument-grpc` Fix arity of context.abort for AIO RPCs - ([#2066](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2066)) -- Consolidate instrumentation suppression mechanisms and fix bug in httpx instrumentation - ([#2061](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2061)) - -### Fixed - -- `opentelemetry-instrumentation-httpx` Remove URL credentials - ([#2020](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2020)) -- `opentelemetry-instrumentation-urllib`/`opentelemetry-instrumentation-urllib3` Fix metric descriptions to match semantic conventions - ([#1959](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1959)) -- `opentelemetry-resource-detector-azure` Added dependency for Cloud Resource ID attribute - ([#2072](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2072)) - -## Version 1.21.0/0.42b0 (2023-11-01) - -### Added - -- `opentelemetry-instrumentation-aiohttp-server` Add instrumentor and auto instrumentation support for aiohttp-server - ([#1800](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1800)) -- `opentelemetry-instrumentation-botocore` Include SNS topic ARN as a span attribute with name `messaging.destination.name` to uniquely identify the SNS topic - ([#1995](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1995)) -- `opentelemetry-instrumentation-system-metrics` Add support for collecting process metrics - ([#1948](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1948)) -- Added schema_url (`"https://opentelemetry.io/schemas/1.11.0"`) to all metrics and traces - ([#1977](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1977)) - -### Fixed - -- `opentelemetry-instrumentation-aio-pika` and `opentelemetry-instrumentation-pika` Fix missing trace context propagation when trace not recording. - ([#1969](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1969)) -- Fix version of Flask dependency `werkzeug` - ([#1980](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1980)) -- `opentelemetry-resource-detector-azure` Using new Cloud Resource ID attribute. - ([#1976](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1976)) -- Do not collect `system.network.connections` by default on macOS which was causing exceptions in metrics collection. - ([#2008](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2008)) - -## Version 1.20.0/0.41b0 (2023-09-01) - -### Fixed - -- `opentelemetry-instrumentation-asgi` Fix UnboundLocalError local variable 'start' referenced before assignment - ([#1889](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1889)) -- Fixed union typing error not compatible with Python 3.7 introduced in `opentelemetry-util-http`, fix tests introduced by patch related to sanitize method for wsgi - ([#1913](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1913)) -- `opentelemetry-instrumentation-celery` Unwrap Celery's `ExceptionInfo` errors and report the actual exception that was raised. ([#1863](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1863)) - -### Added - -- `opentelemetry-resource-detector-azure` Add resource detectors for Azure App Service and VM - ([#1901](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1901)) -- `opentelemetry-instrumentation-flask` Add support for Flask 3.0.0 - ([#152](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2013)) - -## Version 1.19.0/0.40b0 (2023-07-13) - -- `opentelemetry-instrumentation-asgi` Add `http.server.request.size` metric - ([#1867](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1867)) - -### Fixed - -- `opentelemetry-instrumentation-django` Fix empty span name when using - `path("", ...)` ([#1788](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1788) -- Fix elastic-search instrumentation sanitization to support bulk queries - ([#1870](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1870)) -- Update falcon instrumentation to follow semantic conventions - ([#1824](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1824)) -- Fix sqlalchemy instrumentation wrap methods to accept sqlcommenter options - ([#1873](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1873)) -- Exclude background task execution from root server span in ASGI middleware - ([#1952](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1952)) - -### Added - -- Add instrumentor support for cassandra and scylla - ([#1902](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1902)) -- Add instrumentor support for mysqlclient - ([#1744](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1744)) -- Fix async redis clients not being traced correctly - ([#1830](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1830)) -- Make Flask request span attributes available for `start_span`. - ([#1784](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1784)) -- Fix falcon instrumentation's usage of Span Status to only set the description if the status code is ERROR. - ([#1840](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1840)) -- Instrument all httpx versions >= 0.18. - ([#1748](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1748)) -- Fix `Invalid type NoneType for attribute X (opentelemetry-instrumentation-aws-lambda)` error when some attributes do not exist - ([#1780](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1780)) -- Add metric instrumentation for celery - ([#1679](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1679)) -- `opentelemetry-instrumentation-asgi` Add `http.server.response.size` metric - ([#1789](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1789)) -- `opentelemetry-instrumentation-grpc` Allow gRPC connections via Unix socket - ([#1833](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1833)) -- Fix elasticsearch `Transport.perform_request` instrument wrap for elasticsearch >= 8 - ([#1810](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1810)) -- `opentelemetry-instrumentation-urllib3` Add support for urllib3 version 2 - ([#1879](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1879)) -- Add optional distro and configurator selection for auto-instrumentation - ([#1823](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1823)) -- `opentelemetry-instrumentation-django` - Add option to add Opentelemetry middleware at specific position in middleware chain - ([#2912]https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2912) - -### Added - -- `opentelemetry-instrumentation-kafka-python` Add instrumentation to `consume` method - ([#1786](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1786)) - -## Version 1.18.0/0.39b0 (2023-05-10) - -- Update runtime metrics to follow semantic conventions - ([#1735](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1735)) -- Add request and response hooks for GRPC instrumentation (client only) - ([#1706](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1706)) -- Fix memory leak in SQLAlchemy instrumentation where disposed `Engine` does not get garbage collected - ([#1771](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1771)) -- `opentelemetry-instrumentation-pymemcache` Update instrumentation to support pymemcache >4 - ([#1764](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1764)) -- `opentelemetry-instrumentation-confluent-kafka` Add support for higher versions of confluent_kafka - ([#1815](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1815)) - -### Added - -- Expand sqlalchemy pool.name to follow the semantic conventions - ([#1778](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1778)) -- Add `excluded_urls` functionality to `urllib` and `urllib3` instrumentations - ([#1733](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1733)) -- Make Django request span attributes available for `start_span`. - ([#1730](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1730)) -- Make ASGI request span attributes available for `start_span`. - ([#1762](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1762)) -- `opentelemetry-instrumentation-celery` Add support for anonymous tasks. - ([#1407](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1407)) -- `opentelemetry-instrumentation-logging` Add `otelTraceSampled` to instrumetation-logging - ([#1773](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1773)) - -### Changed - -- `opentelemetry-instrumentation-botocore` now uses the AWS X-Ray propagator by default - ([#1741](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1741)) - -### Fixed - -- Fix redis db.statements to be sanitized by default - ([#1778](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1778)) -- Fix elasticsearch db.statement attribute to be sanitized by default - ([#1758](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1758)) -- Fix `AttributeError` when AWS Lambda handler receives a list event - ([#1738](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1738)) -- Fix `None does not implement middleware` error when there are no middlewares registered - ([#1766](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1766)) -- Fix Flask instrumentation to only close the span if it was created by the same request context. - ([#1692](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1692)) - -### Changed - -- Update HTTP server/client instrumentation span names to comply with spec - ([#1759](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1759)) - -## Version 1.17.0/0.38b0 (2023-03-22) - -### Added - -- Add connection attributes to sqlalchemy connect span - ([#1608](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1608)) -- Add support for enabling Redis sanitization from environment variable - ([#1690](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1690)) -- Add metrics instrumentation for sqlalchemy - ([#1645](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1645)) - -### Fixed - -- Fix Flask instrumentation to only close the span if it was created by the same thread. - ([#1654](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1654)) -- Fix confluent-kafka instrumentation by allowing Producer headers to be dict or list - ([#1655](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1655)) -- `opentelemetry-instrumentation-system-metrics` Fix initialization of the instrumentation class when configuration is provided - ([#1438](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1439)) -- Fix exception in Urllib3 when dealing with filelike body. - ([#1399](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1399)) -- Fix httpx resource warnings - ([#1695](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1695)) - -### Changed - -- `opentelemetry-instrumentation-requests` Replace `name_callback` and `span_callback` with standard `response_hook` and `request_hook` callbacks - ([#670](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/670)) - -## Version 1.16.0/0.37b0 (2023-02-17) - -### Added - -- Support `aio_pika` 9.x (([#1670](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1670]) -- `opentelemetry-instrumentation-redis` Add `sanitize_query` config option to allow query sanitization. ([#1572](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1572)) -- `opentelemetry-instrumentation-elasticsearch` Add optional db.statement query sanitization. - ([#1598](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1598)) -- `opentelemetry-instrumentation-celery` Record exceptions as events on the span. - ([#1573](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1573)) -- Add metric instrumentation for urllib - ([#1553](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1553)) -- `opentelemetry/sdk/extension/aws` Implement [`aws.ecs.*`](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/resource/semantic_conventions/cloud_provider/aws/ecs.md) and [`aws.logs.*`](https://opentelemetry.io/docs/reference/specification/resource/semantic_conventions/cloud_provider/aws/logs/) resource attributes in the `AwsEcsResourceDetector` detector when the ECS Metadata v4 is available - ([#1212](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1212)) -- `opentelemetry-instrumentation-aio-pika` Support `aio_pika` 8.x - ([#1481](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1481)) -- `opentelemetry-instrumentation-aws-lambda` Flush `MeterProvider` at end of function invocation. - ([#1613](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1613)) -- Fix aiohttp bug with unset `trace_configs` - ([#1592](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1592)) -- `opentelemetry-instrumentation-django` Allow explicit `excluded_urls` configuration through `instrument()` - ([#1618](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1618)) - -### Fixed - -- Fix TortoiseORM instrumentation `AttributeError: type object 'Config' has no attribute 'title'` - ([#1575](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1575)) -- Fix SQLAlchemy uninstrumentation - ([#1581](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1581)) -- `opentelemetry-instrumentation-grpc` Fix code()/details() of \_OpentelemetryServicerContext. - ([#1578](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1578)) -- Fix aiopg instrumentation to work with aiopg < 2.0.0 - ([#1473](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1473)) -- `opentelemetry-instrumentation-aws-lambda` Adds an option to configure `disable_aws_context_propagation` by - environment variable: `OTEL_LAMBDA_DISABLE_AWS_CONTEXT_PROPAGATION` - ([#1507](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1507)) -- Fix pymongo to collect the property DB_MONGODB_COLLECTION - ([#1555](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1555)) -- `opentelemetry-instrumentation-asgi` Fix keys() in class ASGIGetter to correctly fetch values from carrier headers. - ([#1435](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1435)) -- mongo db - fix db statement capturing - ([#1512](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1512)) -- Add commit method for ConfluentKafkaInstrumentor's ProxiedConsumer - ([#1656](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1656)) - -## Version 1.15.0/0.36b0 (2022-12-10) - -- Add uninstrument test for sqlalchemy - ([#1471](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1471)) -- `opentelemetry-instrumentation-tortoiseorm` Initial release - ([#685](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/685)) -- Add metric instrumentation for tornado - ([#1252](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1252)) -- `opentelemetry-instrumentation-aws-lambda` Add option to disable aws context propagation - ([#1466](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1466)) - -### Added - -- `opentelemetry-resource-detector-container` Add support resource detection of container properties. - ([#1584](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1584)) -- `opentelemetry-instrumentation-pymysql` Add tests for commit() and rollback(). - ([#1424](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1424)) -- `opentelemetry-instrumentation-fastapi` Add support for regular expression matching and sanitization of HTTP headers. - ([#1403](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1403)) -- `opentelemetry-instrumentation-botocore` add support for `messaging.*` in the sqs extension. - ([#1350](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1350)) -- `opentelemetry-instrumentation-starlette` Add support for regular expression matching and sanitization of HTTP headers. - ([#1404](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1404)) -- `opentelemetry-instrumentation-botocore` Add support for SNS `publish` and `publish_batch`. - ([#1409](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1409)) -- Strip leading comments from SQL queries when generating the span name. - ([#1434](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1434)) -- `opentelemetry-instrumentation-confluent-kafka` Add support for the latest versions of the library. - ([#1468](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1468)) - -### Fixed - -- Fix bug in Urllib instrumentation - add status code to span attributes only if the status code is not None. - ([#1430](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1430)) -- `opentelemetry-instrumentation-aiohttp-client` Allow overriding of status in response hook. - ([#1394](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1394)) -- `opentelemetry-instrumentation-pymysql` Fix dbapi connection instrument wrapper has no \_sock member. - ([#1424](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1424)) -- `opentelemetry-instrumentation-dbapi` Fix the check for the connection already being instrumented in instrument_connection(). - ([#1424](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1424)) -- Remove db.name attribute from Redis instrumentation - ([#1427](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1427)) -- `opentelemetry-instrumentation-asgi` Fix target extraction for duration metric - ([#1461](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1461)) -- Add grpc.aio instrumentation to package entry points - ([#1442](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1442)) -- Fix a bug in SQLAlchemy instrumentation - support disabling enable_commenter variable - ([#1440](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1440)) - -## Version 1.14.0/0.35b0 (2022-11-03) - -### Deprecated - -- `opentelemetry-distro` Deprecate `otlp_proto_grpc` and `otlp_proto_http` in favor of using - `OTEL_EXPORTER_OTLP_TRACES_PROTOCOL` as according to specifications - ([#1250](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1250)) - -### Added - -- Capture common HTTP attributes from API Gateway proxy events in `opentelemetry-instrumentation-aws-lambda` - ([#1233](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1233)) -- Add metric instrumentation for tornado - ([#1252](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1252)) -- `opentelemetry-instrumentation-django` Fixed bug where auto-instrumentation fails when django is installed and settings are not configured. - ([#1369](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1369)) -- `opentelemetry-instrumentation-system-metrics` add supports to collect system thread count. ([#1339](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1339)) -- `opentelemetry-exporter-richconsole` Fixing RichConsoleExpoter to allow multiple traces, fixing duplicate spans and include resources ([#1336](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1336)) -- `opentelemetry-instrumentation-asgi` Add support for regular expression matching and sanitization of HTTP headers. - ([#1333](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1333)) -- `opentelemetry-instrumentation-asgi` metrics record target attribute (FastAPI only) - ([#1323](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1323)) -- `opentelemetry-instrumentation-wsgi` Add support for regular expression matching and sanitization of HTTP headers. - ([#1402](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1402)) -- Add support for py3.11 - ([#1415](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1415)) -- `opentelemetry-instrumentation-django` Add support for regular expression matching and sanitization of HTTP headers. - ([#1411](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1411)) -- `opentelemetry-instrumentation-falcon` Add support for regular expression matching and sanitization of HTTP headers. - ([#1412](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1412)) -- `opentelemetry-instrumentation-flask` Add support for regular expression matching and sanitization of HTTP headers. - ([#1413](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1413)) -- `opentelemetry-instrumentation-pyramid` Add support for regular expression matching and sanitization of HTTP headers. - ([#1414](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1414)) -- `opentelemetry-instrumentation-grpc` Add support for grpc.aio Clients and Servers - ([#1245](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1245)) -- Add metric exporter for Prometheus Remote Write - ([#1359](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1359)) - -### Fixed - -- Fix bug in Falcon instrumentation - ([#1377](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1377)) -- `opentelemetry-instrumentation-asgi` Fix keys() in class ASGIGetter so it decodes the keys before returning them. - ([#1333](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1333)) -- `opentelemetry-instrumentation-asgi` Make ASGIGetter.get() compare all keys in a case insensitive manner. - ([#1333](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1333)) -- Use resp.text instead of resp.body for Falcon 3 to avoid a deprecation warning. - ([#1412](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1412)) - -## Version 1.13.0/0.34b0 (2022-09-26) - -- `opentelemetry-instrumentation-asyncpg` Fix high cardinality in the span name - ([#1324](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1324)) - -### Added - -- `opentelemetry-instrumentation-grpc` add supports to filter requests to instrument. - ([#1241](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1241)) -- Flask sqlalchemy psycopg2 integration - ([#1224](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1224)) -- Add metric instrumentation in Falcon - ([#1230](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1230)) -- Add metric instrumentation in fastapi - ([#1199](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1199)) -- Add metric instrumentation in Pyramid - ([#1242](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1242)) -- `opentelemetry-util-http` Add support for sanitizing HTTP header values. - ([#1253](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1253)) -- Add metric instrumentation in starlette - ([#1327](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1327)) - -### Fixed - -- `opentelemetry-instrumentation-kafka-python`: wait for metadata - ([#1260](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1260)) -- `opentelemetry-instrumentation-boto3sqs` Make propagation compatible with other SQS instrumentations, add 'messaging.url' span attribute, and fix missing package dependencies. - ([#1234](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1234)) -- `opentelemetry-instrumentation-pymongo` Change span names to not contain queries but only database name and command name - ([#1247](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1247)) -- restoring metrics in django framework - ([#1208](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1208)) -- `opentelemetry-instrumentation-aiohttp-client` Fix producing additional spans with each newly created ClientSession -- ([#1246](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1246)) -- Add \_is_opentelemetry_instrumented check in \_InstrumentedFastAPI class - ([#1313](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1313)) -- Fix uninstrumentation of existing app instances in FastAPI - ([#1258](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1258)) -- Fix uninstrumentation of existing app instances in falcon - ([#1341]https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1341) - -## Version 1.12.0/0.33b0 (2022-08-08) - -- Adding multiple db connections support for django-instrumentation's sqlcommenter - ([#1187](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1187)) -- SQLCommenter semicolon bug fix - ([#1200](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1200/files)) -- Adding sqlalchemy native tags in sqlalchemy commenter - ([#1206](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1206)) -- Add psycopg2 native tags to sqlcommenter - ([#1203](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1203)) - -### Added - -- `opentelemetry-instrumentation-redis` add support to instrument RedisCluster clients - ([#1177](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1177)) -- `opentelemetry-instrumentation-sqlalchemy` Added span for the connection phase ([#1133](https://github.com/open-telemetry/opentelemetry-python-contrib/issues/1133)) -- Add metric instrumentation in asgi - ([#1197](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1197)) -- Add metric instrumentation for flask - ([#1186](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1186)) -- Add a test for asgi using NoOpTracerProvider - ([#1367](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1367)) - -## [1.12.0rc2-0.32b0](https://github.com/open-telemetry/opentelemetry-python/releases/tag/v1.12.0rc2-0.32b0) - 2022-07-01 - -- Pyramid: Only categorize 500s server exceptions as errors - ([#1037](https://github.com/open-telemetry/opentelemetry-python-contrib/issues/1037)) - -### Fixed - -- Fix bug in system metrics by checking their configuration - ([#1129](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1129)) -- Adding escape call to fix [auto-instrumentation not producing spans on Windows](https://github.com/open-telemetry/opentelemetry-python/issues/2703). - ([#1100](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1100)) -- `opentelemetry-instrumentation-grpc` narrow protobuf dependency to exclude protobuf >= 4 - ([#1109](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1109)) -- cleanup type hints for textmap `Getter` and `Setter` classes -- Suppressing downstream HTTP instrumentation to avoid [extra spans](https://github.com/open-telemetry/opentelemetry-python-contrib/issues/930) - ([#1116](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1116)) -- fixed typo in `system.network.io` metric configuration - ([#1135](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1135)) - -### Added - -- `opentelemetry-instrumentation-aiohttp-client` Add support for optional custom trace_configs argument. - ([1079](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1079)) -- `opentelemetry-instrumentation-sqlalchemy` add support to instrument multiple engines - ([#1132](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1132)) -- `opentelemetry-instrumentation-logging` add log hook support - ([#1117](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1117)) -- `opentelemetry-instrumentation-remoulade` Initial release - ([#1082](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1082)) -- Added `opentelemetry-instrumention-confluent-kafka` - ([#1111](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1111)) -- Set otlp-proto-grpc as the default metrics exporter for auto-instrumentation - ([#1127](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1127)) -- Add metric instrumentation for WSGI - ([#1128](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1128)) -- Add metric instrumentation for Urllib3 - ([#1198](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1198)) -- `opentelemetry-instrumentation-aio-pika` added RabbitMQ aio-pika module instrumentation. - ([#1095](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1095)) -- `opentelemetry-instrumentation-requests` Restoring metrics in requests - ([#1110](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1110)) -- Integrated sqlcommenter plugin into opentelemetry-instrumentation-django - ([#896](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/896)) - -## Version 1.12.0rc1/0.31b0 (2022-05-17) - -### Fixed - -- `opentelemetry-instrumentation-aiohttp-client` make span attributes available to sampler - ([#1072](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1072)) -- `opentelemetry-instrumentation-aws-lambda` Fixed an issue - in some rare cases (API GW proxy integration test) - headers are set to None, breaking context propagators. - ([#1055](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1055)) -- Refactoring custom header collection API for consistency - ([#1064](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1064)) -- `opentelemetry-instrumentation-sqlalchemy` will correctly report `otel.library.name` - ([#1086](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1086)) -- `opentelemetry-sdk-extension-aws` change timeout for AWS EC2 and EKS metadata requests from 1000 seconds and 2000 seconds to 1 second - -### Added - -- `opentelemetry-instrument` and `opentelemetry-bootstrap` now include a `--version` flag - ([#1065](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1065)) -- `opentelemetry-instrumentation-redis` now instruments asynchronous Redis clients, if the installed redis-py includes async support (>=4.2.0). - ([#1076](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1076)) -- `opentelemetry-instrumentation-boto3sqs` added AWS's SQS instrumentation. - ([#1081](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1081)) - -## Version 1.11.1/0.30b1 (2022-04-21) - -### Added - -- `opentelemetry-instrumentation-starlette` Capture custom request/response headers in span attributes - ([#1046](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1046)) - -### Fixed - -- Prune autoinstrumentation sitecustomize module directory from PYTHONPATH immediately - ([#1066](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1066)) - -## Version 1.11.0/0.30b0 (2022-04-18) - -### Fixed - -- `opentelemetry-instrumentation-pyramid` Fixed which package is the correct caller in \_traced_init. - ([#830](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/830)) -- `opentelemetry-instrumentation-tornado` Fix Tornado errors mapping to 500 - ([#1048](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1048)) -- `opentelemetry-instrumentation-urllib` make span attributes available to sampler - ([1014](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1014)) -- `opentelemetry-instrumentation-flask` Fix non-recording span bug - ([#999](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/999)) -- `opentelemetry-instrumentation-tornado` Fix non-recording span bug - ([#999](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/999)) - -### Added - -- `opentelemetry-instrumentation-fastapi` Capture custom request/response headers in span attributes - ([#1032](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1032)) -- `opentelemetry-instrumentation-django` Capture custom request/response headers in span attributes - ([#1024](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1024)) -- `opentelemetry-instrumentation-asgi` Capture custom request/response headers in span attributes - ([#1004](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1004)) -- `opentelemetry-instrumentation-psycopg2` extended the sql commenter support of dbapi into psycopg2 - ([#940](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/940)) -- `opentelemetry-instrumentation-falcon` Add support for falcon==1.4.1 - ([#1000](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1000)) -- `opentelemetry-instrumentation-falcon` Falcon: Capture custom request/response headers in span attributes - ([#1003](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1003)) -- `opentelemetry-instrumentation-elasticsearch` no longer creates unique span names by including search target, replaces them with `` and puts the value in attribute `elasticsearch.target` - ([#1018](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1018)) -- `opentelemetry-instrumentation-pyramid` Handle non-HTTPException exceptions - ([#1001](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1001)) -- `opentelemetry-instrumentation-system-metrics` restore `SystemMetrics` instrumentation as `SystemMetricsInstrumentor` - ([#1012](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1012)) -- `opentelemetry-instrumentation-pyramid` Pyramid: Capture custom request/response headers in span attributes - ([#1022](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1022)) - -## Version 1.10.0/0.29b0 (2022-03-10) - -- `opentelemetry-instrumentation-wsgi` Capture custom request/response headers in span attributes - ([#925](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/925)) -- `opentelemetry-instrumentation-flask` Flask: Capture custom request/response headers in span attributes - ([#952](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/952)) -- `opentelemetry-instrumentation-tornado` Tornado: Capture custom request/response headers in span attributes - ([#950](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/950)) - -### Added - -- `opentelemetry-instrumentation-aws-lambda` `SpanKind.SERVER` by default, add more cases for `SpanKind.CONSUMER` services. ([#926](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/926)) -- `opentelemetry-instrumentation-sqlalchemy` added experimental sql commenter capability - ([#924](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/924)) -- `opentelemetry-contrib-instrumentations` added new meta-package that installs all contrib instrumentations. - ([#681](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/681)) -- `opentelemetry-instrumentation-dbapi` add experimental sql commenter capability - ([#908](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/908)) -- `opentelemetry-instrumentation-requests` make span attribute available to samplers - ([#931](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/931)) -- `opentelemetry-datadog-exporter` add deprecation note to example. - ([#900](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/900)) - -### Fixed - -- `opentelemetry-instrumentation-dbapi` Changed the format of traceparent id. - ([#941](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/941)) -- `opentelemetry-instrumentation-logging` retrieves service name defensively. - ([#890](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/890)) -- `opentelemetry-instrumentation-wsgi` WSGI: Conditionally create SERVER spans - ([#903](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/903)) -- `opentelemetry-instrumentation-falcon` Safer patching mechanism - ([#895](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/895)) -- `opentelemetry-instrumentation-kafka-python` Fix topic extraction - ([#949](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/949)) - -### Changed - -- `opentelemetry-instrumentation-pymemcache` should run against newer versions of pymemcache. - ([#935](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/935)) - -## Version 1.9.1/0.28b1 (2022-01-29) - -### Fixed - -- `opentelemetry-instrumentation-pika` requires `packaging` dependency - -- `opentelemetry-instrumentation-tornado` Tornado: Conditionally create SERVER spans - ([#889](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/889)) - -## Version 1.9.0/0.28b0 (2022-01-26) - -### Added - -- `opentelemetry-instrumentation-pyramid` Pyramid: Conditionally create SERVER spans - ([#869](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/869)) -- `opentelemetry-instrumentation-grpc` added `trailing_metadata` to \_OpenTelemetryServicerContext. - ([#871](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/871)) -- `opentelemetry-instrumentation-asgi` now returns a `traceresponse` response header. - ([#817](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/817)) -- `opentelemetry-instrumentation-kafka-python` added kafka-python module instrumentation. - ([#814](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/814)) -- `opentelemetry-instrumentation-falcon` Falcon: Conditionally create SERVER spans - ([#867](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/867)) -- `opentelemetry-instrumentation-pymongo` now supports `pymongo v4` - ([#876](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/876)) - -- `opentelemetry-instrumentation-httpx` now supports versions higher than `0.19.0`. - ([#866](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/866)) - -### Fixed - -- `opentelemetry-instrumentation-django` Django: Conditionally create SERVER spans - ([#832](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/832)) -- `opentelemetry-instrumentation-flask` Flask: Conditionally create SERVER spans - ([#828](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/828)) -- `opentelemetry-instrumentation-celery` Celery: Support partial task time limit - ([#846](https://github.com/open-telemetry/opentelemetry-python-contrib/issues/846)) -- `opentelemetry-instrumentation-asgi` ASGI: Conditionally create SERVER spans - ([#843](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/843)) -- `opentelemetry-instrumentation-django` Django: fix issue preventing detection of MIDDLEWARE_CLASSES -- `opentelemetry-instrumentation-sqlite3` Instrumentation now works with `dbapi2.connect` -- `opentelemetry-instrumentation-kafka` Kafka: safe kafka partition extraction - ([#872](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/872)) -- `opentelemetry-instrumentation-aiohttp-client` aiohttp: Correct url filter input type - ([#843](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/864)) - -- `opentelemetry-instrumentation-aiohttp-client` aiohttp: Remove `span_name` from docs - ([#857](https://github.com/open-telemetry/opentelemetry-python-contrib/issues/857)) - -## Version 1.8.0/0.27b0 (2021-12-17) - -### Added - -- `opentelemetry-instrumentation-aws-lambda` Adds support for configurable flush timeout via `OTEL_INSTRUMENTATION_AWS_LAMBDA_FLUSH_TIMEOUT` property. ([#825](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/825)) -- `opentelemetry-instrumentation-pika` Adds support for versions between `0.12.0` to `1.0.0`. ([#837](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/837)) - -### Fixed - -- `opentelemetry-instrumentation-urllib` Fixed an error on unexpected status values. - ([#823](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/823)) - -- `opentelemetry-exporter-richconsole` Fixed attribute error on parentless spans. - ([#782](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/782)) - -- `opentelemetry-instrumentation-tornado` Add support instrumentation for Tornado 5.1.1 - ([#812](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/812)) - -## Version 1.7.1/0.26b1 (2021-11-11) - -### Added - -- `opentelemetry-instrumentation-aws-lambda` Add instrumentation for AWS Lambda Service - pkg metadata files (Part 1/2) - ([#739](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/739)) -- Add support for Python 3.10 - ([#742](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/742)) -- Pass in auto-instrumentation version to configurator - ([#783](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/783)) -- `opentelemetry-instrumentation` Add `setuptools` to `install_requires` - ([#781](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/781)) -- `opentelemetry-instrumentation-aws-lambda` Add instrumentation for AWS Lambda Service - Implementation (Part 2/2) - ([#777](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/777)) -- `opentelemetry-instrumentation-pymongo` Add `request_hook`, `response_hook` and `failed_hook` callbacks passed as arguments to the instrument method - ([#793](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/793)) -- `opentelemetry-instrumentation-pymysql` Add support for PyMySQL 1.x series - ([#792](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/792)) -- Add support for generic OTEL_PYTHON_EXCLUDED_URLS variable - ([#790](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/790)) - -### Fixed - -- `opentelemetry-instrumentation-asgi` now explicitly depends on asgiref as it uses the package instead of instrumenting it. - ([#765](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/765)) -- `opentelemetry-instrumentation-pika` now propagates context to basic_consume callback - ([#766](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/766)) -- `opentelemetry-instrumentation-falcon` Dropped broken support for Python 3.4. - ([#774](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/774)) -- `opentelemetry-instrumentation-django` Fixed carrier usage on ASGI requests. - ([#767](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/767)) -- Don't set Span Status on 4xx http status code for SpanKind.SERVER spans - ([#776](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/776)) -- `opentelemetry-instrumentation-django` Fixed instrumentation and tests for all Django major versions. - ([#780](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/780)) - -## Version 1.6.2/0.25b2 (2021-10-19) - -- `opentelemetry-instrumentation-sqlalchemy` Fix PostgreSQL instrumentation for Unix sockets - ([#761](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/761)) - -### Changed - -- `opentelemetry-sdk-extension-aws` & `opentelemetry-propagator-aws` Release AWS Python SDK Extension as 2.0.1 and AWS Propagator as 1.0.1 - ([#753](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/753)) -- `opentelemetry-instrumentation-pika` Add `_decorate_basic_consume` to ensure post instrumentation `basic_consume` calls are also instrumented. - ([#759](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/759)) -- Consolidate instrumentation documentation in docstrings - ([#754](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/754)) - -### Fixed - -- `opentelemetry-distro` uses the correct entrypoint name which was updated in the core release of 1.6.0 but the distro was not updated with it - ([#755](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/755)) - -### Added - -- `opentelemetry-instrumentation-pika` Add `publish_hook` and `consume_hook` callbacks passed as arguments to the instrument method - ([#763](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/763)) - -## Version 1.6.1/0.25b1 (2021-10-18) - -### Changed - -- `opentelemetry-util-http` no longer contains an instrumentation entrypoint and will not be loaded - automatically by the auto instrumentor. - ([#745](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/745)) -- `opentelemetry-instrumentation-pika` Bugfix use properties.headers. It will prevent the header injection from raising. - ([#740](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/740)) -- `opentelemetry-instrumentation-botocore` Add extension for DynamoDB - ([#735](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/735)) -- `opentelemetry-sdk-extension-aws` & `opentelemetry-propagator-aws` Remove unnecessary dependencies on `opentelemetry-test` - ([#752](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/752)) -- `opentelemetry-instrumentation-botocore` Add Lambda extension - ([#760](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/760)) - -## Version 1.6.0/0.25b0 (2021-10-13) - -### Added - -- `opentelemetry-sdk-extension-aws` Release AWS Python SDK Extension as 1.0.0 - ([#667](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/667)) -- `opentelemetry-instrumentation-urllib3`, `opentelemetry-instrumentation-requests` - The `net.peer.ip` attribute is set to the IP of the connected HTTP server or proxy - using a new instrumentor in `opententelemetry-util-http` - ([#661](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/661)) -- `opentelemetry-instrumentation-pymongo` Add check for suppression key in PyMongo. - ([#736](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/736)) -- `opentelemetry-instrumentation-elasticsearch` Added `response_hook` and `request_hook` callbacks - ([#670](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/670)) -- `opentelemetry-instrumentation-redis` added request_hook and response_hook callbacks passed as arguments to the instrument method. - ([#669](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/669)) -- `opentelemetry-instrumentation-botocore` add `request_hook` and `response_hook` callbacks - ([679](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/679)) -- `opentelemetry-exporter-richconsole` Initial release - ([#686](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/686)) -- `opentelemetry-instrumentation-elasticsearch` no longer creates unique span names by including document IDs, replaces them with `:id` and puts the value in attribute `elasticsearch.id` - ([#705](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/705)) -- `opentelemetry-instrumentation-tornado` now sets `http.client_ip` and `tornado.handler` attributes - ([#706](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/706)) -- `opentelemetry-instrumentation-requests` added exclude urls functionality - ([#714](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/714)) -- `opentelemetry-instrumentation-django` Add ASGI support - ([#391](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/391)) - -### Changed - -- `opentelemetry-instrumentation-flask` Fix `RuntimeError: Working outside of request context` - ([#734](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/734)) -- `opentelemetry-propagators-aws-xray` Rename `AwsXRayFormat` to `AwsXRayPropagator` - ([#729](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/729)) -- `opentelemetry-instrumentation-sqlalchemy` Respect provided tracer provider when instrumenting SQLAlchemy - ([#728](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/728)) -- `opentelemetry-sdk-extension-aws` Move AWS X-Ray Propagator into its own `opentelemetry-propagators-aws` package - ([#720](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/720)) -- `opentelemetry-instrumentation-sqlalchemy` Added `packaging` dependency - ([#713](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/713)) -- `opentelemetry-instrumentation-jinja2` Allow instrumentation of newer Jinja2 versions. - ([#712](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/712)) -- `opentelemetry-instrumentation-botocore` Make common span attributes compliant with semantic conventions - ([#674](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/674)) -- `opentelemetry-sdk-extension-aws` Release AWS Python SDK Extension as 1.0.0 - ([#667](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/667)) -- `opentelemetry-instrumentation-botocore` Unpatch botocore Endpoint.prepare_request on uninstrument - ([#664](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/664)) -- `opentelemetry-instrumentation-botocore` Fix span injection for lambda invoke - ([#663](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/663)) -- `opentelemetry-instrumentation-botocore` Introduce instrumentation extensions - ([#718](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/718)) -- `opentelemetry-instrumentation-urllib3` Updated `_RequestHookT` with two additional fields - the request body and the request headers - ([#660](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/660)) -- Tests for Falcon 3 support - ([#644](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/644)) - -## Version 1.5.0/0.24b0 (2021-08-26) - -### Added - -- `opentelemetry-sdk-extension-aws` Add AWS resource detectors to extension package - ([#586](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/586)) -- `opentelemetry-instrumentation-asgi`, `opentelemetry-instrumentation-aiohttp-client`, `openetelemetry-instrumentation-fastapi`, - `opentelemetry-instrumentation-starlette`, `opentelemetry-instrumentation-urllib`, `opentelemetry-instrumentation-urllib3` Added `request_hook` and `response_hook` callbacks - ([#576](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/576)) -- `opentelemetry-instrumentation-pika` added RabbitMQ's pika module instrumentation. - ([#680](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/680)) - -### Changed - -- `opentelemetry-instrumentation-fastapi` Allow instrumentation of newer FastAPI versions. - ([#602](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/602)) -- Enable explicit `excluded_urls` argument in `opentelemetry-instrumentation-flask` - ([#604](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/604)) - -## Version 1.4.0/0.23b0 (2021-07-21) - -### Removed - -- Move `opentelemetry-instrumentation` to the core repo. - ([#595](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/595)) - -### Changed - -- `opentelemetry-instrumentation-falcon` added support for Falcon 3. - ([#607](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/607)) -- `opentelemetry-instrumentation-tornado` properly instrument work done in tornado on_finish method. - ([#499](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/499)) -- `opentelemetry-instrumentation` Fixed cases where trying to use an instrumentation package without the - target library was crashing auto instrumentation agent. - ([#530](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/530)) -- Fix weak reference error for pyodbc cursor in SQLAlchemy instrumentation. - ([#469](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/469)) -- Implemented specification that HTTP span attributes must not contain username and password. - ([#538](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/538)) -- Changed the psycopg2-binary to psycopg2 as dependency in production - ([#543](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/543)) -- Implement consistent way of checking if instrumentation is already active - ([#549](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/549)) -- Require aiopg to be less than 1.3.0 - ([#560](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/560)) -- `opentelemetry-instrumentation-django` Migrated Django middleware to new-style. - ([#533](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/533)) -- Updating dependency for opentelemetry api/sdk packages to support major version instead - of pinning to specific versions. - ([#567](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/567)) -- `opentelemetry-instrumentation-grpc` Respect the suppress instrumentation in gRPC client instrumentor - ([#559](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/559)) -- `opentelemetry-instrumentation-grpc` Fixed asynchronous unary call traces - ([#536](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/536)) -- `opentelemetry-sdk-extension-aws` Update AWS entry points to match spec - ([#566](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/566)) -- Include Flask 2.0 as compatible with existing flask instrumentation - ([#545](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/545)) -- `openelemetry-sdk-extension-aws` Take a dependency on `opentelemetry-sdk` - ([#558](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/558)) -- Change `opentelemetry-instrumentation-httpx` to replace `client` classes with instrumented versions. - ([#577](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/577)) -- `opentelemetry-instrumentation-requests` Fix potential `AttributeError` when `requests` - is used with a custom transport adapter. - ([#562](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/562)) -- `opentelemetry-instrumentation-django` Fix AttributeError: ResolverMatch object has no attribute route - ([#581](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/581)) -- `opentelemetry-instrumentation-botocore` Suppress botocore downstream instrumentation like urllib3 - ([#563](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/563)) -- `opentelemetry-exporter-datadog` Datadog exporter should not use `unknown_service` as fallback resource service name. - ([#570](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/570)) -- Add support for the async extension of SQLAlchemy (>= 1.4) - ([#568](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/568)) - -### Added - -- `opentelemetry-instrumentation-httpx` Add `httpx` instrumentation - ([#461](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/461)) - -## Version 1.3.0/0.22b0 (2021-06-01) - -### Changed - -- `opentelemetry-bootstrap` not longer forcibly removes and re-installs libraries and their instrumentations. - This means running bootstrap will not auto-upgrade existing dependencies and as a result not cause dependency - conflicts. - ([#514](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/514)) -- `opentelemetry-instrumentation-asgi` Set the response status code on the server span - ([#478](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/478)) -- `opentelemetry-instrumentation-tornado` Fixed cases where description was used with non- - error status code when creating Status objects. - ([#504](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/504)) -- `opentelemetry-instrumentation-asgi` Fix instrumentation default span name. - ([#418](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/418)) -- Propagators use the root context as default for `extract` and do not modify - the context if extracting from carrier does not work. - ([#488](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/488)) - -### Added - -- `opentelemetry-instrumentation-botocore` now supports - context propagation for lambda invoke via Payload embedded headers. - ([#458](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/458)) -- Added support for CreateKey functionality. - ([#502](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/502)) - -## Version 1.2.0/0.21b0 (2021-05-11) - -### Changed - -- Instrumentation packages don't specify the libraries they instrument as dependencies - anymore. Instead, they verify the correct version of libraries are installed at runtime. - ([#475](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/475)) -- `opentelemetry-propagator-ot-trace` Use `TraceFlags` object in `extract` - ([#472](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/472)) -- Set the `traced_request_attrs` of FalconInstrumentor by an argument correctly. - ([#473](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/473)) -- Enable passing explicit urls to exclude in instrumentation in FastAPI - ([#486](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/486)) -- Distros can now implement `load_instrumentor(EntryPoint)` method to customize instrumentor - loading behaviour. - ([#480](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/480)) -- Fix entrypoint for ottrace propagator - ([#492](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/492)) - -### Added - -- Move `opentelemetry-instrumentation` from core repository - ([#465](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/465)) - -## Version 0.20b0 (2021-04-20) - -### Changed - -- Restrict DataDog exporter's `ddtrace` dependency to known working versions. - ([#400](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/400)) -- GRPC instrumentation now correctly injects trace context into outgoing requests. - ([#392](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/39)) -- Publish `opentelemetry-propagator-ot-trace` package as a part of the release process - ([#387](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/387)) -- Update redis instrumentation to follow semantic conventions - ([#403](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/403)) -- Update instrumentations to use tracer_provider for creating tracer if given, otherwise use global tracer provider - ([#402](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/402)) -- `opentelemetry-instrumentation-wsgi` Replaced `name_callback` with `request_hook` - and `response_hook` callbacks. - ([#424](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/424)) -- Update gRPC instrumentation to better wrap server context - ([#420](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/420)) -- `opentelemetry-instrumentation-redis` Fix default port KeyError and Wrong Attribute name (net.peer.ip -> net.peer.port) - ([#265](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/265)) -- `opentelemetry-instrumentation-asyncpg` Fix default port KeyError and Wrong Attribute name (net.peer.ip -> net.peer.port) - ([#265](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/265)) - -### Added - -- `opentelemetry-instrumentation-urllib3` Add urllib3 instrumentation - ([#299](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/299)) - -- `opentelemetry-instrumentation-flask` Added `request_hook` and `response_hook` callbacks. - ([#416](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/416)) - -- `opentelemetry-instrumenation-django` now supports request and response hooks. - ([#407](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/407)) -- `opentelemetry-instrumentation-falcon` FalconInstrumentor now supports request/response hooks. - ([#415](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/415)) -- `opentelemetry-instrumentation-tornado` Add request/response hooks. - ([#426](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/426)) -- `opentelemetry-exporter-datadog` Add parsing exception events for error tags. - ([#459](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/459)) -- `opentelemetry-instrumenation-django` now supports trace response headers. - ([#436](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/436)) -- `opentelemetry-instrumenation-tornado` now supports trace response headers. - ([#436](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/436)) -- `opentelemetry-instrumenation-pyramid` now supports trace response headers. - ([#436](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/436)) -- `opentelemetry-instrumenation-falcon` now supports trace response headers. - ([#436](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/436)) -- `opentelemetry-instrumenation-flask` now supports trace response headers. - ([#436](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/436)) -- `opentelemetry-instrumentation-grpc` Keep client interceptor in sync with grpc client interceptors. - ([#442](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/442)) - -### Removed - -- Remove `http.status_text` from span attributes - ([#406](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/406)) - -## Version 0.19b0 (2021-03-26) - -- Implement context methods for `_InterceptorChannel` - ([#363](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/363)) - -### Changed - -- Rename `IdsGenerator` to `IdGenerator` - ([#350](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/350)) -- `opentelemetry-exporter-datadog` Fix warning when DatadogFormat encounters a request with - no DD_ORIGIN headers ([#368](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/368)). -- `opentelemetry-instrumentation-aiopg` Fix multiple nested spans when - `aiopg.pool` is used - ([#336](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/381)). -- Updated instrumentations to use `opentelemetry.trace.use_span` instead of `Tracer.use_span()` - ([#364](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/364)) -- `opentelemetry-propagator-ot-trace` Do not throw an exception when headers are not present - ([#378](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/378)) -- `opentelemetry-instrumentation-wsgi` Reimplement `keys` method to return actual keys from the carrier instead of an empty list. - ([#379](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/379)) -- `opentelemetry-instrumentation-sqlalchemy` Fix multithreading issues in recording spans from SQLAlchemy - ([#315](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/315)) -- Make getters and setters optional - ([#372](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/372)) - -### Removed - -- Removing support for Python 3.5 - ([#374](https://github.com/open-telemetry/opentelemetry-python/pull/374)) - -## Version 0.18b0 (2021-02-16) - -### Added - -- `opentelemetry-propagator-ot-trace` Add OT Trace Propagator - ([#302](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/302)) -- `opentelemetry-instrumentation-logging` Added logging instrumentation to enable log - trace correlation. - ([#345](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/345)) - -### Removed - -- Remove `component` span attribute in instrumentations. - `opentelemetry-instrumentation-aiopg`, `opentelemetry-instrumentation-dbapi` Remove unused `database_type` parameter from `trace_integration` function. - ([#301](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/301)) -- `opentelemetry-instrumentation-asgi` Return header values using case insensitive keys - ([#308](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/308)) -- Remove metrics from all instrumentations - ([#312](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/312)) -- `opentelemetry-instrumentation-boto` updated to set span attributes instead of overriding the resource. - ([#310](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/310)) -- `opentelemetry-instrumentation-grpc` Fix issue tracking child spans in streaming responses - ([#260](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/260)) -- `opentelemetry-instrumentation-grpc` Updated client attributes, added tests, fixed examples, docs - ([#269](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/269)) - -## Version 0.17b0 (2021-01-20) - -### Added - -- `opentelemetry-instrumentation-sqlalchemy` Ensure spans have kind set to "CLIENT" - ([#278](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/278)) -- `opentelemetry-instrumentation-celery` Add support for Celery version 5.x - ([#266](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/266)) -- `opentelemetry-instrumentation-urllib` Add urllib instrumentation - ([#222](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/222)) -- `opentelemetry-exporter-datadog` Add fields method - ([#226](https://github.com/open-telemetry/opentelemetry-python/pull/226)) -- `opentelemetry-sdk-extension-aws` Add method to return fields injected by propagator - ([#226](https://github.com/open-telemetry/opentelemetry-python/pull/226)) -- `opentelemetry-exporter-prometheus-remote-write` Prometheus Remote Write Exporter Setup - ([#180](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/180)) -- `opentelemetry-exporter-prometheus-remote-write` Add Exporter constructor validation methods in Prometheus Remote Write Exporter - ([#206](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/206)) -- `opentelemetry-exporter-prometheus-remote-write` Add conversion to TimeSeries methods in Prometheus Remote Write Exporter - ([#207](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/207)) -- `opentelemetry-exporter-prometheus-remote-write` Add request methods to Prometheus Remote Write Exporter - ([#212](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/212)) -- `opentelemetry-instrumentation-fastapi` Added support for excluding some routes with env var `OTEL_PYTHON_FASTAPI_EXCLUDED_URLS` - ([#237](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/237)) -- `opentelemetry-instrumentation-starlette` Added support for excluding some routes with env var `OTEL_PYTHON_STARLETTE_EXCLUDED_URLS` - ([#237](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/237)) -- Add Prometheus Remote Write Exporter integration tests in opentelemetry-docker-tests - ([#216](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/216)) -- `opentelemetry-instrumentation-grpc` Add tests for grpc span attributes, grpc `abort()` conditions - ([#236](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/236)) -- Add README and example app for Prometheus Remote Write Exporter - ([#227](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/227])) -- `opentelemetry-instrumentation-botocore` Adds a field to report the number of retries it take to complete an API call - ([#275](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/275)) -- `opentelemetry-instrumentation-requests` Use instanceof to check if responses are valid Response objects - ([#273](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/273)) - -### Changed - -- Fix broken links to project ([#413](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/413)) -- `opentelemetry-instrumentation-asgi`, `opentelemetry-instrumentation-wsgi` Return `None` for `CarrierGetter` if key not found - ([#233](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/233)) -- `opentelemetry-instrumentation-grpc` Comply with updated spec, rework tests - ([#236](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/236)) -- `opentelemetry-instrumentation-asgi`, `opentelemetry-instrumentation-falcon`, `opentelemetry-instrumentation-flask`, `opentelemetry-instrumentation-pyramid`, `opentelemetry-instrumentation-wsgi` Renamed `host.port` attribute to `net.host.port` - ([#242](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/242)) -- `opentelemetry-instrumentation-flask` Do not emit a warning message for request contexts created with `app.test_request_context` - ([#253](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/253)) -- `opentelemetry-instrumentation-requests`, `opentelemetry-instrumentation-urllib` Fix span name callback parameters - ([#259](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/259)) -- `opentelemetry-exporter-datadog` Fix unintentional type change of span trace flags - ([#261](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/261)) -- `opentelemetry-instrumentation-aiopg` Fix AttributeError `__aexit__` when `aiopg.connect` and `aio[g].create_pool` used with async context manager - ([#235](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/235)) -- `opentelemetry-exporter-datadog` `opentelemetry-sdk-extension-aws` Fix reference to ids_generator in sdk - ([#283](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/283)) -- `opentelemetry-instrumentation-sqlalchemy` Use SQL operation and DB name as span name. - ([#254](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/254)) -- `opentelemetry-instrumentation-dbapi`, `TracedCursor` replaced by `CursorTracer` - ([#246](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/246)) -- `opentelemetry-instrumentation-psycopg2`, Added support for psycopg2 registered types. - ([#246](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/246)) -- `opentelemetry-instrumentation-dbapi`, `opentelemetry-instrumentation-psycopg2`, `opentelemetry-instrumentation-mysql`, `opentelemetry-instrumentation-pymysql`, `opentelemetry-instrumentation-aiopg` Use SQL command name as the span operation name instead of the entire query. - ([#246](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/246)) -- Update TraceState to adhere to specs - ([#276](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/276)) - -### Removed - -- Remove Configuration - ([#285](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/285)) - -## Version 0.16b1 (2020-11-26) - -## Version 0.16b0 (2020-11-25) - -### Added - -- `opentelemetry-instrumentation-flask` Add span name callback - ([#152](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/152)) -- `opentelemetry-sdk-extension-aws` Add AWS X-Ray Ids Generator Entry Point - ([#201](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/201)) -- `opentelemetry-sdk-extension-aws` Fix typo for installing OTel SDK in docs - ([#200](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/200)) -- `opentelemetry-sdk-extension-aws` Import missing components for docs - ([#198](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/198)) -- `opentelemetry-sdk-extension-aws` Provide components needed to Configure OTel SDK for Tracing with AWS X-Ray - ([#130](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/130)) -- `opentelemetry-instrumentation-sklearn` Initial release - ([#151](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/151)) -- `opentelemetry-instrumentation-requests` Add span name callback - ([#158](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/158)) -- `opentelemetry-instrumentation-botocore` Add propagator injection for botocore calls - ([#181](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/181)) - -### Changed - -- `opentelemetry-instrumentation-pymemcache` Update pymemcache instrumentation to follow semantic conventions - ([#183](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/183)) -- `opentelemetry-instrumentation-redis` Update redis instrumentation to follow semantic conventions - ([#184](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/184)) -- `opentelemetry-instrumentation-pymongo` Update pymongo instrumentation to follow semantic conventions - ([#203](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/203)) -- `opentelemetry-instrumentation-sqlalchemy` Update sqlalchemy instrumentation to follow semantic conventions - ([#202](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/202)) -- `opentelemetry-instrumentation-botocore` Make botocore instrumentation check if instrumentation has been suppressed - ([#182](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/182)) -- `opentelemetry-instrumentation-botocore` Botocore SpanKind as CLIENT and modify existing traced attributes - ([#150](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/150)) -- `opentelemetry-instrumentation-dbapi` Update dbapi and its dependent instrumentations to follow semantic conventions - ([#195](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/195)) -- `opentelemetry-instrumentation-dbapi` Stop capturing query parameters by default - ([#156](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/156)) -- `opentelemetry-instrumentation-asyncpg` Update asyncpg instrumentation to follow semantic conventions - ([#188](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/188)) -- `opentelemetry-instrumentation-grpc` Update protobuf versions - ([#1356](https://github.com/open-telemetry/opentelemetry-python/pull/1356)) - -## Version 0.15b0 (2020-11-02) - -### Added - -- `opentelemetry-instrumentation-requests` Add support for tracking http metrics - ([#1230](https://github.com/open-telemetry/opentelemetry-python/pull/1230)) -- `opentelemetry-instrumentation-django` Added capture of http.route - ([#1226](https://github.com/open-telemetry/opentelemetry-python/issues/1226)) -- `opentelemetry-instrumentation-django` Add support for tracking http metrics - ([#1230](https://github.com/open-telemetry/opentelemetry-python/pull/1230)) - -### Changed - -- `opentelemetry-exporter-datadog` Make `SpanProcessor.on_start` accept parent Context - ([#1251](https://github.com/open-telemetry/opentelemetry-python/pull/1251)) -- `opentelemetry-instrumentation-flask` Use `url.rule` instead of `request.endpoint` for span name - ([#1260](https://github.com/open-telemetry/opentelemetry-python/pull/1260)) -- `opentelemetry-instrumentation-django` Django instrumentation is now enabled by default but can be disabled by setting `OTEL_PYTHON_DJANGO_INSTRUMENT` to `False` - ([#1239](https://github.com/open-telemetry/opentelemetry-python/pull/1239)) -- `opentelemetry-instrumentation-django` Bugfix use request.path replace request.get_full_path(). It will get correct span name - ([#1309](https://github.com/open-telemetry/opentelemetry-python/pull/1309#)) -- `opentelemetry-instrumentation-django` Record span status and http.status_code attribute on exception - ([#1257](https://github.com/open-telemetry/opentelemetry-python/pull/1257)) -- `opentelemetry-instrumentation-grpc` Rewrite gRPC server interceptor - ([#1171](https://github.com/open-telemetry/opentelemetry-python/pull/1171)) - -## Version 0.14b0 (2020-10-13) - -### Added - -- `opentelemetry-exporter-datadog` Add support for span resource labels and service name -- `opentelemetry-instrumentation-celery` Span operation names now include the task type. - ([#1135](https://github.com/open-telemetry/opentelemetry-python/pull/1135)) -- `opentelemetry-instrumentation-celery` Added automatic context propagation. - ([#1135](https://github.com/open-telemetry/opentelemetry-python/pull/1135)) -- `opentelemetry-instrumentation-falcon` Added support for `OTEL_PYTHON_FALCON_TRACED_REQUEST_ATTRS` - ([#1158](https://github.com/open-telemetry/opentelemetry-python/pull/1158)) -- `opentelemetry-instrumentation-tornado` Added support for `OTEL_PYTHON_TORNADO_TRACED_REQUEST_ATTRS` - ([#1178](https://github.com/open-telemetry/opentelemetry-python/pull/1178)) -- `opentelemetry-instrumentation-django` Added support for `OTEL_PYTHON_DJANGO_TRACED_REQUEST_ATTRS` - ([#1154](https://github.com/open-telemetry/opentelemetry-python/pull/1154)) - -### Changed - -- `opentelemetry-instrumentation-pymongo` Cast PyMongo commands as strings - ([#1132](https://github.com/open-telemetry/opentelemetry-python/pull/1132)) -- `opentelemetry-instrumentation-system-metrics` Fix issue when specific metrics are not available in certain OS - ([#1207](https://github.com/open-telemetry/opentelemetry-python/pull/1207)) -- `opentelemetry-instrumentation-pymysql` Bumped version from 0.9.3 to 0.10.1 - ([#1228](https://github.com/open-telemetry/opentelemetry-python/pull/1228)) -- `opentelemetry-instrumentation-django` Changed span name extraction from request to comply semantic convention - ([#992](https://github.com/open-telemetry/opentelemetry-python/pull/992)) - -## Version 0.13b0 (2020-09-17) - -### Added - -- `opentelemetry-instrumentation-falcon` Initial release. Added instrumentation for Falcon 2.0+ -- `opentelemetry-instrumentation-tornado` Initial release. Supports Tornado 6.x on Python 3.5 and newer. -- `opentelemetry-instrumentation-aiohttp-client` Add instrumentor and auto instrumentation support for aiohttp - ([#1075](https://github.com/open-telemetry/opentelemetry-python/pull/1075)) -- `opentelemetry-instrumentation-requests` Add support for instrumenting prepared requests - ([#1040](https://github.com/open-telemetry/opentelemetry-python/pull/1040)) -- `opentelemetry-instrumentation-requests` Add support for http metrics - ([#1116](https://github.com/open-telemetry/opentelemetry-python/pull/1116)) - -### Changed - -- `opentelemetry-instrumentation-aiohttp-client` Updating span name to match semantic conventions - ([#972](https://github.com/open-telemetry/opentelemetry-python/pull/972)) -- `opentelemetry-instrumentation-dbapi` cursors and connections now produce spans when used with context managers - ([#1028](https://github.com/open-telemetry/opentelemetry-python/pull/1028)) - -### Removed - -- Drop support for Python 3.4 - ([#1099](https://github.com/open-telemetry/opentelemetry-python/pull/1099)) - -## Version 0.12b0 (2020-08-14) - -### Changed - -- `opentelemetry-ext-pymemcache` Change package name to opentelemetry-instrumentation-pymemcache - ([#966](https://github.com/open-telemetry/opentelemetry-python/pull/966)) -- `opentelemetry-ext-redis` Update default SpanKind to `SpanKind.CLIENT` - ([#965](https://github.com/open-telemetry/opentelemetry-python/pull/965)) -- `opentelemetry-ext-redis` Change package name to opentelemetry-instrumentation-redis - ([#966](https://github.com/open-telemetry/opentelemetry-python/pull/966)) -- `opentelemetry-ext-datadog` Change package name to opentelemetry-exporter-datadog - ([#953](https://github.com/open-telemetry/opentelemetry-python/pull/953)) -- `opentelemetry-ext-jinja2` Change package name to opentelemetry-instrumentation-jinja2 - ([#969](https://github.com/open-telemetry/opentelemetry-python/pull/969)) -- `opentelemetry-ext-elasticsearch` Update environment variable names, prefix changed from `OPENTELEMETRY` to `OTEL` - ([#904](https://github.com/open-telemetry/opentelemetry-python/pull/904)) -- `opentelemetry-ext-elasticsearch` Change package name to opentelemetry-instrumentation-elasticsearch - ([#969](https://github.com/open-telemetry/opentelemetry-python/pull/969)) -- `opentelemetry-ext-celery` Change package name to opentelemetry-instrumentation-celery - ([#969](https://github.com/open-telemetry/opentelemetry-python/pull/969)) -- `opentelemetry-ext-pyramid` Change package name to opentelemetry-instrumentation-pyramid - ([#966](https://github.com/open-telemetry/opentelemetry-python/pull/966)) -- `opentelemetry-ext-pyramid` Update environment variable names, prefix changed from `OPENTELEMETRY` to `OTEL` - ([#904](https://github.com/open-telemetry/opentelemetry-python/pull/904)) -- `opentelemetry-ext-pymongo` Change package name to opentelemetry-instrumentation-pymongo - ([#966](https://github.com/open-telemetry/opentelemetry-python/pull/966)) -- `opentelemetry-ext-sqlite3` Change package name to opentelemetry-instrumentation-sqlite3 - ([#966](https://github.com/open-telemetry/opentelemetry-python/pull/966)) -- `opentelemetry-ext-sqlalchemy` Change package name to opentelemetry-instrumentation-sqlalchemy - ([#966](https://github.com/open-telemetry/opentelemetry-python/pull/966)) -- `opentelemetry-ext-psycopg2` Change package name to opentelemetry-instrumentation-psycopg2 - ([#966](https://github.com/open-telemetry/opentelemetry-python/pull/966)) -- `opentelemetry-ext-aiohttp-client` Change package name to opentelemetry-instrumentation-aiohttp-client - ([#961](https://github.com/open-telemetry/opentelemetry-python/pull/961)) -- `opentelemetry-ext-boto` Change package name to opentelemetry-instrumentation-boto - ([#969](https://github.com/open-telemetry/opentelemetry-python/pull/969)) -- `opentelemetry-ext-system-metrics` Change package name to opentelemetry-instrumentation-system-metrics - ([#969](https://github.com/open-telemetry/opentelemetry-python/pull/969)) -- `opentelemetry-ext-asgi` Change package name to opentelemetry-instrumentation-asgi - ([#961](https://github.com/open-telemetry/opentelemetry-python/pull/961)) -- `opentelemetry-ext-wsgi` Change package name to opentelemetry-instrumentation-wsgi - ([#961](https://github.com/open-telemetry/opentelemetry-python/pull/961)) -- `opentelemetry-ext-pymysql` Change package name to opentelemetry-instrumentation-pymysql - ([#966](https://github.com/open-telemetry/opentelemetry-python/pull/966)) -- `opentelemetry-ext-requests` Change package name to opentelemetry-instrumentation-requests - ([#961](https://github.com/open-telemetry/opentelemetry-python/pull/961)) -- `opentelemetry-ext-requests` Span name reported updated to follow semantic conventions to reduce - cardinality ([#972](https://github.com/open-telemetry/opentelemetry-python/pull/972)) -- `opentelemetry-ext-botocore` Change package name to opentelemetry-instrumentation-botocore - ([#969](https://github.com/open-telemetry/opentelemetry-python/pull/969)) -- `opentelemetry-ext-dbapi` Change package name to opentelemetry-instrumentation-dbapi - ([#966](https://github.com/open-telemetry/opentelemetry-python/pull/966)) -- `opentelemetry-ext-flask` Change package name to opentelemetry-instrumentation-flask - ([#961](https://github.com/open-telemetry/opentelemetry-python/pull/961)) -- `opentelemetry-ext-flask` Update environment variable names, prefix changed from `OPENTELEMETRY` to `OTEL` - ([#904](https://github.com/open-telemetry/opentelemetry-python/pull/904)) -- `opentelemetry-ext-django` Change package name to opentelemetry-instrumentation-django - ([#961](https://github.com/open-telemetry/opentelemetry-python/pull/961)) -- `opentelemetry-ext-django` Update environment variable names, prefix changed from `OPENTELEMETRY` to `OTEL` - ([#904](https://github.com/open-telemetry/opentelemetry-python/pull/904)) -- `opentelemetry-ext-asyncpg` Change package name to opentelemetry-instrumentation-asyncpg - ([#966](https://github.com/open-telemetry/opentelemetry-python/pull/966)) -- `opentelemetry-ext-mysql` Change package name to opentelemetry-instrumentation-mysql - ([#966](https://github.com/open-telemetry/opentelemetry-python/pull/966)) -- `opentelemetry-ext-grpc` Change package name to opentelemetry-instrumentation-grpc - ([#969](https://github.com/open-telemetry/opentelemetry-python/pull/969)) - -## Version 0.11b0 (2020-07-28) - -### Added - -- `opentelemetry-instrumentation-aiopg` Initial release -- `opentelemetry-instrumentation-fastapi` Initial release - ([#890](https://github.com/open-telemetry/opentelemetry-python/pull/890)) -- `opentelemetry-ext-grpc` Add status code to gRPC client spans - ([896](https://github.com/open-telemetry/opentelemetry-python/pull/896)) -- `opentelemetry-ext-grpc` Add gRPC client and server instrumentors - ([788](https://github.com/open-telemetry/opentelemetry-python/pull/788)) -- `opentelemetry-ext-grpc` Add metric recording (bytes in/out, errors, latency) to gRPC client - -### Changed - -- `opentelemetry-ext-pyramid` Use one general exclude list instead of two - ([#872](https://github.com/open-telemetry/opentelemetry-python/pull/872)) -- `opentelemetry-ext-boto` fails to export spans via jaeger - ([#866](https://github.com/open-telemetry/opentelemetry-python/pull/866)) -- `opentelemetry-ext-botocore` fails to export spans via jaeger - ([#866](https://github.com/open-telemetry/opentelemetry-python/pull/866)) -- `opentelemetry-ext-wsgi` Set span status on wsgi errors - ([#864](https://github.com/open-telemetry/opentelemetry-python/pull/864)) -- `opentelemetry-ext-flask` Use one general exclude list instead of two - ([#872](https://github.com/open-telemetry/opentelemetry-python/pull/872)) -- `opentelemetry-ext-django` Use one general exclude list instead of two - ([#872](https://github.com/open-telemetry/opentelemetry-python/pull/872)) -- `opentelemetry-ext-asyncpg` Shouldn't capture query parameters by default - ([#854](https://github.com/open-telemetry/opentelemetry-python/pull/854)) -- `opentelemetry-ext-mysql` bugfix: Fix auto-instrumentation entry point for mysql - ([#858](https://github.com/open-telemetry/opentelemetry-python/pull/858)) - -## Version 0.10b0 (2020-06-23) - -### Added - -- `opentelemetry-ext-pymemcache` Initial release -- `opentelemetry-ext-elasticsearch` Initial release -- `opentelemetry-ext-celery` Add instrumentation for Celery - ([#780](https://github.com/open-telemetry/opentelemetry-python/pull/780)) -- `opentelemetry-instrumentation-starlette` Initial release - ([#777](https://github.com/open-telemetry/opentelemetry-python/pull/777)) -- `opentelemetry-ext-asyncpg` Initial Release - ([#814](https://github.com/open-telemetry/opentelemetry-python/pull/814)) - -## Version 0.9b0 (2020-06-10) - -### Added - -- `opentelemetry-ext-pyramid` Initial release -- `opentelemetry-ext-boto` Initial release -- `opentelemetry-ext-botocore` Initial release -- `opentelemetry-ext-system-metrics` Initial release - (https://github.com/open-telemetry/opentelemetry-python/pull/652) - -## Version 0.8b0 (2020-05-27) - -### Added - -- `opentelemetry-ext-datadog` Add exporter to Datadog - ([#572](https://github.com/open-telemetry/opentelemetry-python/pull/572)) -- `opentelemetry-ext-sqlite3` Initial release -- `opentelemetry-ext-psycopg2` Implement instrumentor interface, enabling auto-instrumentation - ([#694](https://github.com/open-telemetry/opentelemetry-python/pull/694)) -- `opentelemetry-ext-asgi` Add ASGI middleware - ([#716](https://github.com/open-telemetry/opentelemetry-python/pull/716)) -- `opentelemetry-ext-django` Add exclude list for paths and hosts to prevent from tracing - ([#670](https://github.com/open-telemetry/opentelemetry-python/pull/670)) -- `opentelemetry-ext-django` Add support for django >= 1.10 (#717) - -### Changed - -- `opentelemetry-ext-grpc` lint: version of grpc causes lint issues - ([#696](https://github.com/open-telemetry/opentelemetry-python/pull/696)) - -## Version 0.7b1 (2020-05-12) - -### Added - -- `opentelemetry-ext-redis` Initial release -- `opentelemetry-ext-jinja2` Add jinja2 instrumentation - ([#643](https://github.com/open-telemetry/opentelemetry-python/pull/643)) -- `opentelemetry-ext-pymongo` Implement instrumentor interface - ([#612](https://github.com/open-telemetry/opentelemetry-python/pull/612)) -- `opentelemetry-ext-sqlalchemy` Initial release -- `opentelemetry-ext-aiohttp-client` Initial release -- `opentelemetry-ext-pymysql` Initial release -- `opentelemetry-ext-http-requests` Implement instrumentor interface, enabling auto-instrumentation - ([#597](https://github.com/open-telemetry/opentelemetry-python/pull/597)) -- `opentelemetry-ext-http-requests` Adding disable_session for more granular instrumentation control - ([#573](https://github.com/open-telemetry/opentelemetry-python/pull/573)) -- `opentelemetry-ext-http-requests` Add a callback for custom attributes - ([#656](https://github.com/open-telemetry/opentelemetry-python/pull/656)) -- `opentelemetry-ext-dbapi` Implement instrument_connection and uninstrument_connection - ([#624](https://github.com/open-telemetry/opentelemetry-python/pull/624)) -- `opentelemetry-ext-flask` Add exclude list for paths and hosts - ([#630](https://github.com/open-telemetry/opentelemetry-python/pull/630)) -- `opentelemetry-ext-django` Initial release -- `opentelemetry-ext-mysql` Implement instrumentor interface - ([#654](https://github.com/open-telemetry/opentelemetry-python/pull/654)) - -### Changed - -- `opentelemetry-ext-http-requests` Rename package to opentelemetry-ext-requests - ([#619](https://github.com/open-telemetry/opentelemetry-python/pull/619)) - -## Version 0.6b0 (2020-03-30) - -### Added - -- `opentelemetry-ext-flask` Add an entry_point to be usable in auto-instrumentation - ([#327](https://github.com/open-telemetry/opentelemetry-python/pull/327)) -- `opentelemetry-ext-grpc` Add gRPC integration - ([#476](https://github.com/open-telemetry/opentelemetry-python/pull/476)) - -## Version 0.5b0 (2020-03-16) - -## Version 0.4a0 (2020-02-21) - -### Added - -- `opentelemetry-ext-psycopg2` Initial release -- `opentelemetry-ext-dbapi` Initial release -- `opentelemetry-ext-mysql` Initial release - -### Changed - -- `opentelemetry-ext-pymongo` Updating network connection attribute names - ([#350](https://github.com/open-telemetry/opentelemetry-python/pull/350)) -- `opentelemetry-ext-wsgi` Updating network connection attribute names - ([#350](https://github.com/open-telemetry/opentelemetry-python/pull/350)) -- `opentelemetry-ext-flask` Use string keys for WSGI environ values - ([#366](https://github.com/open-telemetry/opentelemetry-python/pull/366)) - -## Version 0.3a0 (2019-12-11) - -### Added - -- `opentelemetry-ext-flask` Initial release -- `opentelemetry-ext-pymongo` Initial release - -### Changed - -- `opentelemetry-ext-wsgi` Support new semantic conventions - ([#299](https://github.com/open-telemetry/opentelemetry-python/pull/299)) -- `opentelemetry-ext-wsgi` Updates for core library changes - -## Version 0.2a0 (2019-10-29) - -### Changed - -- `opentelemetry-ext-wsgi` Updates for core library changes -- `opentelemetry-ext-http-requests` Updates for core library changes - -- `Added support for PyPy3` Initial release - -## [#1033](https://github.com/open-telemetryopentelemetry-python-contrib/issues/1033) - -## Version 0.1a0 (2019-09-30) - -### Added - -- `opentelemetry-ext-wsgi` Initial release -- `opentelemetry-ext-http-requests` Initial release - -- Drop support for 3.7 - ([#2151](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2151)) -- `opentelemetry-resource-detector-azure` Added 10s timeout to VM Resource Detector - ([#2119](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2119)) -- `opentelemetry-instrumentation-asyncpg` Allow AsyncPGInstrumentor to be instantiated multiple times - ([#1791](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1791)) -- `opentelemetry-instrumentation-confluent-kafka` Add support for higher versions until 2.3.0 of confluent_kafka - ([#2132](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2132)) -- `opentelemetry-resource-detector-azure` Changed timeout to 4 seconds due to [timeout bug](https://github.com/open-telemetry/opentelemetry-python/issues/3644) - ([#2136](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2136)) -- `opentelemetry-resource-detector-azure` Suppress instrumentation for `urllib` call - ([#2178](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2178)) -- AwsLambdaInstrumentor handles and re-raises function exception ([#2245](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2245)) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0043d2155..9cbbe7aa3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,17 +1,2224 @@ -# Changelog for LoongSuite +# Changelog -All notable changes to loongsuite components will be documented in this file. +All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). > [!NOTE] > The following components are released independently and maintain individual CHANGELOG files. -> Use [this search for a list of all CHANGELOG.md files in this repo](https://github.com/search?q=repo%3Aalibaba%2Floongsuite-python-agent+path%3A**%2FCHANGELOG.md&type=code). +> Use [this search for a list of all CHANGELOG.md files in this repo](https://github.com/search?q=repo%3Aopen-telemetry%2Fopentelemetry-python-contrib+path%3A**%2FCHANGELOG.md&type=code). ## Unreleased -# Added +- `opentelemetry-instrumentation-requests`, `opentelemetry-instrumentation-wsgi`, `opentelemetry-instrumentation-asgi` Detect synthetic sources on requests, ASGI, and WSGI. + ([#3674](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3674)) -- `loongsuite-instrumentation-mem0`: add support for mem0 - ([#67](https://github.com/alibaba/loongsuite-python-agent/pull/67)) +### Added + +- `opentelemetry-instrumentation-aiohttp-client`: add support for url exclusions via `OTEL_PYTHON_EXCLUDED_URLS` / `OTEL_PYTHON_AIOHTTP_CLIENT_EXCLUDED_URLS` + ([#3850](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3850)) +- `opentelemetry-instrumentation-httpx`: add support for url exclusions via `OTEL_PYTHON_EXCLUDED_URLS` / `OTEL_PYTHON_HTTPX_EXCLUDED_URLS` + ([#3837](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3837)) +- `opentelemetry-instrumentation-flask`: improve readthedocs for sqlcommenter configuration. + ([#3883](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3883)) +- `opentelemetry-instrumentation-sqlalchemy`: improve readthedocs for sqlcommenter configuration. + ([#3886](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3886)) +- `opentelemetry-instrumentation-mysql`, `opentelemetry-instrumentation-mysqlclient`, `opentelemetry-instrumentation-pymysql`: improve readthedocs for sqlcommenter configuration. + ([#3885](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3885)) +- `opentelemetry-instrumentation-django`: improve readthedocs for sqlcommenter configuration. + ([#3884](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3884)) +- `opentelemetry-instrumentation-aiohttp-server`: add support for custom header captures via `OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_REQUEST` and `OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_RESPONSE` + ([#3916](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3916)) +- `opentelemetry-instrumentation-redis`: add support for `suppress_instrumentation` context manager for both sync and async Redis clients and pipelines +- `opentelemetry-instrumentation-django`: improve docs for response_hook with examples of providing attributes from middlewares + ([#3923](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3923)) +- Update for Log SDK breaking changes. Rename InMemoryLogExporter to InMemoryLogRecordExporter in several tests + ([#3589](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3589)) +- opentelemetry-instrumentation: allow to skip all instrumentations loading with a wildcard + ([#3967](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3967)) +- `opentelemetry-instrumentation-redis`: add missing copyright header for opentelemetry-instrumentation-redis + ([#3976](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3976)) + +### Fixed + +- `opentelemetry-instrumentation-botocore`: bedrock: only decode JSON input buffer in Anthropic Claude streaming + ([#3875](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3875)) +- `opentelemetry-instrumentation-aiohttp-client`, `opentelemetry-instrumentation-aiohttp-server`: Fix readme links and text + ([#3902](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3902)) +- `opentelemetry-instrumentation-aws-lambda`: Fix ImportError with slash-delimited handler paths + ([#3894](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3894)) +- `opentelemetry-exporter-richconsole`: Prevent deadlock when parent span is not part of the batch + ([#3900](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3900)) +- `opentelemetry-instrumentation-psycopg2`, `opentelemetry-instrumentation-psycopg`: improve readthedocs for sqlcommenter configuration. + ([#3882](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3882)) +- `opentelemetry-instrumentation-aiohttp-server`: delay initialization of tracer, meter and excluded urls to instrumentation for testability + ([#3836](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3836)) +- Replace Python 3.14-deprecated `asyncio.iscoroutinefunction` with `inspect.iscoroutinefunction`. + ([#3880](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3880)) +- `opentelemetry-instrumentation-elasticsearch`: Enhance elasticsearch query body sanitization + ([#3919](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3919)) +- `opentelemetry-instrumentation-pymongo`: Fix span error descriptions + ([#3904](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3904)) +- build: bump ruff to 0.14.1 + ([#3842](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3842)) +- `opentelemetry-instrumentation-redis`: Add default span name for pipeline operations + ([#3941](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3941)) +- `opentelemetry-instrumentation-pymongo`: Fix invalid mongodb collection attribute type + ([#3942](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3942)) +- `opentelemetry-instrumentation-aiohttp-client`: Fix metric attribute leakage + ([#3936](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3936)) +- `opentelemetry-instrumentation-aiohttp-client`: Update instrumentor to respect suppressing http instrumentation + ([#3957](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3957)) + +## Version 1.38.0/0.59b0 (2025-10-16) + +### Fixed + +- `opentelemetry-instrumentation-flask`: Do not record `http.server.duration` metrics for excluded URLs. + ([#3794](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3794)) +- `opentelemetry-instrumentation-botocore`: migrate off the deprecated events API to use the logs API + ([#3624](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3624)) +- `opentelemetry-instrumentation-dbapi`: fix crash retrieving libpq version when enabling commenter with psycopg + ([#3796](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3796)) +- `opentelemetry-instrumentation-fastapi`: Fix handling of APIRoute subclasses + ([#3681](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3681)) +- `opentelemetry-instrumentation-flask`: Fix exemplars generation for `http.server.request.duration` and `http.server.duration` metrics + ([#3912](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3912)) + +### Added + +- `opentelemetry-instrumentation-botocore`: Add support for AWS Secrets Manager semantic convention attribute + ([#3765](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3765)) +- `opentelemetry-instrumentation-dbapi`: Add support for `commenter_options` in `trace_integration` function to control SQLCommenter behavior + ([#3743](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3743)) +- Add `rstcheck` to pre-commit to stop introducing invalid RST + ([#3777](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3777)) +- `opentelemetry-exporter-credential-provider-gcp`: create this package which provides support for supplying your machine's Application Default + Credentials (https://cloud.google.com/docs/authentication/application-default-credentials) to the OTLP Exporters created automatically by OpenTelemetry Python's auto instrumentation. These credentials authorize OTLP traces to be sent to `telemetry.googleapis.com`. [#3766](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3766). +- `opentelemetry-instrumentation-psycopg`: Add missing parameter `capture_parameters` to instrumentor. + ([#3676](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3676)) +- `opentelemetry-instrumentation-dbapi`: Adds sqlcommenter to documentation. + ([#3720](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3720)) + +## Version 1.37.0/0.58b0 (2025-09-11) + +### Fixed + +- `opentelemetry-instrumentation-fastapi`: Fix middleware ordering to cover all exception handling use cases. + ([#3664](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3664)) +- `opentelemetry-instrumentation-asgi`: Make all user hooks failsafe and record exceptions in hooks. + ([#3664](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3664)) +- `opentelemetry-instrumentation-fastapi`: Fix memory leak in `uninstrument_app()` by properly removing apps from the tracking set + ([#3688](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3688)) +- `opentelemetry-instrumentation-tornado` Fix server (request) duration metric calculation + ([#3679](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3679)) +- `opentelemetry-instrumentation-tornado`: Fix to properly skip all server telemetry when URL excluded. + ([#3680](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3680)) +- `opentelemetry-instrumentation`: Avoid calls to `context.detach` with `None` token. + ([#3673](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3673)) +- `opentelemetry-instrumentation-starlette`/`opentelemetry-instrumentation-fastapi`: Fixes a crash when host-based routing is used + ([#3507](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3507)) +- Fix documentation order of sections and headers for Django, Flask, MySQL, mysqlclient, psycopg, psycopg2, pymysql, sqlalchemy instrumentations. + ([#3719](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3719)) +- `opentelemetry-instrumentation-asgi` Fixed an issue where FastAPI reports IP instead of URL. + ([#3670](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3670)) +- `opentelemetry-instrumentation-httpx`: fix missing metric response attributes when tracing is disabled + ([#3615](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3615)) +- `opentelemetry-instrumentation-fastapi`: Don't pass bounded server_request_hook when using `FastAPIInstrumentor.instrument()` + ([#3701](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3701)) + +### Added + +- `opentelemetry-instrumentation-confluent-kafka` Add support for confluent-kafka <=2.11.0 + ([#3685](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3685)) +- `opentelemetry-instrumentation-system-metrics`: Add `cpython.gc.collected_objects` and `cpython.gc.uncollectable_objects` metrics + ([#3666](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3666)) +- `opentelemetry-sdk-extension-aws` Add AWS X-Ray Remote Sampler with initial Rules Poller implementation + ([#3366](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3366)) +- `opentelemetry-instrumentation`: add support for `OTEL_PYTHON_AUTO_INSTRUMENTATION_EXPERIMENTAL_GEVENT_PATCH` to inform opentelemetry-instrument about gevent monkeypatching + ([#3699](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3699)) +- `opentelemetry-instrumentation`: botocore: Add support for AWS Step Functions semantic convention attributes + ([#3737](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3737)) +- `opentelemetry-instrumentation-botocore`: Add support for SNS semantic convention attribute aws.sns.topic.arn + ([#3734](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3734)) +- `opentelemetry-instrumentation`: botocore: upgrade moto package from 5.0.9 to 5.1.11 + ([#3736](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3736)) + +## Version 1.36.0/0.57b0 (2025-07-29) + +### Fixed + +- `opentelemetry-instrumentation`: Fix dependency conflict detection when instrumented packages are not installed by moving check back to before instrumentors are loaded. Add "instruments-any" feature for instrumentations that target multiple packages. + ([#3610](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3610)) +- infra(ci): Fix git pull failures in core contrib test + ([#3357](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3357)) + +### Added + +- `opentelemetry-instrumentation-psycopg2` Utilize instruments-any functionality. + ([#3610](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3610)) +- `opentelemetry-instrumentation-kafka-python` Utilize instruments-any functionality. + ([#3610](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3610)) +- `opentelemetry-instrumentation-system-metrics`: Add `cpython.gc.collections` metrics with collection unit is specified in semconv ([3617](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3617)) + +## Version 1.35.0/0.56b0 (2025-07-11) + +### Added + +- `opentelemetry-instrumentation-pika` Added instrumentation for All `SelectConnection` adapters + ([#3584](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3584)) +- `opentelemetry-instrumentation-tornado` Add support for `WebSocketHandler` instrumentation + ([#3498](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3498)) +- `opentelemetry-util-http` Added support for redacting specific url query string values and url credentials in instrumentations + ([#3508](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3508)) +- `opentelemetry-instrumentation-pymongo` `aggregate` and `getMore` capture statements support + ([#3601](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3601)) + +### Fixed + +- `opentelemetry-instrumentation-asgi`: fix excluded_urls in instrumentation-asgi + ([#3567](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3567)) +- `opentelemetry-resource-detector-containerid`: make it more quiet on platforms without cgroups + ([#3579](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3579)) + +## Version 1.34.0/0.55b0 (2025-06-04) + +### Fixed + +- `opentelemetry-instrumentation-system-metrics`: fix loading on Google Cloud Run + ([#3533](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3533)) +- `opentelemetry-instrumentation-fastapi`: fix wrapping of middlewares + ([#3012](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3012)) +- `opentelemetry-instrumentation-starlette` Remove max version constraint on starlette + ([#3456](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3456)) +- `opentelemetry-instrumentation-starlette` Fix memory leak and double middleware + ([#3529](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3529)) +- `opentelemetry-instrumentation-urllib3`: proper bucket boundaries in stable semconv http duration metrics + ([#3518](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3518)) +- `opentelemetry-instrumentation-urllib`: proper bucket boundaries in stable semconv http duration metrics + ([#3519](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3519)) +- `opentelemetry-instrumentation-falcon`: proper bucket boundaries in stable semconv http duration + ([#3525](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3525)) +- `opentelemetry-instrumentation-wsgi`: add explicit http duration buckets for stable semconv + ([#3527](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3527)) +- `opentelemetry-instrumentation-asgi`: add explicit http duration buckets for stable semconv + ([#3526](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3526)) +- `opentelemetry-instrumentation-flask`: proper bucket boundaries in stable semconv http duration + ([#3523](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3523)) +- `opentelemetry-instrumentation-django`: proper bucket boundaries in stable semconv http duration + ([#3524](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3524)) +- `opentelemetry-instrumentation-grpc`: support non-list interceptors + ([#3520](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3520)) +- `opentelemetry-instrumentation-botocore` Ensure spans end on early stream closure for Bedrock Streaming APIs + ([#3481](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3481)) +- `opentelemetry-instrumentation-sqlalchemy` Respect suppress_instrumentation functionality + ([#3477](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3477)) +- `opentelemetry-instrumentation-botocore`: fix handling of tool input in Bedrock ConverseStream + ([#3544](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3544)) +- `opentelemetry-instrumentation-botocore` Add type check when extracting tool use from Bedrock request message content + ([#3548](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3548)) +- `opentelemetry-instrumentation-dbapi` Respect suppress_instrumentation functionality ([#3460](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3460)) +- `opentelemetry-resource-detector-container` Correctly parse container id when using systemd and cgroupsv1 + ([#3429](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3429)) + +### Breaking changes + +- `opentelemetry-instrumentation-botocore` Use `cloud.region` instead of `aws.region` span attribute as per semantic conventions. + ([#3474](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3474)) +- `opentelemetry-instrumentation-fastapi`: Drop support for FastAPI versions earlier than `0.92` + ([#3012](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3012)) +- `opentelemetry-resource-detector-container`: rename package name to `opentelemetry-resource-detector-containerid` + ([#3536](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3536)) + +### Added + +- `opentelemetry-instrumentation-aiohttp-client` Add support for HTTP metrics + ([#3517](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3517)) +- `opentelemetry-instrumentation-httpx` Add support for HTTP metrics + ([#3513](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3513)) +- `opentelemetry-instrumentation` Allow re-raising exception when instrumentation fails + ([#3545](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3545)) +- `opentelemetry-instrumentation-aiokafka` Add instrumentation of `consumer.getmany` (batch) + ([#3257](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3257)) + +### Deprecated + +- Drop support for Python 3.8, bump baseline to Python 3.9. + ([#3399](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3399)) + +## Version 1.33.0/0.54b0 (2025-05-09) + +### Added + +- `opentelemetry-instrumentation-requests` Support explicit_bucket_boundaries_advisory in duration metrics + ([#3464](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3464)) +- `opentelemetry-instrumentation-redis` Add support for redis client-specific instrumentation. + ([#3143](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3143)) + +### Fixed + +- `opentelemetry-instrumentation` Catch `ModuleNotFoundError` when the library is not installed + and log as debug instead of exception + ([#3423](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3423)) +- `opentelemetry-instrumentation-asyncio` Fix duplicate instrumentation + ([#3383](https://github.com/open-telemetry/opentelemetry-python-contrib/issues/3383)) +- `opentelemetry-instrumentation-botocore` Add GenAI instrumentation for additional Bedrock models for InvokeModel API + ([#3419](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3419)) +- `opentelemetry-instrumentation` don't print duplicated conflict log error message + ([#3432](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3432)) +- `opentelemetry-instrumentation-grpc` Check for None result in gRPC + ([#3380](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3381)) +- `opentelemetry-instrumentation-[asynclick/click]` Add missing opentelemetry-instrumentation dep + ([#3447](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3447)) +- `opentelemetry-instrumentation-botocore` Capture server attributes for botocore API calls + ([#3448](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3448)) + +## Version 1.32.0/0.53b0 (2025-04-10) + +### Added + +- `opentelemetry-instrumentation-asyncclick`: new instrumentation to trace asyncclick commands + ([#3319](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3319)) +- `opentelemetry-instrumentation-botocore` Add support for GenAI tool events using Amazon Nova models and `InvokeModel*` APIs + ([#3385](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3385)) +- `opentelemetry-instrumentation` Make auto instrumentation use the same dependency resolver as manual instrumentation does + ([#3202](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3202)) + +### Fixed + +- `opentelemetry-instrumentation` Fix client address is set to server address in new semconv + ([#3354](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3354)) +- `opentelemetry-instrumentation-dbapi`, `opentelemetry-instrumentation-django`, + `opentelemetry-instrumentation-sqlalchemy`: Fix sqlcomment for non string query and composable object. + ([#3113](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3113)) +- `opentelemetry-instrumentation-grpc` Fix error when using gprc versions <= 1.50.0 with unix sockets. + ([[#3393](https://github.com/open-telemetry/opentelemetry-python-contrib/issues/3393)]) +- `opentelemetry-instrumentation-asyncio` Fix duplicate instrumentation. + ([[#3383](https://github.com/open-telemetry/opentelemetry-python-contrib/issues/3383)]) +- `opentelemetry-instrumentation-aiokafka` Fix send_and_wait method no headers kwargs error. + ([[#3332](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3332)]) + +## Version 1.31.0/0.52b0 (2025-03-12) + +### Added + +- `opentelemetry-instrumentation-openai-v2` Update doc for OpenAI Instrumentation to support OpenAI Compatible Platforms + ([#3279](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3279)) +- `opentelemetry-instrumentation-system-metrics` Add `process` metrics and deprecated `process.runtime` prefixed ones + ([#3250](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3250)) +- `opentelemetry-instrumentation-botocore` Add support for GenAI user events and lazy initialize tracer + ([#3258](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3258)) +- `opentelemetry-instrumentation-botocore` Add support for GenAI system events + ([#3266](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3266)) +- `opentelemetry-instrumentation-botocore` Add support for GenAI choice events + ([#3275](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3275)) +- `opentelemetry-instrumentation-botocore` Add support for GenAI tool events + ([#3302](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3302)) +- `opentelemetry-instrumentation-botocore` Add support for GenAI metrics + ([#3326](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3326)) +- `opentelemetry-instrumentation` make it simpler to initialize auto-instrumentation programmatically + ([#3273](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3273)) +- Add `opentelemetry-instrumentation-vertexai>=2.0b0` to `opentelemetry-bootstrap` + ([#3307](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3307)) +- Loosen `opentelemetry-instrumentation-starlette[instruments]` specifier + ([#3304](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3304)) + +### Fixed + +- `opentelemetry-instrumentation-redis` Add missing entry in doc string for `def _instrument` + ([#3247](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3247)) +- `opentelemetry-instrumentation-botocore` sns-extension: Change destination name attribute + to match topic ARN and redact phone number from attributes + ([#3249](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3249)) +- `opentelemetry-instrumentation-asyncpg` Fix fallback for empty queries. + ([#3253](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3253)) +- `opentelemetry-instrumentation` Fix a traceback in sqlcommenter when psycopg connection pooling is enabled. + ([#3309](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3309)) +- `opentelemetry-instrumentation-threading` Fix broken context typehints + ([#3322](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3322)) +- `opentelemetry-instrumentation-requests` always record span status code in duration metric + ([#3323](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3323)) + +## Version 1.30.0/0.51b0 (2025-02-03) + +### Added + +- `opentelemetry-instrumentation-confluent-kafka` Add support for confluent-kafka <=2.7.0 + ([#3100](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3100)) +- Add support to database stability opt-in in `_semconv` utilities and add tests + ([#3111](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3111)) +- `opentelemetry-instrumentation-urllib` Add `py.typed` file to enable PEP 561 + ([#3131](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3131)) +- `opentelemetry-opentelemetry-pymongo` Add `py.typed` file to enable PEP 561 + ([#3136](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3136)) +- `opentelemetry-opentelemetry-requests` Add `py.typed` file to enable PEP 561 + ([#3135](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3135)) +- `opentelemetry-instrumentation-system-metrics` Add `py.typed` file to enable PEP 561 + ([#3132](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3132)) +- `opentelemetry-opentelemetry-sqlite3` Add `py.typed` file to enable PEP 561 + ([#3133](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3133)) +- `opentelemetry-instrumentation-falcon` add support version to v4 + ([#3086](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3086)) +- `opentelemetry-instrumentation-falcon` Implement new HTTP semantic convention opt-in for Falcon + ([#2790](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2790)) +- `opentelemetry-instrumentation-wsgi` always record span status code to have it available in metrics + ([#3148](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3148)) +- add support to Python 3.13 + ([#3134](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3134)) +- `opentelemetry-opentelemetry-wsgi` Add `py.typed` file to enable PEP 561 + ([#3129](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3129)) +- `opentelemetry-util-http` Add `py.typed` file to enable PEP 561 + ([#3127](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3127)) +- `opentelemetry-instrumentation-psycopg2` Add support for psycopg2-binary + ([#3186](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3186)) +- `opentelemetry-opentelemetry-botocore` Add basic support for GenAI attributes for AWS Bedrock Converse API + ([#3161](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3161)) +- `opentelemetry-opentelemetry-botocore` Add basic support for GenAI attributes for AWS Bedrock InvokeModel API + ([#3200](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3200)) +- `opentelemetry-opentelemetry-botocore` Add basic support for GenAI attributes for AWS Bedrock ConverseStream API + ([#3204](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3204)) +- `opentelemetry-opentelemetry-botocore` Add basic support for GenAI attributes for AWS Bedrock InvokeModelWithStreamResponse API + ([#3206](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3206)) +- `opentelemetry-instrumentation-pymssql` Add pymssql instrumentation + ([#394](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/394)) +- `opentelemetry-instrumentation-mysql` Add sqlcommenter support + ([#3163](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3163)) + +### Fixed + +- `opentelemetry-instrumentation-httpx` Fix `RequestInfo`/`ResponseInfo` type hints + ([#3105](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3105)) +- `opentelemetry-instrumentation-dbapi` Move `TracedCursorProxy` and `TracedConnectionProxy` to the module level + ([#3068](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3068)) +- `opentelemetry-instrumentation-click` Disable tracing of well-known server click commands + ([#3174](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3174)) +- `opentelemetry-instrumentation` Fix `get_dist_dependency_conflicts` if no distribution requires + ([#3168](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3168)) + +### Breaking changes + +- `opentelemetry-exporter-prometheus-remote-write` updated protobuf required version from 4.21 to 5.26 and regenerated protobufs + ([#3219](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3219)) +- `opentelemetry-instrumentation-sqlalchemy` including sqlcomment in `db.statement` span attribute value is now opt-in + ([#3112](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3112)) +- `opentelemetry-instrumentation-dbapi` including sqlcomment in `db.statement` span attribute value is now opt-in + ([#3115](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3115)) +- `opentelemetry-instrumentation-psycopg2`, `opentelemetry-instrumentation-psycopg`, `opentelemetry-instrumentation-mysqlclient`, `opentelemetry-instrumentation-pymysql`: including sqlcomment in `db.statement` span attribute value is now opt-in + ([#3121](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3121)) + +## Version 1.29.0/0.50b0 (2024-12-11) + +### Added + +- `opentelemetry-instrumentation-starlette` Add type hints to the instrumentation + ([#3045](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3045)) +- `opentelemetry-distro` default to OTLP log exporter. + ([#3042](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3042)) +- `opentelemetry-instrumentation-sqlalchemy` Update unit tests to run with SQLALchemy 2 + ([#2976](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2976)) +- Add `opentelemetry-instrumentation-openai-v2` to `opentelemetry-bootstrap` + ([#2996](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2996)) +- `opentelemetry-instrumentation-sqlalchemy` Add sqlcomment to `db.statement` attribute + ([#2937](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2937)) +- `opentelemetry-instrumentation-dbapi` Add sqlcomment to `db.statement` attribute + ([#2935](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2935)) +- `opentelemetry-instrumentation-dbapi` instrument_connection accepts optional connect_module + ([#3027](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3027)) +- `opentelemetry-instrumentation-mysqlclient` Add sqlcommenter support + ([#2941](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2941)) +- `opentelemetry-instrumentation-pymysql` Add sqlcommenter support + ([#2942](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2942)) +- `opentelemetry-instrumentation-click`: new instrumentation to trace click commands + ([#2994](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2994)) + +### Fixed + +- `opentelemetry-instrumentation-starlette`: Retrieve `meter_provider` key instead of `_meter_provider` on `_instrument` + ([#3048](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3048)) +- `opentelemetry-instrumentation-httpx`: instrument_client is a static method again + ([#3003](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3003)) +- `opentelemetry-instrumentation-system_metrics`: fix callbacks reading wrong config + ([#3025](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3025)) +- `opentelemetry-instrumentation-httpx`: Check if mount transport is none before wrap it + ([#3022](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3022)) +- Replace all instrumentor unit test `assertEqualSpanInstrumentationInfo` calls with `assertEqualSpanInstrumentationScope` calls + ([#3037](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3037)) +- `opentelemetry-instrumentation-sqlalchemy` Fixes engines from `sqlalchemy.engine_from_config` not being fully instrumented + ([#2816](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2816)) +- `opentelemetry-instrumentation-sqlalchemy`: Fix a remaining memory leak in EngineTracer + ([#3053](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3053)) +- `opentelemetry-instrumentation-sqlite3`: Update documentation on explicit cursor support of tracing + ([#3088](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3088)) + +### Breaking changes + +- `opentelemetry-instrumentation-sqlalchemy` teach instruments version + ([#2971](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2971)) +- Drop `opentelemetry-instrumentation-test` package from default instrumentation list + ([#2969](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2969)) +- `opentelemetry-instrumentation-httpx`: remove private unused `_InstrumentedClient` and `_InstrumentedAsyncClient` classes + ([#3036](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3036)) + +## Version 1.28.0/0.49b0 (2024-11-05) + +### Added + +- `opentelemetry-instrumentation-openai-v2` Instrumentation for OpenAI >= 0.27.0 + ([#2759](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2759)) +- `opentelemetry-instrumentation-fastapi` Add autoinstrumentation mechanism tests. + ([#2860](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2860)) +- `opentelemetry-instrumentation-aiokafka` Add instrumentor and auto instrumentation support for aiokafka + ([#2082](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2082)) +- `opentelemetry-instrumentation-redis` Add additional attributes for methods create_index and search, rename those spans + ([#2635](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2635)) +- `opentelemetry-instrumentation` Add support for string based dotted module paths in unwrap + ([#2919](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2919)) + +### Fixed + +- `opentelemetry-instrumentation-aiokafka` Wrap `AIOKafkaConsumer.getone()` instead of `AIOKafkaConsumer.__anext__` + ([#2874](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2874)) +- `opentelemetry-instrumentation-confluent-kafka` Fix to allow `topic` to be extracted from `kwargs` in `produce()` + ([#2901])(https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2901) +- `opentelemetry-instrumentation-system-metrics` Update metric units to conform to UCUM conventions. + ([#2922](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2922)) +- `opentelemetry-instrumentation-celery` Don't detach context without a None token + ([#2927](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2927)) +- `opentelemetry-exporter-prometheus-remote-write`: sort labels before exporting + ([#2940](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2940)) +- `opentelemetry-instrumentation-dbapi` sqlcommenter key values created from PostgreSQL, MySQL systems + ([#2897](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2897)) +- `opentelemetry-instrumentation-system-metrics`: don't report open file descriptors on Windows + ([#2946](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2946)) + +### Breaking changes + +- Deprecation of pkg_resource in favor of importlib.metadata + ([#2871](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2871)) +- `opentelemetry-instrumentation` Don't fail distro loading if instrumentor raises ImportError, instead skip them + ([#2923](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2923)) +- `opentelemetry-instrumentation-httpx` Rewrote instrumentation to use wrapt instead of subclassing + ([#2909](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2909)) + +## Version 1.27.0/0.48b0 (2024-08-28) + +### Added + +- `opentelemetry-instrumentation-kafka-python` Instrument temporary fork, kafka-python-ng inside kafka-python's instrumentation + ([#2537](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2537)) +- `opentelemetry-instrumentation-asgi`, `opentelemetry-instrumentation-fastapi` Add ability to disable internal HTTP send and receive spans + ([#2802](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2802)) +- `opentelemetry-instrumentation-asgi` Add fallback decoding for ASGI headers + ([#2837](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2837)) + +### Breaking changes + +- `opentelemetry-bootstrap` Remove `opentelemetry-instrumentation-aws-lambda` from the defaults instrumentations + ([#2786](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2786)) + +### Fixed + +- `opentelemetry-instrumentation-httpx` fix handling of async hooks + ([#2823](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2823)) +- `opentelemetry-instrumentation-system-metrics` fix `process.runtime.cpu.utilization` values to be shown in range of 0 to 1 + ([#2812](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2812)) +- `opentelemetry-instrumentation-fastapi` fix `fastapi` auto-instrumentation by removing `fastapi-slim` support, `fastapi-slim` itself is discontinued from maintainers + ([#2783](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2783)) +- `opentelemetry-instrumentation-aws-lambda` Avoid exception when a handler is not present. + ([#2750](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2750)) +- `opentelemetry-instrumentation-django` Fix regression - `http.target` re-added back to old semconv duration metrics + ([#2746](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2746)) +- `opentelemetry-instrumentation-asgi` do not set `url.full` attribute for server spans + ([#2735](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2735)) +- `opentelemetry-instrumentation-grpc` Fixes the issue with the gRPC instrumentation not working with the 1.63.0 and higher version of gRPC + ([#2483](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2484)) +- `opentelemetry-instrumentation-aws-lambda` Fixing w3c baggage support + ([#2589](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2589)) +- `opentelemetry-instrumentation-celery` propagates baggage + ([#2385](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2385)) +- `opentelemetry-instrumentation-asyncio` Fixes async generator coroutines not being awaited + ([#2792](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2792)) +- `opentelemetry-instrumentation-tornado` Handle http client exception and record exception info into span + ([#2563](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2563)) +- `opentelemetry-instrumentation` fix `http.host` new http semantic convention mapping to depend on `kind` of span + ([#2814](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2814)) +- `opentelemetry-instrumentation` Fix the description of `http.server.duration` and `http.server.request.duration` + ([#2753](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2753)) +- `opentelemetry-instrumentation-grpc` Fix grpc supported version + ([#2845](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2845)) +- `opentelemetry-instrumentation-asyncio` fix `AttributeError` in + `AsyncioInstrumentor.trace_to_thread` when `func` is a `functools.partial` instance + ([#2911](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2911)) + +## Version 1.26.0/0.47b0 (2024-07-23) + +### Added + +- `opentelemetry-instrumentation-flask` Add `http.route` and `http.target` to metric attributes + ([#2621](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2621)) +- `opentelemetry-instrumentation-aws-lambda` Enable global propagator for AWS instrumentation + ([#2708](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2708)) +- `opentelemetry-instrumentation-sklearn` Deprecated the sklearn instrumentation + ([#2708](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2708)) +- `opentelemetry-instrumentation-pyramid` Record exceptions raised when serving a request + ([#2622](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2622)) +- `opentelemetry-sdk-extension-aws` Add AwsXrayLambdaPropagator + ([#2573](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2573)) +- `opentelemetry-instrumentation-confluent-kafka` Add support for version 2.4.0 of confluent_kafka + ([#2616](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2616)) +- `opentelemetry-instrumentation-asyncpg` Add instrumentation to cursor based queries + ([#2501](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2501)) +- `opentelemetry-instrumentation-confluent-kafka` Add support for produce purge + ([#2638](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2638)) +- `opentelemetry-instrumentation-asgi` Implement new semantic convention opt-in with stable http semantic conventions + ([#2610](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2610)) +- `opentelemetry-instrumentation-fastapi` Implement new semantic convention opt-in with stable http semantic conventions + ([#2682](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2682)) +- `opentelemetry-instrumentation-httpx` Implement new semantic convention opt-in migration with stable http semantic conventions + ([#2631](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2631)) +- `opentelemetry-instrumentation-system-metrics` Permit to use psutil 6.0+. + ([#2630](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2630)) +- `opentelemetry-instrumentation-system-metrics` Add support for capture open file descriptors + ([#2652](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2652)) +- `opentelemetry-instrumentation-httpx` Add support for instrument client with proxy + ([#2664](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2664)) +- `opentelemetry-instrumentation-aiohttp-client` Implement new semantic convention opt-in migration + ([#2673](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2673)) +- `opentelemetry-instrumentation-django` Add `http.target` to Django duration metric attributes + ([#2624](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2624)) +- `opentelemetry-instrumentation-urllib3` Implement new semantic convention opt-in migration + ([#2715](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2715)) +- `opentelemetry-instrumentation-django` Implement new semantic convention opt-in with stable http semantic conventions + ([#2714](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2714)) +- `opentelemetry-instrumentation-urllib` Implement new semantic convention opt-in migration + ([#2736](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2736)) + +### Breaking changes + +- `opentelemetry-instrumentation-asgi`, `opentelemetry-instrumentation-fastapi`, `opentelemetry-instrumentation-starlette` Use `tracer` and `meter` of originating components instead of one from `asgi` middleware + ([#2580](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2580)) +- Populate `{method}` as `HTTP` on `_OTHER` methods from scope for `asgi` middleware + ([#2610](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2610)) +- Populate `{method}` as `HTTP` on `_OTHER` methods from scope for `fastapi` middleware + ([#2682](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2682)) +- `opentelemetry-instrumentation-urllib3` Populate `{method}` as `HTTP` on `_OTHER` methods for span name + ([#2715](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2715)) +- Populate `{method}` as `HTTP` on `_OTHER` methods from scope for `fastapi` instrumentation + ([#2682](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2682)) +- Populate `{method}` as `HTTP` on `_OTHER` methods from scope for `django` middleware + ([#2714](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2714)) +- Populate `{method}` as `HTTP` on `_OTHER` methods from scope for `urllib` instrumentation + ([#2736](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2736)) +- `opentelemetry-instrumentation-httpx`, `opentelemetry-instrumentation-aiohttp-client`, + `opentelemetry-instrumentation-requests` Populate `{method}` as `HTTP` on `_OTHER` methods + ([#2726](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2726)) +- `opentelemetry-instrumentation-fastapi` Add dependency support for fastapi-slim + ([#2702](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2702)) +- `opentelemetry-instrumentation-urllib3` improve request_hook, replacing `headers` and `body` parameters with a single `request_info: RequestInfo` parameter that now contains the `method` and `url` ([#2711](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2711)) + +### Fixed + +- Handle `redis.exceptions.WatchError` as a non-error event in redis instrumentation + ([#2668](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2668)) +- `opentelemetry-instrumentation-httpx` Ensure httpx.get or httpx.request like methods are instrumented + ([#2538](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2538)) +- Add Python 3.12 support + ([#2572](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2572)) +- `opentelemetry-instrumentation-aiohttp-server`, `opentelemetry-instrumentation-httpx` Ensure consistently use of suppress_instrumentation utils + ([#2590](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2590)) +- Reference symbols from generated semantic conventions + ([#2611](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2611)) +- `opentelemetry-instrumentation-psycopg` Bugfix: Handle empty statement. + ([#2644](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2644)) +- `opentelemetry-instrumentation-confluent-kafka` Confluent Kafka: Ensure consume span is ended when consumer is closed + ([#2640](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2640)) +- `opentelemetry-instrumentation-asgi` Fix generation of `http.target` and `http.url` attributes for ASGI apps + using sub apps + ([#2477](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2477)) +- `opentelemetry-instrumentation-aws-lambda` Bugfix: AWS Lambda event source key incorrect for SNS in instrumentation library. + ([#2612](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2612)) +- `opentelemetry-instrumentation-asyncio` instrumented `asyncio.wait_for` properly raises `asyncio.TimeoutError` as expected + ([#2637](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2637)) +- `opentelemetry-instrumentation-django` Handle exceptions from request/response hooks + ([#2153](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2153)) +- `opentelemetry-instrumentation-asgi` Removed `NET_HOST_NAME` AND `NET_HOST_PORT` from active requests count attribute + ([#2610](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2610)) +- `opentelemetry-instrumentation-asgi` Bugfix: Middleware did not set status code attribute on duration metrics for non-recording spans. + ([#2627](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2627)) +- `opentelemetry-instrumentation-mysql` Add support for `mysql-connector-python` v9 + ([#2751](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2751)) + +## Version 1.25.0/0.46b0 (2024-05-31) + +### Breaking changes + +- Add return statement to Confluent kafka Producer poll() and flush() calls when instrumented by ConfluentKafkaInstrumentor().instrument_producer() ([#2527](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2527)) +- Rename `type` attribute to `asgi.event.type` in `opentelemetry-instrumentation-asgi` + ([#2300](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2300)) +- Rename AwsLambdaInstrumentor span attributes `faas.id` to `cloud.resource_id`, `faas.execution` to `faas.invocation_id` + ([#2372](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2372)) +- Drop support for instrumenting elasticsearch client < 6 + ([#2422](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2422)) +- `opentelemetry-instrumentation-wsgi` Add `http.method` to `span.name` + ([#2425](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2425)) +- `opentelemetry-instrumentation-flask` Add `http.method` to `span.name` + ([#2454](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2454)) +- Record repeated HTTP headers in lists, rather than a comma separate strings for ASGI based web frameworks + ([#2361](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2361)) +- ASGI, FastAPI, Starlette: provide both send and receive hooks with `scope` and `message` for internal spans +- ([#2546](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2546)) + +### Added + +- `opentelemetry-sdk-extension-aws` Register AWS resource detectors under the + `opentelemetry_resource_detector` entry point + ([#2382](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2382)) +- `opentelemetry-instrumentation-wsgi` Implement new semantic convention opt-in with stable http semantic conventions + ([#2425](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2425)) +- `opentelemetry-instrumentation-flask` Implement new semantic convention opt-in with stable http semantic conventions + ([#2454](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2454)) +- `opentelemetry-instrumentation-threading` Initial release for threading + ([#2253](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2253)) +- `opentelemetry-instrumentation-pika` Instrumentation for `channel.consume()` (supported + only for global, non channel specific instrumentation) + ([#2397](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2397)) +- `opentelemetry-processor-baggage` Initial release + ([#2436](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2436)) +- `opentelemetry-processor-baggage` Add baggage key predicate + ([#2535](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2535)) + +### Fixed + +- `opentelemetry-instrumentation-dbapi` Fix compatibility with Psycopg3 to extract libpq build version + ([#2500](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2500)) +- `opentelemetry-instrumentation-grpc` AioClientInterceptor should propagate with a Metadata object + ([#2363](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2363)) +- `opentelemetry-instrumentation-boto3sqs` Instrument Session and resource + ([#2161](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2161)) +- `opentelemetry-instrumentation-aws-lambda` Fix exception handling for events with requestContext + ([#2418](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2418)) +- Use sqlalchemy version in sqlalchemy commenter instead of opentelemetry library version + ([#2404](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2404)) +- `opentelemetry-instrumentation-asyncio` Check for cancelledException in the future + ([#2461](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2461)) +- Remove SDK dependency from opentelemetry-instrumentation-grpc + ([#2474](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2474)) +- `opentelemetry-instrumentation-elasticsearch` Improved support for version 8 + ([#2420](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2420)) +- `opentelemetry-instrumentation-elasticsearch` Disabling instrumentation with native OTel support enabled + ([#2524](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2524)) +- `opentelemetry-instrumentation-asyncio` Check for **name** attribute in the coroutine + ([#2521](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2521)) +- `opentelemetry-instrumentation-requests` Fix wrong time unit for duration histogram + ([#2553](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2553)) +- `opentelemetry-util-http` Preserve brackets around literal IPv6 hosts ([#2552](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2552)) +- `opentelemetry-util-redis` Fix net peer attribute for unix socket connection ([#2493](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2493)) + +## Version 1.24.0/0.45b0 (2024-03-28) + +### Added + +- `opentelemetry-instrumentation-psycopg` Async Instrumentation for psycopg 3.x + ([#2146](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2146)) + +### Fixed + +- `opentelemetry-instrumentation-celery` Allow Celery instrumentation to be installed multiple times + ([#2342](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2342)) +- Align gRPC span status codes to OTEL specification + ([#1756](https://github.com/open-telemetry/opentelemetry-python-contrib/issues/1756)) +- `opentelemetry-instrumentation-flask` Add importlib metadata default for deprecation warning flask version + ([#2297](https://github.com/open-telemetry/opentelemetry-python-contrib/issues/2297)) +- Ensure all http.server.duration metrics have the same description + ([#2151](https://github.com/open-telemetry/opentelemetry-python-contrib/issues/2298)) +- Fix regression in httpx `request.url` not being of type `httpx.URL` after `0.44b0` + ([#2359](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2359)) +- Avoid losing repeated HTTP headers + ([#2266](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2266)) +- `opentelemetry-instrumentation-elasticsearch` Don't send bulk request body as db statement + ([#2355](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2355)) +- AwsLambdaInstrumentor sets `cloud.account.id` span attribute + ([#2367](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2367)) + +### Added + +- `opentelemetry-instrumentation-fastapi` Add support for configuring header extraction via runtime constructor parameters + ([#2241](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2241)) + +## Version 1.23.0/0.44b0 (2024-02-23) + +- Drop support for 3.7 + ([#2151](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2151)) +- `opentelemetry-resource-detector-azure` Added 10s timeout to VM Resource Detector + ([#2119](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2119)) +- `opentelemetry-instrumentation-asyncpg` Allow AsyncPGInstrumentor to be instantiated multiple times + ([#1791](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1791)) +- `opentelemetry-instrumentation-confluent-kafka` Add support for higher versions until 2.3.0 of confluent_kafka + ([#2132](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2132)) +- `opentelemetry-resource-detector-azure` Changed timeout to 4 seconds due to [timeout bug](https://github.com/open-telemetry/opentelemetry-python/issues/3644) + ([#2136](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2136)) +- `opentelemetry-resource-detector-azure` Suppress instrumentation for `urllib` call + ([#2178](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2178)) +- AwsLambdaInstrumentor handles and re-raises function exception + ([#2245](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2245)) + +### Added + +- `opentelemetry-instrumentation-psycopg` Initial release for psycopg 3.x +- `opentelemetry-instrumentation-asgi` Add support for configuring ASGI middleware header extraction via runtime constructor parameters + ([#2026](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2026)) + +## Version 1.22.0/0.43b0 (2023-12-14) + +### Added + +- `opentelemetry-instrumentation-asyncio` Add support for asyncio + ([#1919](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1943)) +- `opentelemetry-instrumentation` Added Otel semantic convention opt-in mechanism + ([#1987](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1987)) +- `opentelemetry-instrumentation-httpx` Fix mixing async and non async hooks + ([#1920](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1920)) +- `opentelemetry-instrumentation-requests` Implement new semantic convention opt-in with stable http semantic conventions + ([#2002](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2002)) +- `opentelemetry-instrument-grpc` Fix arity of context.abort for AIO RPCs + ([#2066](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2066)) +- Consolidate instrumentation suppression mechanisms and fix bug in httpx instrumentation + ([#2061](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2061)) + +### Fixed + +- `opentelemetry-instrumentation-httpx` Remove URL credentials + ([#2020](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2020)) +- `opentelemetry-instrumentation-urllib`/`opentelemetry-instrumentation-urllib3` Fix metric descriptions to match semantic conventions + ([#1959](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1959)) +- `opentelemetry-resource-detector-azure` Added dependency for Cloud Resource ID attribute + ([#2072](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2072)) + +## Version 1.21.0/0.42b0 (2023-11-01) + +### Added + +- `opentelemetry-instrumentation-aiohttp-server` Add instrumentor and auto instrumentation support for aiohttp-server + ([#1800](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1800)) +- `opentelemetry-instrumentation-botocore` Include SNS topic ARN as a span attribute with name `messaging.destination.name` to uniquely identify the SNS topic + ([#1995](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1995)) +- `opentelemetry-instrumentation-system-metrics` Add support for collecting process metrics + ([#1948](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1948)) +- Added schema_url (`"https://opentelemetry.io/schemas/1.11.0"`) to all metrics and traces + ([#1977](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1977)) + +### Fixed + +- `opentelemetry-instrumentation-aio-pika` and `opentelemetry-instrumentation-pika` Fix missing trace context propagation when trace not recording. + ([#1969](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1969)) +- Fix version of Flask dependency `werkzeug` + ([#1980](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1980)) +- `opentelemetry-resource-detector-azure` Using new Cloud Resource ID attribute. + ([#1976](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1976)) +- Do not collect `system.network.connections` by default on macOS which was causing exceptions in metrics collection. + ([#2008](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2008)) + +## Version 1.20.0/0.41b0 (2023-09-01) + +### Fixed + +- `opentelemetry-instrumentation-asgi` Fix UnboundLocalError local variable 'start' referenced before assignment + ([#1889](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1889)) +- Fixed union typing error not compatible with Python 3.7 introduced in `opentelemetry-util-http`, fix tests introduced by patch related to sanitize method for wsgi + ([#1913](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1913)) +- `opentelemetry-instrumentation-celery` Unwrap Celery's `ExceptionInfo` errors and report the actual exception that was raised. ([#1863](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1863)) + +### Added + +- `opentelemetry-resource-detector-azure` Add resource detectors for Azure App Service and VM + ([#1901](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1901)) +- `opentelemetry-instrumentation-flask` Add support for Flask 3.0.0 + ([#152](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2013)) + +## Version 1.19.0/0.40b0 (2023-07-13) + +- `opentelemetry-instrumentation-asgi` Add `http.server.request.size` metric + ([#1867](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1867)) + +### Fixed + +- `opentelemetry-instrumentation-django` Fix empty span name when using + `path("", ...)` ([#1788](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1788) +- Fix elastic-search instrumentation sanitization to support bulk queries + ([#1870](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1870)) +- Update falcon instrumentation to follow semantic conventions + ([#1824](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1824)) +- Fix sqlalchemy instrumentation wrap methods to accept sqlcommenter options + ([#1873](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1873)) +- Exclude background task execution from root server span in ASGI middleware + ([#1952](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1952)) + +### Added + +- Add instrumentor support for cassandra and scylla + ([#1902](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1902)) +- Add instrumentor support for mysqlclient + ([#1744](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1744)) +- Fix async redis clients not being traced correctly + ([#1830](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1830)) +- Make Flask request span attributes available for `start_span`. + ([#1784](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1784)) +- Fix falcon instrumentation's usage of Span Status to only set the description if the status code is ERROR. + ([#1840](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1840)) +- Instrument all httpx versions >= 0.18. + ([#1748](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1748)) +- Fix `Invalid type NoneType for attribute X (opentelemetry-instrumentation-aws-lambda)` error when some attributes do not exist + ([#1780](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1780)) +- Add metric instrumentation for celery + ([#1679](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1679)) +- `opentelemetry-instrumentation-asgi` Add `http.server.response.size` metric + ([#1789](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1789)) +- `opentelemetry-instrumentation-grpc` Allow gRPC connections via Unix socket + ([#1833](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1833)) +- Fix elasticsearch `Transport.perform_request` instrument wrap for elasticsearch >= 8 + ([#1810](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1810)) +- `opentelemetry-instrumentation-urllib3` Add support for urllib3 version 2 + ([#1879](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1879)) +- Add optional distro and configurator selection for auto-instrumentation + ([#1823](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1823)) +- `opentelemetry-instrumentation-django` - Add option to add Opentelemetry middleware at specific position in middleware chain + ([#2912]https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2912) + +### Added + +- `opentelemetry-instrumentation-kafka-python` Add instrumentation to `consume` method + ([#1786](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1786)) + +## Version 1.18.0/0.39b0 (2023-05-10) + +- Update runtime metrics to follow semantic conventions + ([#1735](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1735)) +- Add request and response hooks for GRPC instrumentation (client only) + ([#1706](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1706)) +- Fix memory leak in SQLAlchemy instrumentation where disposed `Engine` does not get garbage collected + ([#1771](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1771)) +- `opentelemetry-instrumentation-pymemcache` Update instrumentation to support pymemcache >4 + ([#1764](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1764)) +- `opentelemetry-instrumentation-confluent-kafka` Add support for higher versions of confluent_kafka + ([#1815](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1815)) + +### Added + +- Expand sqlalchemy pool.name to follow the semantic conventions + ([#1778](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1778)) +- Add `excluded_urls` functionality to `urllib` and `urllib3` instrumentations + ([#1733](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1733)) +- Make Django request span attributes available for `start_span`. + ([#1730](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1730)) +- Make ASGI request span attributes available for `start_span`. + ([#1762](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1762)) +- `opentelemetry-instrumentation-celery` Add support for anonymous tasks. + ([#1407](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1407)) +- `opentelemetry-instrumentation-logging` Add `otelTraceSampled` to instrumetation-logging + ([#1773](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1773)) + +### Changed + +- `opentelemetry-instrumentation-botocore` now uses the AWS X-Ray propagator by default + ([#1741](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1741)) + +### Fixed + +- Fix redis db.statements to be sanitized by default + ([#1778](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1778)) +- Fix elasticsearch db.statement attribute to be sanitized by default + ([#1758](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1758)) +- Fix `AttributeError` when AWS Lambda handler receives a list event + ([#1738](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1738)) +- Fix `None does not implement middleware` error when there are no middlewares registered + ([#1766](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1766)) +- Fix Flask instrumentation to only close the span if it was created by the same request context. + ([#1692](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1692)) + +### Changed + +- Update HTTP server/client instrumentation span names to comply with spec + ([#1759](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1759)) + +## Version 1.17.0/0.38b0 (2023-03-22) + +### Added + +- Add connection attributes to sqlalchemy connect span + ([#1608](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1608)) +- Add support for enabling Redis sanitization from environment variable + ([#1690](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1690)) +- Add metrics instrumentation for sqlalchemy + ([#1645](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1645)) + +### Fixed + +- Fix Flask instrumentation to only close the span if it was created by the same thread. + ([#1654](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1654)) +- Fix confluent-kafka instrumentation by allowing Producer headers to be dict or list + ([#1655](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1655)) +- `opentelemetry-instrumentation-system-metrics` Fix initialization of the instrumentation class when configuration is provided + ([#1438](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1439)) +- Fix exception in Urllib3 when dealing with filelike body. + ([#1399](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1399)) +- Fix httpx resource warnings + ([#1695](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1695)) + +### Changed + +- `opentelemetry-instrumentation-requests` Replace `name_callback` and `span_callback` with standard `response_hook` and `request_hook` callbacks + ([#670](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/670)) + +## Version 1.16.0/0.37b0 (2023-02-17) + +### Added + +- Support `aio_pika` 9.x (([#1670](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1670]) +- `opentelemetry-instrumentation-redis` Add `sanitize_query` config option to allow query sanitization. ([#1572](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1572)) +- `opentelemetry-instrumentation-elasticsearch` Add optional db.statement query sanitization. + ([#1598](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1598)) +- `opentelemetry-instrumentation-celery` Record exceptions as events on the span. + ([#1573](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1573)) +- Add metric instrumentation for urllib + ([#1553](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1553)) +- `opentelemetry/sdk/extension/aws` Implement [`aws.ecs.*`](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/resource/semantic_conventions/cloud_provider/aws/ecs.md) and [`aws.logs.*`](https://opentelemetry.io/docs/reference/specification/resource/semantic_conventions/cloud_provider/aws/logs/) resource attributes in the `AwsEcsResourceDetector` detector when the ECS Metadata v4 is available + ([#1212](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1212)) +- `opentelemetry-instrumentation-aio-pika` Support `aio_pika` 8.x + ([#1481](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1481)) +- `opentelemetry-instrumentation-aws-lambda` Flush `MeterProvider` at end of function invocation. + ([#1613](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1613)) +- Fix aiohttp bug with unset `trace_configs` + ([#1592](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1592)) +- `opentelemetry-instrumentation-django` Allow explicit `excluded_urls` configuration through `instrument()` + ([#1618](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1618)) + +### Fixed + +- Fix TortoiseORM instrumentation `AttributeError: type object 'Config' has no attribute 'title'` + ([#1575](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1575)) +- Fix SQLAlchemy uninstrumentation + ([#1581](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1581)) +- `opentelemetry-instrumentation-grpc` Fix code()/details() of \_OpentelemetryServicerContext. + ([#1578](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1578)) +- Fix aiopg instrumentation to work with aiopg < 2.0.0 + ([#1473](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1473)) +- `opentelemetry-instrumentation-aws-lambda` Adds an option to configure `disable_aws_context_propagation` by + environment variable: `OTEL_LAMBDA_DISABLE_AWS_CONTEXT_PROPAGATION` + ([#1507](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1507)) +- Fix pymongo to collect the property DB_MONGODB_COLLECTION + ([#1555](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1555)) +- `opentelemetry-instrumentation-asgi` Fix keys() in class ASGIGetter to correctly fetch values from carrier headers. + ([#1435](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1435)) +- mongo db - fix db statement capturing + ([#1512](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1512)) +- Add commit method for ConfluentKafkaInstrumentor's ProxiedConsumer + ([#1656](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1656)) + +## Version 1.15.0/0.36b0 (2022-12-10) + +- Add uninstrument test for sqlalchemy + ([#1471](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1471)) +- `opentelemetry-instrumentation-tortoiseorm` Initial release + ([#685](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/685)) +- Add metric instrumentation for tornado + ([#1252](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1252)) +- `opentelemetry-instrumentation-aws-lambda` Add option to disable aws context propagation + ([#1466](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1466)) + +### Added + +- `opentelemetry-resource-detector-container` Add support resource detection of container properties. + ([#1584](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1584)) +- `opentelemetry-instrumentation-pymysql` Add tests for commit() and rollback(). + ([#1424](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1424)) +- `opentelemetry-instrumentation-fastapi` Add support for regular expression matching and sanitization of HTTP headers. + ([#1403](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1403)) +- `opentelemetry-instrumentation-botocore` add support for `messaging.*` in the sqs extension. + ([#1350](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1350)) +- `opentelemetry-instrumentation-starlette` Add support for regular expression matching and sanitization of HTTP headers. + ([#1404](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1404)) +- `opentelemetry-instrumentation-botocore` Add support for SNS `publish` and `publish_batch`. + ([#1409](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1409)) +- Strip leading comments from SQL queries when generating the span name. + ([#1434](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1434)) +- `opentelemetry-instrumentation-confluent-kafka` Add support for the latest versions of the library. + ([#1468](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1468)) + +### Fixed + +- Fix bug in Urllib instrumentation - add status code to span attributes only if the status code is not None. + ([#1430](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1430)) +- `opentelemetry-instrumentation-aiohttp-client` Allow overriding of status in response hook. + ([#1394](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1394)) +- `opentelemetry-instrumentation-pymysql` Fix dbapi connection instrument wrapper has no \_sock member. + ([#1424](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1424)) +- `opentelemetry-instrumentation-dbapi` Fix the check for the connection already being instrumented in instrument_connection(). + ([#1424](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1424)) +- Remove db.name attribute from Redis instrumentation + ([#1427](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1427)) +- `opentelemetry-instrumentation-asgi` Fix target extraction for duration metric + ([#1461](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1461)) +- Add grpc.aio instrumentation to package entry points + ([#1442](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1442)) +- Fix a bug in SQLAlchemy instrumentation - support disabling enable_commenter variable + ([#1440](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1440)) + +## Version 1.14.0/0.35b0 (2022-11-03) + +### Deprecated + +- `opentelemetry-distro` Deprecate `otlp_proto_grpc` and `otlp_proto_http` in favor of using + `OTEL_EXPORTER_OTLP_TRACES_PROTOCOL` as according to specifications + ([#1250](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1250)) + +### Added + +- Capture common HTTP attributes from API Gateway proxy events in `opentelemetry-instrumentation-aws-lambda` + ([#1233](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1233)) +- Add metric instrumentation for tornado + ([#1252](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1252)) +- `opentelemetry-instrumentation-django` Fixed bug where auto-instrumentation fails when django is installed and settings are not configured. + ([#1369](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1369)) +- `opentelemetry-instrumentation-system-metrics` add supports to collect system thread count. ([#1339](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1339)) +- `opentelemetry-exporter-richconsole` Fixing RichConsoleExpoter to allow multiple traces, fixing duplicate spans and include resources ([#1336](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1336)) +- `opentelemetry-instrumentation-asgi` Add support for regular expression matching and sanitization of HTTP headers. + ([#1333](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1333)) +- `opentelemetry-instrumentation-asgi` metrics record target attribute (FastAPI only) + ([#1323](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1323)) +- `opentelemetry-instrumentation-wsgi` Add support for regular expression matching and sanitization of HTTP headers. + ([#1402](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1402)) +- Add support for py3.11 + ([#1415](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1415)) +- `opentelemetry-instrumentation-django` Add support for regular expression matching and sanitization of HTTP headers. + ([#1411](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1411)) +- `opentelemetry-instrumentation-falcon` Add support for regular expression matching and sanitization of HTTP headers. + ([#1412](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1412)) +- `opentelemetry-instrumentation-flask` Add support for regular expression matching and sanitization of HTTP headers. + ([#1413](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1413)) +- `opentelemetry-instrumentation-pyramid` Add support for regular expression matching and sanitization of HTTP headers. + ([#1414](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1414)) +- `opentelemetry-instrumentation-grpc` Add support for grpc.aio Clients and Servers + ([#1245](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1245)) +- Add metric exporter for Prometheus Remote Write + ([#1359](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1359)) + +### Fixed + +- Fix bug in Falcon instrumentation + ([#1377](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1377)) +- `opentelemetry-instrumentation-asgi` Fix keys() in class ASGIGetter so it decodes the keys before returning them. + ([#1333](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1333)) +- `opentelemetry-instrumentation-asgi` Make ASGIGetter.get() compare all keys in a case insensitive manner. + ([#1333](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1333)) +- Use resp.text instead of resp.body for Falcon 3 to avoid a deprecation warning. + ([#1412](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1412)) + +## Version 1.13.0/0.34b0 (2022-09-26) + +- `opentelemetry-instrumentation-asyncpg` Fix high cardinality in the span name + ([#1324](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1324)) + +### Added + +- `opentelemetry-instrumentation-grpc` add supports to filter requests to instrument. + ([#1241](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1241)) +- Flask sqlalchemy psycopg2 integration + ([#1224](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1224)) +- Add metric instrumentation in Falcon + ([#1230](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1230)) +- Add metric instrumentation in fastapi + ([#1199](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1199)) +- Add metric instrumentation in Pyramid + ([#1242](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1242)) +- `opentelemetry-util-http` Add support for sanitizing HTTP header values. + ([#1253](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1253)) +- Add metric instrumentation in starlette + ([#1327](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1327)) + +### Fixed + +- `opentelemetry-instrumentation-kafka-python`: wait for metadata + ([#1260](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1260)) +- `opentelemetry-instrumentation-boto3sqs` Make propagation compatible with other SQS instrumentations, add 'messaging.url' span attribute, and fix missing package dependencies. + ([#1234](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1234)) +- `opentelemetry-instrumentation-pymongo` Change span names to not contain queries but only database name and command name + ([#1247](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1247)) +- restoring metrics in django framework + ([#1208](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1208)) +- `opentelemetry-instrumentation-aiohttp-client` Fix producing additional spans with each newly created ClientSession +- ([#1246](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1246)) +- Add \_is_opentelemetry_instrumented check in \_InstrumentedFastAPI class + ([#1313](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1313)) +- Fix uninstrumentation of existing app instances in FastAPI + ([#1258](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1258)) +- Fix uninstrumentation of existing app instances in falcon + ([#1341]https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1341) + +## Version 1.12.0/0.33b0 (2022-08-08) + +- Adding multiple db connections support for django-instrumentation's sqlcommenter + ([#1187](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1187)) +- SQLCommenter semicolon bug fix + ([#1200](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1200/files)) +- Adding sqlalchemy native tags in sqlalchemy commenter + ([#1206](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1206)) +- Add psycopg2 native tags to sqlcommenter + ([#1203](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1203)) + +### Added + +- `opentelemetry-instrumentation-redis` add support to instrument RedisCluster clients + ([#1177](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1177)) +- `opentelemetry-instrumentation-sqlalchemy` Added span for the connection phase ([#1133](https://github.com/open-telemetry/opentelemetry-python-contrib/issues/1133)) +- Add metric instrumentation in asgi + ([#1197](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1197)) +- Add metric instrumentation for flask + ([#1186](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1186)) +- Add a test for asgi using NoOpTracerProvider + ([#1367](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1367)) + +## [1.12.0rc2-0.32b0](https://github.com/open-telemetry/opentelemetry-python/releases/tag/v1.12.0rc2-0.32b0) - 2022-07-01 + +- Pyramid: Only categorize 500s server exceptions as errors + ([#1037](https://github.com/open-telemetry/opentelemetry-python-contrib/issues/1037)) + +### Fixed + +- Fix bug in system metrics by checking their configuration + ([#1129](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1129)) +- Adding escape call to fix [auto-instrumentation not producing spans on Windows](https://github.com/open-telemetry/opentelemetry-python/issues/2703). + ([#1100](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1100)) +- `opentelemetry-instrumentation-grpc` narrow protobuf dependency to exclude protobuf >= 4 + ([#1109](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1109)) +- cleanup type hints for textmap `Getter` and `Setter` classes +- Suppressing downstream HTTP instrumentation to avoid [extra spans](https://github.com/open-telemetry/opentelemetry-python-contrib/issues/930) + ([#1116](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1116)) +- fixed typo in `system.network.io` metric configuration + ([#1135](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1135)) + +### Added + +- `opentelemetry-instrumentation-aiohttp-client` Add support for optional custom trace_configs argument. + ([1079](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1079)) +- `opentelemetry-instrumentation-sqlalchemy` add support to instrument multiple engines + ([#1132](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1132)) +- `opentelemetry-instrumentation-logging` add log hook support + ([#1117](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1117)) +- `opentelemetry-instrumentation-remoulade` Initial release + ([#1082](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1082)) +- Added `opentelemetry-instrumention-confluent-kafka` + ([#1111](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1111)) +- Set otlp-proto-grpc as the default metrics exporter for auto-instrumentation + ([#1127](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1127)) +- Add metric instrumentation for WSGI + ([#1128](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1128)) +- Add metric instrumentation for Urllib3 + ([#1198](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1198)) +- `opentelemetry-instrumentation-aio-pika` added RabbitMQ aio-pika module instrumentation. + ([#1095](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1095)) +- `opentelemetry-instrumentation-requests` Restoring metrics in requests + ([#1110](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1110)) +- Integrated sqlcommenter plugin into opentelemetry-instrumentation-django + ([#896](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/896)) + +## Version 1.12.0rc1/0.31b0 (2022-05-17) + +### Fixed + +- `opentelemetry-instrumentation-aiohttp-client` make span attributes available to sampler + ([#1072](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1072)) +- `opentelemetry-instrumentation-aws-lambda` Fixed an issue - in some rare cases (API GW proxy integration test) + headers are set to None, breaking context propagators. + ([#1055](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1055)) +- Refactoring custom header collection API for consistency + ([#1064](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1064)) +- `opentelemetry-instrumentation-sqlalchemy` will correctly report `otel.library.name` + ([#1086](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1086)) +- `opentelemetry-sdk-extension-aws` change timeout for AWS EC2 and EKS metadata requests from 1000 seconds and 2000 seconds to 1 second + +### Added + +- `opentelemetry-instrument` and `opentelemetry-bootstrap` now include a `--version` flag + ([#1065](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1065)) +- `opentelemetry-instrumentation-redis` now instruments asynchronous Redis clients, if the installed redis-py includes async support (>=4.2.0). + ([#1076](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1076)) +- `opentelemetry-instrumentation-boto3sqs` added AWS's SQS instrumentation. + ([#1081](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1081)) + +## Version 1.11.1/0.30b1 (2022-04-21) + +### Added + +- `opentelemetry-instrumentation-starlette` Capture custom request/response headers in span attributes + ([#1046](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1046)) + +### Fixed + +- Prune autoinstrumentation sitecustomize module directory from PYTHONPATH immediately + ([#1066](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1066)) + +## Version 1.11.0/0.30b0 (2022-04-18) + +### Fixed + +- `opentelemetry-instrumentation-pyramid` Fixed which package is the correct caller in \_traced_init. + ([#830](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/830)) +- `opentelemetry-instrumentation-tornado` Fix Tornado errors mapping to 500 + ([#1048](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1048)) +- `opentelemetry-instrumentation-urllib` make span attributes available to sampler + ([1014](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1014)) +- `opentelemetry-instrumentation-flask` Fix non-recording span bug + ([#999](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/999)) +- `opentelemetry-instrumentation-tornado` Fix non-recording span bug + ([#999](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/999)) + +### Added + +- `opentelemetry-instrumentation-fastapi` Capture custom request/response headers in span attributes + ([#1032](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1032)) +- `opentelemetry-instrumentation-django` Capture custom request/response headers in span attributes + ([#1024](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1024)) +- `opentelemetry-instrumentation-asgi` Capture custom request/response headers in span attributes + ([#1004](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1004)) +- `opentelemetry-instrumentation-psycopg2` extended the sql commenter support of dbapi into psycopg2 + ([#940](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/940)) +- `opentelemetry-instrumentation-falcon` Add support for falcon==1.4.1 + ([#1000](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1000)) +- `opentelemetry-instrumentation-falcon` Falcon: Capture custom request/response headers in span attributes + ([#1003](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1003)) +- `opentelemetry-instrumentation-elasticsearch` no longer creates unique span names by including search target, replaces them with `` and puts the value in attribute `elasticsearch.target` + ([#1018](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1018)) +- `opentelemetry-instrumentation-pyramid` Handle non-HTTPException exceptions + ([#1001](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1001)) +- `opentelemetry-instrumentation-system-metrics` restore `SystemMetrics` instrumentation as `SystemMetricsInstrumentor` + ([#1012](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1012)) +- `opentelemetry-instrumentation-pyramid` Pyramid: Capture custom request/response headers in span attributes + ([#1022](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1022)) + +## Version 1.10.0/0.29b0 (2022-03-10) + +- `opentelemetry-instrumentation-wsgi` Capture custom request/response headers in span attributes + ([#925](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/925)) +- `opentelemetry-instrumentation-flask` Flask: Capture custom request/response headers in span attributes + ([#952](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/952)) +- `opentelemetry-instrumentation-tornado` Tornado: Capture custom request/response headers in span attributes + ([#950](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/950)) + +### Added + +- `opentelemetry-instrumentation-aws-lambda` `SpanKind.SERVER` by default, add more cases for `SpanKind.CONSUMER` services. ([#926](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/926)) +- `opentelemetry-instrumentation-sqlalchemy` added experimental sql commenter capability + ([#924](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/924)) +- `opentelemetry-contrib-instrumentations` added new meta-package that installs all contrib instrumentations. + ([#681](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/681)) +- `opentelemetry-instrumentation-dbapi` add experimental sql commenter capability + ([#908](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/908)) +- `opentelemetry-instrumentation-requests` make span attribute available to samplers + ([#931](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/931)) +- `opentelemetry-datadog-exporter` add deprecation note to example. + ([#900](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/900)) + +### Fixed + +- `opentelemetry-instrumentation-dbapi` Changed the format of traceparent id. + ([#941](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/941)) +- `opentelemetry-instrumentation-logging` retrieves service name defensively. + ([#890](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/890)) +- `opentelemetry-instrumentation-wsgi` WSGI: Conditionally create SERVER spans + ([#903](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/903)) +- `opentelemetry-instrumentation-falcon` Safer patching mechanism + ([#895](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/895)) +- `opentelemetry-instrumentation-kafka-python` Fix topic extraction + ([#949](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/949)) + +### Changed + +- `opentelemetry-instrumentation-pymemcache` should run against newer versions of pymemcache. + ([#935](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/935)) + +## Version 1.9.1/0.28b1 (2022-01-29) + +### Fixed + +- `opentelemetry-instrumentation-pika` requires `packaging` dependency + +- `opentelemetry-instrumentation-tornado` Tornado: Conditionally create SERVER spans + ([#889](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/889)) + +## Version 1.9.0/0.28b0 (2022-01-26) + +### Added + +- `opentelemetry-instrumentation-pyramid` Pyramid: Conditionally create SERVER spans + ([#869](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/869)) +- `opentelemetry-instrumentation-grpc` added `trailing_metadata` to \_OpenTelemetryServicerContext. + ([#871](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/871)) +- `opentelemetry-instrumentation-asgi` now returns a `traceresponse` response header. + ([#817](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/817)) +- `opentelemetry-instrumentation-kafka-python` added kafka-python module instrumentation. + ([#814](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/814)) +- `opentelemetry-instrumentation-falcon` Falcon: Conditionally create SERVER spans + ([#867](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/867)) +- `opentelemetry-instrumentation-pymongo` now supports `pymongo v4` + ([#876](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/876)) + +- `opentelemetry-instrumentation-httpx` now supports versions higher than `0.19.0`. + ([#866](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/866)) + +### Fixed + +- `opentelemetry-instrumentation-django` Django: Conditionally create SERVER spans + ([#832](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/832)) +- `opentelemetry-instrumentation-flask` Flask: Conditionally create SERVER spans + ([#828](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/828)) +- `opentelemetry-instrumentation-celery` Celery: Support partial task time limit + ([#846](https://github.com/open-telemetry/opentelemetry-python-contrib/issues/846)) +- `opentelemetry-instrumentation-asgi` ASGI: Conditionally create SERVER spans + ([#843](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/843)) +- `opentelemetry-instrumentation-django` Django: fix issue preventing detection of MIDDLEWARE_CLASSES +- `opentelemetry-instrumentation-sqlite3` Instrumentation now works with `dbapi2.connect` +- `opentelemetry-instrumentation-kafka` Kafka: safe kafka partition extraction + ([#872](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/872)) +- `opentelemetry-instrumentation-aiohttp-client` aiohttp: Correct url filter input type + ([#843](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/864)) + +- `opentelemetry-instrumentation-aiohttp-client` aiohttp: Remove `span_name` from docs + ([#857](https://github.com/open-telemetry/opentelemetry-python-contrib/issues/857)) + +## Version 1.8.0/0.27b0 (2021-12-17) + +### Added + +- `opentelemetry-instrumentation-aws-lambda` Adds support for configurable flush timeout via `OTEL_INSTRUMENTATION_AWS_LAMBDA_FLUSH_TIMEOUT` property. ([#825](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/825)) +- `opentelemetry-instrumentation-pika` Adds support for versions between `0.12.0` to `1.0.0`. ([#837](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/837)) + +### Fixed + +- `opentelemetry-instrumentation-urllib` Fixed an error on unexpected status values. + ([#823](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/823)) + +- `opentelemetry-exporter-richconsole` Fixed attribute error on parentless spans. + ([#782](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/782)) + +- `opentelemetry-instrumentation-tornado` Add support instrumentation for Tornado 5.1.1 + ([#812](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/812)) + +## Version 1.7.1/0.26b1 (2021-11-11) + +### Added + +- `opentelemetry-instrumentation-aws-lambda` Add instrumentation for AWS Lambda Service - pkg metadata files (Part 1/2) + ([#739](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/739)) +- Add support for Python 3.10 + ([#742](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/742)) +- Pass in auto-instrumentation version to configurator + ([#783](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/783)) +- `opentelemetry-instrumentation` Add `setuptools` to `install_requires` + ([#781](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/781)) +- `opentelemetry-instrumentation-aws-lambda` Add instrumentation for AWS Lambda Service - Implementation (Part 2/2) + ([#777](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/777)) +- `opentelemetry-instrumentation-pymongo` Add `request_hook`, `response_hook` and `failed_hook` callbacks passed as arguments to the instrument method + ([#793](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/793)) +- `opentelemetry-instrumentation-pymysql` Add support for PyMySQL 1.x series + ([#792](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/792)) +- Add support for generic OTEL_PYTHON_EXCLUDED_URLS variable + ([#790](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/790)) + +### Fixed + +- `opentelemetry-instrumentation-asgi` now explicitly depends on asgiref as it uses the package instead of instrumenting it. + ([#765](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/765)) +- `opentelemetry-instrumentation-pika` now propagates context to basic_consume callback + ([#766](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/766)) +- `opentelemetry-instrumentation-falcon` Dropped broken support for Python 3.4. + ([#774](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/774)) +- `opentelemetry-instrumentation-django` Fixed carrier usage on ASGI requests. + ([#767](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/767)) +- Don't set Span Status on 4xx http status code for SpanKind.SERVER spans + ([#776](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/776)) +- `opentelemetry-instrumentation-django` Fixed instrumentation and tests for all Django major versions. + ([#780](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/780)) + +## Version 1.6.2/0.25b2 (2021-10-19) + +- `opentelemetry-instrumentation-sqlalchemy` Fix PostgreSQL instrumentation for Unix sockets + ([#761](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/761)) + +### Changed + +- `opentelemetry-sdk-extension-aws` & `opentelemetry-propagator-aws` Release AWS Python SDK Extension as 2.0.1 and AWS Propagator as 1.0.1 + ([#753](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/753)) +- `opentelemetry-instrumentation-pika` Add `_decorate_basic_consume` to ensure post instrumentation `basic_consume` calls are also instrumented. + ([#759](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/759)) +- Consolidate instrumentation documentation in docstrings + ([#754](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/754)) + +### Fixed + +- `opentelemetry-distro` uses the correct entrypoint name which was updated in the core release of 1.6.0 but the distro was not updated with it + ([#755](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/755)) + +### Added + +- `opentelemetry-instrumentation-pika` Add `publish_hook` and `consume_hook` callbacks passed as arguments to the instrument method + ([#763](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/763)) + +## Version 1.6.1/0.25b1 (2021-10-18) + +### Changed + +- `opentelemetry-util-http` no longer contains an instrumentation entrypoint and will not be loaded + automatically by the auto instrumentor. + ([#745](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/745)) +- `opentelemetry-instrumentation-pika` Bugfix use properties.headers. It will prevent the header injection from raising. + ([#740](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/740)) +- `opentelemetry-instrumentation-botocore` Add extension for DynamoDB + ([#735](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/735)) +- `opentelemetry-sdk-extension-aws` & `opentelemetry-propagator-aws` Remove unnecessary dependencies on `opentelemetry-test` + ([#752](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/752)) +- `opentelemetry-instrumentation-botocore` Add Lambda extension + ([#760](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/760)) + +## Version 1.6.0/0.25b0 (2021-10-13) + +### Added + +- `opentelemetry-sdk-extension-aws` Release AWS Python SDK Extension as 1.0.0 + ([#667](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/667)) +- `opentelemetry-instrumentation-urllib3`, `opentelemetry-instrumentation-requests` + The `net.peer.ip` attribute is set to the IP of the connected HTTP server or proxy + using a new instrumentor in `opententelemetry-util-http` + ([#661](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/661)) +- `opentelemetry-instrumentation-pymongo` Add check for suppression key in PyMongo. + ([#736](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/736)) +- `opentelemetry-instrumentation-elasticsearch` Added `response_hook` and `request_hook` callbacks + ([#670](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/670)) +- `opentelemetry-instrumentation-redis` added request_hook and response_hook callbacks passed as arguments to the instrument method. + ([#669](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/669)) +- `opentelemetry-instrumentation-botocore` add `request_hook` and `response_hook` callbacks + ([679](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/679)) +- `opentelemetry-exporter-richconsole` Initial release + ([#686](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/686)) +- `opentelemetry-instrumentation-elasticsearch` no longer creates unique span names by including document IDs, replaces them with `:id` and puts the value in attribute `elasticsearch.id` + ([#705](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/705)) +- `opentelemetry-instrumentation-tornado` now sets `http.client_ip` and `tornado.handler` attributes + ([#706](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/706)) +- `opentelemetry-instrumentation-requests` added exclude urls functionality + ([#714](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/714)) +- `opentelemetry-instrumentation-django` Add ASGI support + ([#391](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/391)) + +### Changed + +- `opentelemetry-instrumentation-flask` Fix `RuntimeError: Working outside of request context` + ([#734](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/734)) +- `opentelemetry-propagators-aws-xray` Rename `AwsXRayFormat` to `AwsXRayPropagator` + ([#729](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/729)) +- `opentelemetry-instrumentation-sqlalchemy` Respect provided tracer provider when instrumenting SQLAlchemy + ([#728](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/728)) +- `opentelemetry-sdk-extension-aws` Move AWS X-Ray Propagator into its own `opentelemetry-propagators-aws` package + ([#720](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/720)) +- `opentelemetry-instrumentation-sqlalchemy` Added `packaging` dependency + ([#713](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/713)) +- `opentelemetry-instrumentation-jinja2` Allow instrumentation of newer Jinja2 versions. + ([#712](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/712)) +- `opentelemetry-instrumentation-botocore` Make common span attributes compliant with semantic conventions + ([#674](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/674)) +- `opentelemetry-sdk-extension-aws` Release AWS Python SDK Extension as 1.0.0 + ([#667](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/667)) +- `opentelemetry-instrumentation-botocore` Unpatch botocore Endpoint.prepare_request on uninstrument + ([#664](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/664)) +- `opentelemetry-instrumentation-botocore` Fix span injection for lambda invoke + ([#663](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/663)) +- `opentelemetry-instrumentation-botocore` Introduce instrumentation extensions + ([#718](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/718)) +- `opentelemetry-instrumentation-urllib3` Updated `_RequestHookT` with two additional fields - the request body and the request headers + ([#660](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/660)) +- Tests for Falcon 3 support + ([#644](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/644)) + +## Version 1.5.0/0.24b0 (2021-08-26) + +### Added + +- `opentelemetry-sdk-extension-aws` Add AWS resource detectors to extension package + ([#586](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/586)) +- `opentelemetry-instrumentation-asgi`, `opentelemetry-instrumentation-aiohttp-client`, `openetelemetry-instrumentation-fastapi`, + `opentelemetry-instrumentation-starlette`, `opentelemetry-instrumentation-urllib`, `opentelemetry-instrumentation-urllib3` Added `request_hook` and `response_hook` callbacks + ([#576](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/576)) +- `opentelemetry-instrumentation-pika` added RabbitMQ's pika module instrumentation. + ([#680](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/680)) + +### Changed + +- `opentelemetry-instrumentation-fastapi` Allow instrumentation of newer FastAPI versions. + ([#602](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/602)) +- Enable explicit `excluded_urls` argument in `opentelemetry-instrumentation-flask` + ([#604](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/604)) + +## Version 1.4.0/0.23b0 (2021-07-21) + +### Removed + +- Move `opentelemetry-instrumentation` to the core repo. + ([#595](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/595)) + +### Changed + +- `opentelemetry-instrumentation-falcon` added support for Falcon 3. + ([#607](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/607)) +- `opentelemetry-instrumentation-tornado` properly instrument work done in tornado on_finish method. + ([#499](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/499)) +- `opentelemetry-instrumentation` Fixed cases where trying to use an instrumentation package without the + target library was crashing auto instrumentation agent. + ([#530](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/530)) +- Fix weak reference error for pyodbc cursor in SQLAlchemy instrumentation. + ([#469](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/469)) +- Implemented specification that HTTP span attributes must not contain username and password. + ([#538](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/538)) +- Changed the psycopg2-binary to psycopg2 as dependency in production + ([#543](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/543)) +- Implement consistent way of checking if instrumentation is already active + ([#549](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/549)) +- Require aiopg to be less than 1.3.0 + ([#560](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/560)) +- `opentelemetry-instrumentation-django` Migrated Django middleware to new-style. + ([#533](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/533)) +- Updating dependency for opentelemetry api/sdk packages to support major version instead + of pinning to specific versions. + ([#567](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/567)) +- `opentelemetry-instrumentation-grpc` Respect the suppress instrumentation in gRPC client instrumentor + ([#559](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/559)) +- `opentelemetry-instrumentation-grpc` Fixed asynchronous unary call traces + ([#536](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/536)) +- `opentelemetry-sdk-extension-aws` Update AWS entry points to match spec + ([#566](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/566)) +- Include Flask 2.0 as compatible with existing flask instrumentation + ([#545](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/545)) +- `openelemetry-sdk-extension-aws` Take a dependency on `opentelemetry-sdk` + ([#558](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/558)) +- Change `opentelemetry-instrumentation-httpx` to replace `client` classes with instrumented versions. + ([#577](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/577)) +- `opentelemetry-instrumentation-requests` Fix potential `AttributeError` when `requests` + is used with a custom transport adapter. + ([#562](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/562)) +- `opentelemetry-instrumentation-django` Fix AttributeError: ResolverMatch object has no attribute route + ([#581](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/581)) +- `opentelemetry-instrumentation-botocore` Suppress botocore downstream instrumentation like urllib3 + ([#563](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/563)) +- `opentelemetry-exporter-datadog` Datadog exporter should not use `unknown_service` as fallback resource service name. + ([#570](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/570)) +- Add support for the async extension of SQLAlchemy (>= 1.4) + ([#568](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/568)) + +### Added + +- `opentelemetry-instrumentation-httpx` Add `httpx` instrumentation + ([#461](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/461)) + +## Version 1.3.0/0.22b0 (2021-06-01) + +### Changed + +- `opentelemetry-bootstrap` not longer forcibly removes and re-installs libraries and their instrumentations. + This means running bootstrap will not auto-upgrade existing dependencies and as a result not cause dependency + conflicts. + ([#514](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/514)) +- `opentelemetry-instrumentation-asgi` Set the response status code on the server span + ([#478](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/478)) +- `opentelemetry-instrumentation-tornado` Fixed cases where description was used with non- + error status code when creating Status objects. + ([#504](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/504)) +- `opentelemetry-instrumentation-asgi` Fix instrumentation default span name. + ([#418](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/418)) +- Propagators use the root context as default for `extract` and do not modify + the context if extracting from carrier does not work. + ([#488](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/488)) + +### Added + +- `opentelemetry-instrumentation-botocore` now supports + context propagation for lambda invoke via Payload embedded headers. + ([#458](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/458)) +- Added support for CreateKey functionality. + ([#502](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/502)) + +## Version 1.2.0/0.21b0 (2021-05-11) + +### Changed + +- Instrumentation packages don't specify the libraries they instrument as dependencies + anymore. Instead, they verify the correct version of libraries are installed at runtime. + ([#475](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/475)) +- `opentelemetry-propagator-ot-trace` Use `TraceFlags` object in `extract` + ([#472](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/472)) +- Set the `traced_request_attrs` of FalconInstrumentor by an argument correctly. + ([#473](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/473)) +- Enable passing explicit urls to exclude in instrumentation in FastAPI + ([#486](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/486)) +- Distros can now implement `load_instrumentor(EntryPoint)` method to customize instrumentor + loading behaviour. + ([#480](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/480)) +- Fix entrypoint for ottrace propagator + ([#492](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/492)) + +### Added + +- Move `opentelemetry-instrumentation` from core repository + ([#465](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/465)) + +## Version 0.20b0 (2021-04-20) + +### Changed + +- Restrict DataDog exporter's `ddtrace` dependency to known working versions. + ([#400](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/400)) +- GRPC instrumentation now correctly injects trace context into outgoing requests. + ([#392](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/39)) +- Publish `opentelemetry-propagator-ot-trace` package as a part of the release process + ([#387](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/387)) +- Update redis instrumentation to follow semantic conventions + ([#403](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/403)) +- Update instrumentations to use tracer_provider for creating tracer if given, otherwise use global tracer provider + ([#402](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/402)) +- `opentelemetry-instrumentation-wsgi` Replaced `name_callback` with `request_hook` + and `response_hook` callbacks. + ([#424](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/424)) +- Update gRPC instrumentation to better wrap server context + ([#420](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/420)) +- `opentelemetry-instrumentation-redis` Fix default port KeyError and Wrong Attribute name (net.peer.ip -> net.peer.port) + ([#265](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/265)) +- `opentelemetry-instrumentation-asyncpg` Fix default port KeyError and Wrong Attribute name (net.peer.ip -> net.peer.port) + ([#265](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/265)) + +### Added + +- `opentelemetry-instrumentation-urllib3` Add urllib3 instrumentation + ([#299](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/299)) + +- `opentelemetry-instrumentation-flask` Added `request_hook` and `response_hook` callbacks. + ([#416](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/416)) + +- `opentelemetry-instrumenation-django` now supports request and response hooks. + ([#407](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/407)) +- `opentelemetry-instrumentation-falcon` FalconInstrumentor now supports request/response hooks. + ([#415](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/415)) +- `opentelemetry-instrumentation-tornado` Add request/response hooks. + ([#426](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/426)) +- `opentelemetry-exporter-datadog` Add parsing exception events for error tags. + ([#459](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/459)) +- `opentelemetry-instrumenation-django` now supports trace response headers. + ([#436](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/436)) +- `opentelemetry-instrumenation-tornado` now supports trace response headers. + ([#436](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/436)) +- `opentelemetry-instrumenation-pyramid` now supports trace response headers. + ([#436](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/436)) +- `opentelemetry-instrumenation-falcon` now supports trace response headers. + ([#436](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/436)) +- `opentelemetry-instrumenation-flask` now supports trace response headers. + ([#436](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/436)) +- `opentelemetry-instrumentation-grpc` Keep client interceptor in sync with grpc client interceptors. + ([#442](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/442)) + +### Removed + +- Remove `http.status_text` from span attributes + ([#406](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/406)) + +## Version 0.19b0 (2021-03-26) + +- Implement context methods for `_InterceptorChannel` + ([#363](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/363)) + +### Changed + +- Rename `IdsGenerator` to `IdGenerator` + ([#350](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/350)) +- `opentelemetry-exporter-datadog` Fix warning when DatadogFormat encounters a request with + no DD_ORIGIN headers ([#368](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/368)). +- `opentelemetry-instrumentation-aiopg` Fix multiple nested spans when + `aiopg.pool` is used + ([#336](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/381)). +- Updated instrumentations to use `opentelemetry.trace.use_span` instead of `Tracer.use_span()` + ([#364](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/364)) +- `opentelemetry-propagator-ot-trace` Do not throw an exception when headers are not present + ([#378](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/378)) +- `opentelemetry-instrumentation-wsgi` Reimplement `keys` method to return actual keys from the carrier instead of an empty list. + ([#379](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/379)) +- `opentelemetry-instrumentation-sqlalchemy` Fix multithreading issues in recording spans from SQLAlchemy + ([#315](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/315)) +- Make getters and setters optional + ([#372](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/372)) + +### Removed + +- Removing support for Python 3.5 + ([#374](https://github.com/open-telemetry/opentelemetry-python/pull/374)) + +## Version 0.18b0 (2021-02-16) + +### Added + +- `opentelemetry-propagator-ot-trace` Add OT Trace Propagator + ([#302](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/302)) +- `opentelemetry-instrumentation-logging` Added logging instrumentation to enable log - trace correlation. + ([#345](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/345)) + +### Removed + +- Remove `component` span attribute in instrumentations. + `opentelemetry-instrumentation-aiopg`, `opentelemetry-instrumentation-dbapi` Remove unused `database_type` parameter from `trace_integration` function. + ([#301](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/301)) +- `opentelemetry-instrumentation-asgi` Return header values using case insensitive keys + ([#308](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/308)) +- Remove metrics from all instrumentations + ([#312](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/312)) +- `opentelemetry-instrumentation-boto` updated to set span attributes instead of overriding the resource. + ([#310](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/310)) +- `opentelemetry-instrumentation-grpc` Fix issue tracking child spans in streaming responses + ([#260](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/260)) +- `opentelemetry-instrumentation-grpc` Updated client attributes, added tests, fixed examples, docs + ([#269](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/269)) + +## Version 0.17b0 (2021-01-20) + +### Added + +- `opentelemetry-instrumentation-sqlalchemy` Ensure spans have kind set to "CLIENT" + ([#278](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/278)) +- `opentelemetry-instrumentation-celery` Add support for Celery version 5.x + ([#266](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/266)) +- `opentelemetry-instrumentation-urllib` Add urllib instrumentation + ([#222](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/222)) +- `opentelemetry-exporter-datadog` Add fields method + ([#226](https://github.com/open-telemetry/opentelemetry-python/pull/226)) +- `opentelemetry-sdk-extension-aws` Add method to return fields injected by propagator + ([#226](https://github.com/open-telemetry/opentelemetry-python/pull/226)) +- `opentelemetry-exporter-prometheus-remote-write` Prometheus Remote Write Exporter Setup + ([#180](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/180)) +- `opentelemetry-exporter-prometheus-remote-write` Add Exporter constructor validation methods in Prometheus Remote Write Exporter + ([#206](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/206)) +- `opentelemetry-exporter-prometheus-remote-write` Add conversion to TimeSeries methods in Prometheus Remote Write Exporter + ([#207](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/207)) +- `opentelemetry-exporter-prometheus-remote-write` Add request methods to Prometheus Remote Write Exporter + ([#212](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/212)) +- `opentelemetry-instrumentation-fastapi` Added support for excluding some routes with env var `OTEL_PYTHON_FASTAPI_EXCLUDED_URLS` + ([#237](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/237)) +- `opentelemetry-instrumentation-starlette` Added support for excluding some routes with env var `OTEL_PYTHON_STARLETTE_EXCLUDED_URLS` + ([#237](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/237)) +- Add Prometheus Remote Write Exporter integration tests in opentelemetry-docker-tests + ([#216](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/216)) +- `opentelemetry-instrumentation-grpc` Add tests for grpc span attributes, grpc `abort()` conditions + ([#236](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/236)) +- Add README and example app for Prometheus Remote Write Exporter + ([#227](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/227])) +- `opentelemetry-instrumentation-botocore` Adds a field to report the number of retries it take to complete an API call + ([#275](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/275)) +- `opentelemetry-instrumentation-requests` Use instanceof to check if responses are valid Response objects + ([#273](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/273)) + +### Changed + +- Fix broken links to project ([#413](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/413)) +- `opentelemetry-instrumentation-asgi`, `opentelemetry-instrumentation-wsgi` Return `None` for `CarrierGetter` if key not found + ([#233](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/233)) +- `opentelemetry-instrumentation-grpc` Comply with updated spec, rework tests + ([#236](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/236)) +- `opentelemetry-instrumentation-asgi`, `opentelemetry-instrumentation-falcon`, `opentelemetry-instrumentation-flask`, `opentelemetry-instrumentation-pyramid`, `opentelemetry-instrumentation-wsgi` Renamed `host.port` attribute to `net.host.port` + ([#242](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/242)) +- `opentelemetry-instrumentation-flask` Do not emit a warning message for request contexts created with `app.test_request_context` + ([#253](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/253)) +- `opentelemetry-instrumentation-requests`, `opentelemetry-instrumentation-urllib` Fix span name callback parameters + ([#259](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/259)) +- `opentelemetry-exporter-datadog` Fix unintentional type change of span trace flags + ([#261](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/261)) +- `opentelemetry-instrumentation-aiopg` Fix AttributeError `__aexit__` when `aiopg.connect` and `aio[g].create_pool` used with async context manager + ([#235](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/235)) +- `opentelemetry-exporter-datadog` `opentelemetry-sdk-extension-aws` Fix reference to ids_generator in sdk + ([#283](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/283)) +- `opentelemetry-instrumentation-sqlalchemy` Use SQL operation and DB name as span name. + ([#254](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/254)) +- `opentelemetry-instrumentation-dbapi`, `TracedCursor` replaced by `CursorTracer` + ([#246](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/246)) +- `opentelemetry-instrumentation-psycopg2`, Added support for psycopg2 registered types. + ([#246](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/246)) +- `opentelemetry-instrumentation-dbapi`, `opentelemetry-instrumentation-psycopg2`, `opentelemetry-instrumentation-mysql`, `opentelemetry-instrumentation-pymysql`, `opentelemetry-instrumentation-aiopg` Use SQL command name as the span operation name instead of the entire query. + ([#246](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/246)) +- Update TraceState to adhere to specs + ([#276](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/276)) + +### Removed + +- Remove Configuration + ([#285](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/285)) + +## Version 0.16b1 (2020-11-26) + +## Version 0.16b0 (2020-11-25) + +### Added + +- `opentelemetry-instrumentation-flask` Add span name callback + ([#152](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/152)) +- `opentelemetry-sdk-extension-aws` Add AWS X-Ray Ids Generator Entry Point + ([#201](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/201)) +- `opentelemetry-sdk-extension-aws` Fix typo for installing OTel SDK in docs + ([#200](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/200)) +- `opentelemetry-sdk-extension-aws` Import missing components for docs + ([#198](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/198)) +- `opentelemetry-sdk-extension-aws` Provide components needed to Configure OTel SDK for Tracing with AWS X-Ray + ([#130](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/130)) +- `opentelemetry-instrumentation-sklearn` Initial release + ([#151](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/151)) +- `opentelemetry-instrumentation-requests` Add span name callback + ([#158](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/158)) +- `opentelemetry-instrumentation-botocore` Add propagator injection for botocore calls + ([#181](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/181)) + +### Changed + +- `opentelemetry-instrumentation-pymemcache` Update pymemcache instrumentation to follow semantic conventions + ([#183](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/183)) +- `opentelemetry-instrumentation-redis` Update redis instrumentation to follow semantic conventions + ([#184](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/184)) +- `opentelemetry-instrumentation-pymongo` Update pymongo instrumentation to follow semantic conventions + ([#203](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/203)) +- `opentelemetry-instrumentation-sqlalchemy` Update sqlalchemy instrumentation to follow semantic conventions + ([#202](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/202)) +- `opentelemetry-instrumentation-botocore` Make botocore instrumentation check if instrumentation has been suppressed + ([#182](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/182)) +- `opentelemetry-instrumentation-botocore` Botocore SpanKind as CLIENT and modify existing traced attributes + ([#150](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/150)) +- `opentelemetry-instrumentation-dbapi` Update dbapi and its dependent instrumentations to follow semantic conventions + ([#195](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/195)) +- `opentelemetry-instrumentation-dbapi` Stop capturing query parameters by default + ([#156](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/156)) +- `opentelemetry-instrumentation-asyncpg` Update asyncpg instrumentation to follow semantic conventions + ([#188](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/188)) +- `opentelemetry-instrumentation-grpc` Update protobuf versions + ([#1356](https://github.com/open-telemetry/opentelemetry-python/pull/1356)) + +## Version 0.15b0 (2020-11-02) + +### Added + +- `opentelemetry-instrumentation-requests` Add support for tracking http metrics + ([#1230](https://github.com/open-telemetry/opentelemetry-python/pull/1230)) +- `opentelemetry-instrumentation-django` Added capture of http.route + ([#1226](https://github.com/open-telemetry/opentelemetry-python/issues/1226)) +- `opentelemetry-instrumentation-django` Add support for tracking http metrics + ([#1230](https://github.com/open-telemetry/opentelemetry-python/pull/1230)) + +### Changed + +- `opentelemetry-exporter-datadog` Make `SpanProcessor.on_start` accept parent Context + ([#1251](https://github.com/open-telemetry/opentelemetry-python/pull/1251)) +- `opentelemetry-instrumentation-flask` Use `url.rule` instead of `request.endpoint` for span name + ([#1260](https://github.com/open-telemetry/opentelemetry-python/pull/1260)) +- `opentelemetry-instrumentation-django` Django instrumentation is now enabled by default but can be disabled by setting `OTEL_PYTHON_DJANGO_INSTRUMENT` to `False` + ([#1239](https://github.com/open-telemetry/opentelemetry-python/pull/1239)) +- `opentelemetry-instrumentation-django` Bugfix use request.path replace request.get_full_path(). It will get correct span name + ([#1309](https://github.com/open-telemetry/opentelemetry-python/pull/1309#)) +- `opentelemetry-instrumentation-django` Record span status and http.status_code attribute on exception + ([#1257](https://github.com/open-telemetry/opentelemetry-python/pull/1257)) +- `opentelemetry-instrumentation-grpc` Rewrite gRPC server interceptor + ([#1171](https://github.com/open-telemetry/opentelemetry-python/pull/1171)) + +## Version 0.14b0 (2020-10-13) + +### Added + +- `opentelemetry-exporter-datadog` Add support for span resource labels and service name +- `opentelemetry-instrumentation-celery` Span operation names now include the task type. + ([#1135](https://github.com/open-telemetry/opentelemetry-python/pull/1135)) +- `opentelemetry-instrumentation-celery` Added automatic context propagation. + ([#1135](https://github.com/open-telemetry/opentelemetry-python/pull/1135)) +- `opentelemetry-instrumentation-falcon` Added support for `OTEL_PYTHON_FALCON_TRACED_REQUEST_ATTRS` + ([#1158](https://github.com/open-telemetry/opentelemetry-python/pull/1158)) +- `opentelemetry-instrumentation-tornado` Added support for `OTEL_PYTHON_TORNADO_TRACED_REQUEST_ATTRS` + ([#1178](https://github.com/open-telemetry/opentelemetry-python/pull/1178)) +- `opentelemetry-instrumentation-django` Added support for `OTEL_PYTHON_DJANGO_TRACED_REQUEST_ATTRS` + ([#1154](https://github.com/open-telemetry/opentelemetry-python/pull/1154)) + +### Changed + +- `opentelemetry-instrumentation-pymongo` Cast PyMongo commands as strings + ([#1132](https://github.com/open-telemetry/opentelemetry-python/pull/1132)) +- `opentelemetry-instrumentation-system-metrics` Fix issue when specific metrics are not available in certain OS + ([#1207](https://github.com/open-telemetry/opentelemetry-python/pull/1207)) +- `opentelemetry-instrumentation-pymysql` Bumped version from 0.9.3 to 0.10.1 + ([#1228](https://github.com/open-telemetry/opentelemetry-python/pull/1228)) +- `opentelemetry-instrumentation-django` Changed span name extraction from request to comply semantic convention + ([#992](https://github.com/open-telemetry/opentelemetry-python/pull/992)) + +## Version 0.13b0 (2020-09-17) + +### Added + +- `opentelemetry-instrumentation-falcon` Initial release. Added instrumentation for Falcon 2.0+ +- `opentelemetry-instrumentation-tornado` Initial release. Supports Tornado 6.x on Python 3.5 and newer. +- `opentelemetry-instrumentation-aiohttp-client` Add instrumentor and auto instrumentation support for aiohttp + ([#1075](https://github.com/open-telemetry/opentelemetry-python/pull/1075)) +- `opentelemetry-instrumentation-requests` Add support for instrumenting prepared requests + ([#1040](https://github.com/open-telemetry/opentelemetry-python/pull/1040)) +- `opentelemetry-instrumentation-requests` Add support for http metrics + ([#1116](https://github.com/open-telemetry/opentelemetry-python/pull/1116)) + +### Changed + +- `opentelemetry-instrumentation-aiohttp-client` Updating span name to match semantic conventions + ([#972](https://github.com/open-telemetry/opentelemetry-python/pull/972)) +- `opentelemetry-instrumentation-dbapi` cursors and connections now produce spans when used with context managers + ([#1028](https://github.com/open-telemetry/opentelemetry-python/pull/1028)) + +### Removed + +- Drop support for Python 3.4 + ([#1099](https://github.com/open-telemetry/opentelemetry-python/pull/1099)) + +## Version 0.12b0 (2020-08-14) + +### Changed + +- `opentelemetry-ext-pymemcache` Change package name to opentelemetry-instrumentation-pymemcache + ([#966](https://github.com/open-telemetry/opentelemetry-python/pull/966)) +- `opentelemetry-ext-redis` Update default SpanKind to `SpanKind.CLIENT` + ([#965](https://github.com/open-telemetry/opentelemetry-python/pull/965)) +- `opentelemetry-ext-redis` Change package name to opentelemetry-instrumentation-redis + ([#966](https://github.com/open-telemetry/opentelemetry-python/pull/966)) +- `opentelemetry-ext-datadog` Change package name to opentelemetry-exporter-datadog + ([#953](https://github.com/open-telemetry/opentelemetry-python/pull/953)) +- `opentelemetry-ext-jinja2` Change package name to opentelemetry-instrumentation-jinja2 + ([#969](https://github.com/open-telemetry/opentelemetry-python/pull/969)) +- `opentelemetry-ext-elasticsearch` Update environment variable names, prefix changed from `OPENTELEMETRY` to `OTEL` + ([#904](https://github.com/open-telemetry/opentelemetry-python/pull/904)) +- `opentelemetry-ext-elasticsearch` Change package name to opentelemetry-instrumentation-elasticsearch + ([#969](https://github.com/open-telemetry/opentelemetry-python/pull/969)) +- `opentelemetry-ext-celery` Change package name to opentelemetry-instrumentation-celery + ([#969](https://github.com/open-telemetry/opentelemetry-python/pull/969)) +- `opentelemetry-ext-pyramid` Change package name to opentelemetry-instrumentation-pyramid + ([#966](https://github.com/open-telemetry/opentelemetry-python/pull/966)) +- `opentelemetry-ext-pyramid` Update environment variable names, prefix changed from `OPENTELEMETRY` to `OTEL` + ([#904](https://github.com/open-telemetry/opentelemetry-python/pull/904)) +- `opentelemetry-ext-pymongo` Change package name to opentelemetry-instrumentation-pymongo + ([#966](https://github.com/open-telemetry/opentelemetry-python/pull/966)) +- `opentelemetry-ext-sqlite3` Change package name to opentelemetry-instrumentation-sqlite3 + ([#966](https://github.com/open-telemetry/opentelemetry-python/pull/966)) +- `opentelemetry-ext-sqlalchemy` Change package name to opentelemetry-instrumentation-sqlalchemy + ([#966](https://github.com/open-telemetry/opentelemetry-python/pull/966)) +- `opentelemetry-ext-psycopg2` Change package name to opentelemetry-instrumentation-psycopg2 + ([#966](https://github.com/open-telemetry/opentelemetry-python/pull/966)) +- `opentelemetry-ext-aiohttp-client` Change package name to opentelemetry-instrumentation-aiohttp-client + ([#961](https://github.com/open-telemetry/opentelemetry-python/pull/961)) +- `opentelemetry-ext-boto` Change package name to opentelemetry-instrumentation-boto + ([#969](https://github.com/open-telemetry/opentelemetry-python/pull/969)) +- `opentelemetry-ext-system-metrics` Change package name to opentelemetry-instrumentation-system-metrics + ([#969](https://github.com/open-telemetry/opentelemetry-python/pull/969)) +- `opentelemetry-ext-asgi` Change package name to opentelemetry-instrumentation-asgi + ([#961](https://github.com/open-telemetry/opentelemetry-python/pull/961)) +- `opentelemetry-ext-wsgi` Change package name to opentelemetry-instrumentation-wsgi + ([#961](https://github.com/open-telemetry/opentelemetry-python/pull/961)) +- `opentelemetry-ext-pymysql` Change package name to opentelemetry-instrumentation-pymysql + ([#966](https://github.com/open-telemetry/opentelemetry-python/pull/966)) +- `opentelemetry-ext-requests` Change package name to opentelemetry-instrumentation-requests + ([#961](https://github.com/open-telemetry/opentelemetry-python/pull/961)) +- `opentelemetry-ext-requests` Span name reported updated to follow semantic conventions to reduce + cardinality ([#972](https://github.com/open-telemetry/opentelemetry-python/pull/972)) +- `opentelemetry-ext-botocore` Change package name to opentelemetry-instrumentation-botocore + ([#969](https://github.com/open-telemetry/opentelemetry-python/pull/969)) +- `opentelemetry-ext-dbapi` Change package name to opentelemetry-instrumentation-dbapi + ([#966](https://github.com/open-telemetry/opentelemetry-python/pull/966)) +- `opentelemetry-ext-flask` Change package name to opentelemetry-instrumentation-flask + ([#961](https://github.com/open-telemetry/opentelemetry-python/pull/961)) +- `opentelemetry-ext-flask` Update environment variable names, prefix changed from `OPENTELEMETRY` to `OTEL` + ([#904](https://github.com/open-telemetry/opentelemetry-python/pull/904)) +- `opentelemetry-ext-django` Change package name to opentelemetry-instrumentation-django + ([#961](https://github.com/open-telemetry/opentelemetry-python/pull/961)) +- `opentelemetry-ext-django` Update environment variable names, prefix changed from `OPENTELEMETRY` to `OTEL` + ([#904](https://github.com/open-telemetry/opentelemetry-python/pull/904)) +- `opentelemetry-ext-asyncpg` Change package name to opentelemetry-instrumentation-asyncpg + ([#966](https://github.com/open-telemetry/opentelemetry-python/pull/966)) +- `opentelemetry-ext-mysql` Change package name to opentelemetry-instrumentation-mysql + ([#966](https://github.com/open-telemetry/opentelemetry-python/pull/966)) +- `opentelemetry-ext-grpc` Change package name to opentelemetry-instrumentation-grpc + ([#969](https://github.com/open-telemetry/opentelemetry-python/pull/969)) + +## Version 0.11b0 (2020-07-28) + +### Added + +- `opentelemetry-instrumentation-aiopg` Initial release +- `opentelemetry-instrumentation-fastapi` Initial release + ([#890](https://github.com/open-telemetry/opentelemetry-python/pull/890)) +- `opentelemetry-ext-grpc` Add status code to gRPC client spans + ([896](https://github.com/open-telemetry/opentelemetry-python/pull/896)) +- `opentelemetry-ext-grpc` Add gRPC client and server instrumentors + ([788](https://github.com/open-telemetry/opentelemetry-python/pull/788)) +- `opentelemetry-ext-grpc` Add metric recording (bytes in/out, errors, latency) to gRPC client + +### Changed + +- `opentelemetry-ext-pyramid` Use one general exclude list instead of two + ([#872](https://github.com/open-telemetry/opentelemetry-python/pull/872)) +- `opentelemetry-ext-boto` fails to export spans via jaeger + ([#866](https://github.com/open-telemetry/opentelemetry-python/pull/866)) +- `opentelemetry-ext-botocore` fails to export spans via jaeger + ([#866](https://github.com/open-telemetry/opentelemetry-python/pull/866)) +- `opentelemetry-ext-wsgi` Set span status on wsgi errors + ([#864](https://github.com/open-telemetry/opentelemetry-python/pull/864)) +- `opentelemetry-ext-flask` Use one general exclude list instead of two + ([#872](https://github.com/open-telemetry/opentelemetry-python/pull/872)) +- `opentelemetry-ext-django` Use one general exclude list instead of two + ([#872](https://github.com/open-telemetry/opentelemetry-python/pull/872)) +- `opentelemetry-ext-asyncpg` Shouldn't capture query parameters by default + ([#854](https://github.com/open-telemetry/opentelemetry-python/pull/854)) +- `opentelemetry-ext-mysql` bugfix: Fix auto-instrumentation entry point for mysql + ([#858](https://github.com/open-telemetry/opentelemetry-python/pull/858)) + +## Version 0.10b0 (2020-06-23) + +### Added + +- `opentelemetry-ext-pymemcache` Initial release +- `opentelemetry-ext-elasticsearch` Initial release +- `opentelemetry-ext-celery` Add instrumentation for Celery + ([#780](https://github.com/open-telemetry/opentelemetry-python/pull/780)) +- `opentelemetry-instrumentation-starlette` Initial release + ([#777](https://github.com/open-telemetry/opentelemetry-python/pull/777)) +- `opentelemetry-ext-asyncpg` Initial Release + ([#814](https://github.com/open-telemetry/opentelemetry-python/pull/814)) + +## Version 0.9b0 (2020-06-10) + +### Added + +- `opentelemetry-ext-pyramid` Initial release +- `opentelemetry-ext-boto` Initial release +- `opentelemetry-ext-botocore` Initial release +- `opentelemetry-ext-system-metrics` Initial release + (https://github.com/open-telemetry/opentelemetry-python/pull/652) + +## Version 0.8b0 (2020-05-27) + +### Added + +- `opentelemetry-ext-datadog` Add exporter to Datadog + ([#572](https://github.com/open-telemetry/opentelemetry-python/pull/572)) +- `opentelemetry-ext-sqlite3` Initial release +- `opentelemetry-ext-psycopg2` Implement instrumentor interface, enabling auto-instrumentation + ([#694](https://github.com/open-telemetry/opentelemetry-python/pull/694)) +- `opentelemetry-ext-asgi` Add ASGI middleware + ([#716](https://github.com/open-telemetry/opentelemetry-python/pull/716)) +- `opentelemetry-ext-django` Add exclude list for paths and hosts to prevent from tracing + ([#670](https://github.com/open-telemetry/opentelemetry-python/pull/670)) +- `opentelemetry-ext-django` Add support for django >= 1.10 (#717) + +### Changed + +- `opentelemetry-ext-grpc` lint: version of grpc causes lint issues + ([#696](https://github.com/open-telemetry/opentelemetry-python/pull/696)) + +## Version 0.7b1 (2020-05-12) + +### Added + +- `opentelemetry-ext-redis` Initial release +- `opentelemetry-ext-jinja2` Add jinja2 instrumentation + ([#643](https://github.com/open-telemetry/opentelemetry-python/pull/643)) +- `opentelemetry-ext-pymongo` Implement instrumentor interface + ([#612](https://github.com/open-telemetry/opentelemetry-python/pull/612)) +- `opentelemetry-ext-sqlalchemy` Initial release +- `opentelemetry-ext-aiohttp-client` Initial release +- `opentelemetry-ext-pymysql` Initial release +- `opentelemetry-ext-http-requests` Implement instrumentor interface, enabling auto-instrumentation + ([#597](https://github.com/open-telemetry/opentelemetry-python/pull/597)) +- `opentelemetry-ext-http-requests` Adding disable_session for more granular instrumentation control + ([#573](https://github.com/open-telemetry/opentelemetry-python/pull/573)) +- `opentelemetry-ext-http-requests` Add a callback for custom attributes + ([#656](https://github.com/open-telemetry/opentelemetry-python/pull/656)) +- `opentelemetry-ext-dbapi` Implement instrument_connection and uninstrument_connection + ([#624](https://github.com/open-telemetry/opentelemetry-python/pull/624)) +- `opentelemetry-ext-flask` Add exclude list for paths and hosts + ([#630](https://github.com/open-telemetry/opentelemetry-python/pull/630)) +- `opentelemetry-ext-django` Initial release +- `opentelemetry-ext-mysql` Implement instrumentor interface + ([#654](https://github.com/open-telemetry/opentelemetry-python/pull/654)) + +### Changed + +- `opentelemetry-ext-http-requests` Rename package to opentelemetry-ext-requests + ([#619](https://github.com/open-telemetry/opentelemetry-python/pull/619)) + +## Version 0.6b0 (2020-03-30) + +### Added + +- `opentelemetry-ext-flask` Add an entry_point to be usable in auto-instrumentation + ([#327](https://github.com/open-telemetry/opentelemetry-python/pull/327)) +- `opentelemetry-ext-grpc` Add gRPC integration + ([#476](https://github.com/open-telemetry/opentelemetry-python/pull/476)) + +## Version 0.5b0 (2020-03-16) + +## Version 0.4a0 (2020-02-21) + +### Added + +- `opentelemetry-ext-psycopg2` Initial release +- `opentelemetry-ext-dbapi` Initial release +- `opentelemetry-ext-mysql` Initial release + +### Changed + +- `opentelemetry-ext-pymongo` Updating network connection attribute names + ([#350](https://github.com/open-telemetry/opentelemetry-python/pull/350)) +- `opentelemetry-ext-wsgi` Updating network connection attribute names + ([#350](https://github.com/open-telemetry/opentelemetry-python/pull/350)) +- `opentelemetry-ext-flask` Use string keys for WSGI environ values + ([#366](https://github.com/open-telemetry/opentelemetry-python/pull/366)) + +## Version 0.3a0 (2019-12-11) + +### Added + +- `opentelemetry-ext-flask` Initial release +- `opentelemetry-ext-pymongo` Initial release + +### Changed + +- `opentelemetry-ext-wsgi` Support new semantic conventions + ([#299](https://github.com/open-telemetry/opentelemetry-python/pull/299)) +- `opentelemetry-ext-wsgi` Updates for core library changes + +## Version 0.2a0 (2019-10-29) + +### Changed + +- `opentelemetry-ext-wsgi` Updates for core library changes +- `opentelemetry-ext-http-requests` Updates for core library changes + +- `Added support for PyPy3` Initial release + +## [#1033](https://github.com/open-telemetryopentelemetry-python-contrib/issues/1033) + +## Version 0.1a0 (2019-09-30) + +### Added + +- `opentelemetry-ext-wsgi` Initial release +- `opentelemetry-ext-http-requests` Initial release + +- Drop support for 3.7 + ([#2151](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2151)) +- `opentelemetry-resource-detector-azure` Added 10s timeout to VM Resource Detector + ([#2119](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2119)) +- `opentelemetry-instrumentation-asyncpg` Allow AsyncPGInstrumentor to be instantiated multiple times + ([#1791](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1791)) +- `opentelemetry-instrumentation-confluent-kafka` Add support for higher versions until 2.3.0 of confluent_kafka + ([#2132](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2132)) +- `opentelemetry-resource-detector-azure` Changed timeout to 4 seconds due to [timeout bug](https://github.com/open-telemetry/opentelemetry-python/issues/3644) + ([#2136](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2136)) +- `opentelemetry-resource-detector-azure` Suppress instrumentation for `urllib` call + ([#2178](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2178)) +- AwsLambdaInstrumentor handles and re-raises function exception ([#2245](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2245)) diff --git a/CONTRIBUTING-zh.md b/CONTRIBUTING-loongsuite-zh.md similarity index 100% rename from CONTRIBUTING-zh.md rename to CONTRIBUTING-loongsuite-zh.md diff --git a/CONTRIBUTING-upstream.md b/CONTRIBUTING.md similarity index 100% rename from CONTRIBUTING-upstream.md rename to CONTRIBUTING.md diff --git a/README-upstream.md b/README-upstream.md index d8f008664..e2a86f492 100644 --- a/README-upstream.md +++ b/README-upstream.md @@ -124,6 +124,7 @@ For more information about the maintainer role, see the [community repository](h - [Dylan Russell](https://github.com/dylanrussell), Google - [Emídio Neto](https://github.com/emdneto), PicPay - [Jeremy Voss](https://github.com/jeremydvoss), Microsoft +- [Liudmila Molkova](https://github.com/lmolkova), Grafana Labs - [Owais Lone](https://github.com/owais), Splunk - [Pablo Collins](https://github.com/pmcollins), Splunk - [Sanket Mehta](https://github.com/sanketmehta28), Cisco diff --git a/dev-requirements.txt b/dev-requirements.txt index b41f3d7b9..b2de8cecf 100644 --- a/dev-requirements.txt +++ b/dev-requirements.txt @@ -15,4 +15,4 @@ ruamel.yaml==0.17.21 flaky==3.7.0 pre-commit==3.7.0; python_version >= '3.9' pre-commit==3.5.0; python_version < '3.9' -ruff==0.6.9 +ruff==0.14.1 diff --git a/exporter/opentelemetry-exporter-prometheus-remote-write/tests/test_prometheus_remote_write_exporter.py b/exporter/opentelemetry-exporter-prometheus-remote-write/tests/test_prometheus_remote_write_exporter.py index 1c5034435..814de75be 100644 --- a/exporter/opentelemetry-exporter-prometheus-remote-write/tests/test_prometheus_remote_write_exporter.py +++ b/exporter/opentelemetry-exporter-prometheus-remote-write/tests/test_prometheus_remote_write_exporter.py @@ -122,9 +122,9 @@ def test_parse_metric(metric, prom_rw): "bool_value": True, } - assert ( - len(metric.data.data_points) == 1 - ), "We can only support a single datapoint in tests" + assert len(metric.data.data_points) == 1, ( + "We can only support a single datapoint in tests" + ) series = prom_rw._parse_metric(metric, tuple(attributes.items())) timestamp = metric.data.data_points[0].time_unix_nano // 1_000_000 for single_series in series: diff --git a/gen-requirements.txt b/gen-requirements.txt index 57bfcd89a..2512362a9 100644 --- a/gen-requirements.txt +++ b/gen-requirements.txt @@ -2,7 +2,7 @@ astor==0.8.1 jinja2==3.1.6 markupsafe==2.0.1 -ruff==0.6.9 +ruff==0.14.1 requests tomli tomli_w diff --git a/instrumentation-genai/opentelemetry-instrumentation-google-genai/CHANGELOG.md b/instrumentation-genai/opentelemetry-instrumentation-google-genai/CHANGELOG.md index 13e8d32fc..7f0e70877 100644 --- a/instrumentation-genai/opentelemetry-instrumentation-google-genai/CHANGELOG.md +++ b/instrumentation-genai/opentelemetry-instrumentation-google-genai/CHANGELOG.md @@ -7,7 +7,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased -- Minor change to check LRU cache in Completion Hook before acquiring semaphore/thread ([#3907](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3907)). +- Ensure log event is written and completion hook is called even when model call results in exception. Put new +log event (` gen_ai.client.inference.operation.details`) behind the flag `OTEL_SEMCONV_STABILITY_OPT_IN=gen_ai_latest_experimental`. +Ensure same sem conv attributes are on the log and span. Fix an issue where the instrumentation would crash when a pydantic.BaseModel class was passed as the response schema ([#3905](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3905)). ## Version 0.4b0 (2025-10-16) diff --git a/instrumentation-genai/opentelemetry-instrumentation-google-genai/src/opentelemetry/instrumentation/google_genai/dict_util.py b/instrumentation-genai/opentelemetry-instrumentation-google-genai/src/opentelemetry/instrumentation/google_genai/dict_util.py index 6f39474ed..037311626 100644 --- a/instrumentation-genai/opentelemetry-instrumentation-google-genai/src/opentelemetry/instrumentation/google_genai/dict_util.py +++ b/instrumentation-genai/opentelemetry-instrumentation-google-genai/src/opentelemetry/instrumentation/google_genai/dict_util.py @@ -151,7 +151,7 @@ def _flatten_compound_value_using_json( ) -def _flatten_compound_value( +def _flatten_compound_value( # pylint: disable=too-many-return-statements key: str, value: Any, exclude_keys: Set[str], @@ -189,13 +189,16 @@ def _flatten_compound_value( flatten_functions=flatten_functions, ) if hasattr(value, "model_dump"): - return _flatten_dict( - value.model_dump(), - key_prefix=key, - exclude_keys=exclude_keys, - rename_keys=rename_keys, - flatten_functions=flatten_functions, - ) + try: + return _flatten_dict( + value.model_dump(), + key_prefix=key, + exclude_keys=exclude_keys, + rename_keys=rename_keys, + flatten_functions=flatten_functions, + ) + except TypeError: + return {key: str(value)} return _flatten_compound_value_using_json( key, value, diff --git a/instrumentation-genai/opentelemetry-instrumentation-google-genai/src/opentelemetry/instrumentation/google_genai/generate_content.py b/instrumentation-genai/opentelemetry-instrumentation-google-genai/src/opentelemetry/instrumentation/google_genai/generate_content.py index 4598915dc..82dda55d1 100644 --- a/instrumentation-genai/opentelemetry-instrumentation-google-genai/src/opentelemetry/instrumentation/google_genai/generate_content.py +++ b/instrumentation-genai/opentelemetry-instrumentation-google-genai/src/opentelemetry/instrumentation/google_genai/generate_content.py @@ -162,30 +162,24 @@ def _to_dict(value: object): if isinstance(value, dict): return value if hasattr(value, "model_dump"): - return value.model_dump() + try: + return value.model_dump() + except TypeError: + return {"ModelName": str(value)} + return json.loads(json.dumps(value)) -def _add_request_options_to_span( - span: Span, +def _create_request_attributes( config: Optional[GenerateContentConfigOrDict], + is_experimental_mode: bool, allow_list: AllowList, -): - if config is None: - return - span_context = span.get_span_context() - if not span_context.trace_flags.sampled: - # Avoid potentially costly traversal of config - # options if the span will be dropped, anyway. - return - # Automatically derive attributes from the contents of the - # config object. This ensures that all relevant parameters - # are captured in the telemetry data (except for those - # that are excluded via "exclude_keys"). Dynamic attributes (those - # starting with "gcp.gen_ai." instead of simply "gen_ai.request.") - # are filtered with the "allow_list" before inclusion in the span. +) -> dict[str, Any]: + if not config: + return {} + config = _to_dict(config) attributes = flatten_dict( - _to_dict(config), + config, # A custom prefix is used, because the names/structure of the # configuration is likely to be specific to Google Gen AI SDK. key_prefix=GCP_GENAI_OPERATION_CONFIG, @@ -212,37 +206,21 @@ def _add_request_options_to_span( "gcp.gen_ai.operation.config.seed": gen_ai_attributes.GEN_AI_REQUEST_SEED, }, ) - for key, value in attributes.items(): - if key.startswith( - GCP_GENAI_OPERATION_CONFIG - ) and not allow_list.allowed(key): - # The allowlist is used to control inclusion of the dynamic keys. - continue - span.set_attribute(key, value) - - -def _get_gen_ai_request_attributes( - config: Union[GenerateContentConfigOrDict, None], -) -> dict[str, Any]: - if not config: - return {} - attributes: dict[str, Any] = {} - config = _coerce_config_to_object(config) - if config.seed: - attributes[gen_ai_attributes.GEN_AI_REQUEST_SEED] = config.seed - if config.candidate_count: - attributes[gen_ai_attributes.GEN_AI_REQUEST_CHOICE_COUNT] = ( - config.candidate_count - ) - if config.response_mime_type: - if config.response_mime_type == "text/plain": + response_mime_type = config.get("response_mime_type") + if response_mime_type and is_experimental_mode: + if response_mime_type == "text/plain": attributes[gen_ai_attributes.GEN_AI_OUTPUT_TYPE] = "text" - elif config.response_mime_type == "application/json": + elif response_mime_type == "application/json": attributes[gen_ai_attributes.GEN_AI_OUTPUT_TYPE] = "json" else: attributes[gen_ai_attributes.GEN_AI_OUTPUT_TYPE] = ( - config.response_mime_type + response_mime_type ) + for key in list(attributes.keys()): + if key.startswith( + GCP_GENAI_OPERATION_CONFIG + ) and not allow_list.allowed(key): + del attributes[key] return attributes @@ -372,19 +350,25 @@ def start_span_as_current_span( end_on_exit=end_on_exit, ) - def add_request_options_to_span( - self, config: Optional[GenerateContentConfigOrDict] - ): - span = trace.get_current_span() - _add_request_options_to_span( - span, config, self._generate_content_config_key_allowlist - ) + def create_final_attributes(self) -> dict[str, Any]: + final_attributes = { + gen_ai_attributes.GEN_AI_USAGE_INPUT_TOKENS: self._input_tokens, + gen_ai_attributes.GEN_AI_USAGE_OUTPUT_TOKENS: self._output_tokens, + gen_ai_attributes.GEN_AI_RESPONSE_FINISH_REASONS: sorted( + self._finish_reasons_set + ), + } + if self._error_type: + final_attributes[error_attributes.ERROR_TYPE] = self._error_type + return final_attributes def process_request( self, contents: Union[ContentListUnion, ContentListUnionDict], config: Optional[GenerateContentConfigOrDict], + span: Span, ): + span.set_attribute(gen_ai_attributes.GEN_AI_SYSTEM, self._genai_system) self._maybe_log_system_instruction(config=config) self._maybe_log_user_prompt(contents) @@ -393,39 +377,9 @@ def process_response(self, response: GenerateContentResponse): self._maybe_log_response(response) self._response_index += 1 - def process_completion( - self, - request: Union[ContentListUnion, ContentListUnionDict], - response: GenerateContentResponse, - config: Optional[GenerateContentConfigOrDict] = None, - ): - self._update_response(response) - self._maybe_log_completion_details( - request, response.candidates or [], config - ) - def process_error(self, e: Exception): self._error_type = str(e.__class__.__name__) - def finalize_processing(self): - span = trace.get_current_span() - span.set_attribute( - gen_ai_attributes.GEN_AI_USAGE_INPUT_TOKENS, self._input_tokens - ) - span.set_attribute( - gen_ai_attributes.GEN_AI_USAGE_OUTPUT_TOKENS, self._output_tokens - ) - span.set_attribute( - gen_ai_attributes.GEN_AI_RESPONSE_FINISH_REASONS, - sorted(self._finish_reasons_set), - ) - if self.sem_conv_opt_in_mode == _StabilityMode.DEFAULT: - span.set_attribute( - gen_ai_attributes.GEN_AI_SYSTEM, self._genai_system - ) - self._record_token_usage_metric() - self._record_duration_metric() - def _update_response(self, response: GenerateContentResponse): # TODO: Determine if there are other response properties that # need to be reflected back into the span attributes. @@ -488,11 +442,17 @@ def _maybe_update_error_type(self, response: GenerateContentResponse): def _maybe_log_completion_details( self, + request_attributes: dict[str, Any], + final_attributes: dict[str, Any], request: Union[ContentListUnion, ContentListUnionDict], candidates: list[Candidate], config: Optional[GenerateContentConfigOrDict] = None, ): - attributes = _get_gen_ai_request_attributes(config) + if ( + self.sem_conv_opt_in_mode + != _StabilityMode.GEN_AI_LATEST_EXPERIMENTAL + ): + return system_instructions = [] if system_content := _config_to_system_instruction(config): system_instructions = to_system_instructions( @@ -506,7 +466,7 @@ def _maybe_log_completion_details( span = trace.get_current_span() event = LogRecord( event_name="gen_ai.client.inference.operation.details", - attributes=attributes, + attributes=request_attributes | final_attributes, ) self.completion_hook.on_completion( inputs=input_messages, @@ -540,7 +500,7 @@ def _maybe_log_completion_details( for k, v in completion_details_attributes.items() } ) - span.set_attributes(attributes) + # request attributes were already set on the span.. def _maybe_log_system_instruction( self, config: Optional[GenerateContentConfigOrDict] = None @@ -748,6 +708,7 @@ def instrumented_generate_content( config: Optional[GenerateContentConfigOrDict] = None, **kwargs: Any, ) -> GenerateContentResponse: + candidates = [] helper = _GenerateContentInstrumentationHelper( self, otel_wrapper, @@ -755,12 +716,21 @@ def instrumented_generate_content( completion_hook, generate_content_config_key_allowlist=generate_content_config_key_allowlist, ) + is_experimental_mode = ( + helper.sem_conv_opt_in_mode + == _StabilityMode.GEN_AI_LATEST_EXPERIMENTAL + ) + request_attributes = _create_request_attributes( + config, + is_experimental_mode, + helper._generate_content_config_key_allowlist, + ) with helper.start_span_as_current_span( model, "google.genai.Models.generate_content" - ): - helper.add_request_options_to_span(config) + ) as span: + span.set_attributes(request_attributes) if helper.sem_conv_opt_in_mode == _StabilityMode.DEFAULT: - helper.process_request(contents, config) + helper.process_request(contents, config, span) try: response = wrapped_func( self, @@ -769,23 +739,29 @@ def instrumented_generate_content( config=helper.wrapped_config(config), **kwargs, ) - if helper.sem_conv_opt_in_mode == _StabilityMode.DEFAULT: - helper.process_response(response) - elif ( - helper.sem_conv_opt_in_mode - == _StabilityMode.GEN_AI_LATEST_EXPERIMENTAL - ): - helper.process_completion(contents, response, config) + if is_experimental_mode: + helper._update_response(response) + if response.candidates: + candidates += response.candidates + else: - raise ValueError( - f"Sem Conv opt in mode {helper.sem_conv_opt_in_mode} not supported." - ) + helper.process_response(response) return response except Exception as error: helper.process_error(error) raise finally: - helper.finalize_processing() + final_attributes = helper.create_final_attributes() + span.set_attributes(final_attributes) + helper._maybe_log_completion_details( + request_attributes, + final_attributes, + contents, + candidates, + config, + ) + helper._record_token_usage_metric() + helper._record_duration_metric() return instrumented_generate_content @@ -815,12 +791,21 @@ def instrumented_generate_content_stream( completion_hook, generate_content_config_key_allowlist=generate_content_config_key_allowlist, ) + is_experimental_mode = ( + helper.sem_conv_opt_in_mode + == _StabilityMode.GEN_AI_LATEST_EXPERIMENTAL + ) + request_attributes = _create_request_attributes( + config, + is_experimental_mode, + helper._generate_content_config_key_allowlist, + ) with helper.start_span_as_current_span( model, "google.genai.Models.generate_content_stream" - ): - helper.add_request_options_to_span(config) + ) as span: + span.set_attributes(request_attributes) if helper.sem_conv_opt_in_mode == _StabilityMode.DEFAULT: - helper.process_request(contents, config) + helper.process_request(contents, config, span) try: for response in wrapped_func( self, @@ -829,28 +814,29 @@ def instrumented_generate_content_stream( config=helper.wrapped_config(config), **kwargs, ): - if helper.sem_conv_opt_in_mode == _StabilityMode.DEFAULT: - helper.process_response(response) - elif ( - helper.sem_conv_opt_in_mode - == _StabilityMode.GEN_AI_LATEST_EXPERIMENTAL - ): + if is_experimental_mode: helper._update_response(response) if response.candidates: candidates += response.candidates + else: - raise ValueError( - f"Sem Conv opt in mode {helper.sem_conv_opt_in_mode} not supported." - ) + helper.process_response(response) yield response except Exception as error: helper.process_error(error) raise finally: + final_attributes = helper.create_final_attributes() + span.set_attributes(final_attributes) helper._maybe_log_completion_details( - contents, candidates, config + request_attributes, + final_attributes, + contents, + candidates, + config, ) - helper.finalize_processing() + helper._record_token_usage_metric() + helper._record_duration_metric() return instrumented_generate_content_stream @@ -879,12 +865,22 @@ async def instrumented_generate_content( completion_hook, generate_content_config_key_allowlist=generate_content_config_key_allowlist, ) + is_experimental_mode = ( + helper.sem_conv_opt_in_mode + == _StabilityMode.GEN_AI_LATEST_EXPERIMENTAL + ) + request_attributes = _create_request_attributes( + config, + is_experimental_mode, + helper._generate_content_config_key_allowlist, + ) + candidates: list[Candidate] = [] with helper.start_span_as_current_span( model, "google.genai.AsyncModels.generate_content" - ): - helper.add_request_options_to_span(config) + ) as span: + span.set_attributes(request_attributes) if helper.sem_conv_opt_in_mode == _StabilityMode.DEFAULT: - helper.process_request(contents, config) + helper.process_request(contents, config, span) try: response = await wrapped_func( self, @@ -893,23 +889,28 @@ async def instrumented_generate_content( config=helper.wrapped_config(config), **kwargs, ) - if helper.sem_conv_opt_in_mode == _StabilityMode.DEFAULT: - helper.process_response(response) - elif ( - helper.sem_conv_opt_in_mode - == _StabilityMode.GEN_AI_LATEST_EXPERIMENTAL - ): - helper.process_completion(contents, response, config) + if is_experimental_mode: + helper._update_response(response) + if response.candidates: + candidates += response.candidates else: - raise ValueError( - f"Sem Conv opt in mode {helper.sem_conv_opt_in_mode} not supported." - ) + helper.process_response(response) return response except Exception as error: helper.process_error(error) raise finally: - helper.finalize_processing() + final_attributes = helper.create_final_attributes() + span.set_attributes(final_attributes) + helper._maybe_log_completion_details( + request_attributes, + final_attributes, + contents, + candidates, + config, + ) + helper._record_token_usage_metric() + helper._record_duration_metric() return instrumented_generate_content @@ -939,14 +940,23 @@ async def instrumented_generate_content_stream( completion_hook, generate_content_config_key_allowlist=generate_content_config_key_allowlist, ) + is_experimental_mode = ( + helper.sem_conv_opt_in_mode + == _StabilityMode.GEN_AI_LATEST_EXPERIMENTAL + ) + request_attributes = _create_request_attributes( + config, + is_experimental_mode, + helper._generate_content_config_key_allowlist, + ) with helper.start_span_as_current_span( model, "google.genai.AsyncModels.generate_content_stream", end_on_exit=False, ) as span: - helper.add_request_options_to_span(config) - if helper.sem_conv_opt_in_mode == _StabilityMode.DEFAULT: - helper.process_request(contents, config) + span.set_attributes(request_attributes) + if not is_experimental_mode: + helper.process_request(contents, config, span) try: response_async_generator = await wrapped_func( self, @@ -957,7 +967,17 @@ async def instrumented_generate_content_stream( ) except Exception as error: # pylint: disable=broad-exception-caught helper.process_error(error) - helper.finalize_processing() + helper._record_token_usage_metric() + final_attributes = helper.create_final_attributes() + span.set_attributes(final_attributes) + helper._maybe_log_completion_details( + request_attributes, + final_attributes, + contents, + [], + config, + ) + helper._record_duration_metric() with trace.use_span(span, end_on_exit=True): raise @@ -966,31 +986,29 @@ async def _response_async_generator_wrapper(): with trace.use_span(span, end_on_exit=True): try: async for response in response_async_generator: - if ( - helper.sem_conv_opt_in_mode - == _StabilityMode.DEFAULT - ): - helper.process_response(response) - elif ( - helper.sem_conv_opt_in_mode - == _StabilityMode.GEN_AI_LATEST_EXPERIMENTAL - ): + if is_experimental_mode: helper._update_response(response) if response.candidates: candidates += response.candidates + else: - raise ValueError( - f"Sem Conv opt in mode {helper.sem_conv_opt_in_mode} not supported." - ) + helper.process_response(response) yield response except Exception as error: helper.process_error(error) raise finally: + final_attributes = helper.create_final_attributes() + span.set_attributes(final_attributes) helper._maybe_log_completion_details( - contents, candidates, config + request_attributes, + final_attributes, + contents, + candidates, + config, ) - helper.finalize_processing() + helper._record_token_usage_metric() + helper._record_duration_metric() return _response_async_generator_wrapper() @@ -1007,6 +1025,14 @@ def instrument_generate_content( completion_hook: CompletionHook, generate_content_config_key_allowlist: Optional[AllowList] = None, ) -> object: + opt_in_mode = _OpenTelemetrySemanticConventionStability._get_opentelemetry_stability_opt_in_mode( + _OpenTelemetryStabilitySignalType.GEN_AI + ) + if opt_in_mode not in ( + _StabilityMode.GEN_AI_LATEST_EXPERIMENTAL, + _StabilityMode.DEFAULT, + ): + raise ValueError(f"Sem Conv opt in mode {opt_in_mode} not supported.") snapshot = _MethodsSnapshot() Models.generate_content = _create_instrumented_generate_content( snapshot, diff --git a/instrumentation-genai/opentelemetry-instrumentation-google-genai/tests/common/base.py b/instrumentation-genai/opentelemetry-instrumentation-google-genai/tests/common/base.py index 2bb686e05..7ed9845cc 100644 --- a/instrumentation-genai/opentelemetry-instrumentation-google-genai/tests/common/base.py +++ b/instrumentation-genai/opentelemetry-instrumentation-google-genai/tests/common/base.py @@ -14,9 +14,14 @@ import os import unittest +from unittest.mock import patch import google.genai +from opentelemetry.instrumentation._semconv import ( + _OpenTelemetrySemanticConventionStability, +) + from .auth import FakeCredentials from .instrumentation_context import InstrumentationContext from .otel_mocker import OTelMocker @@ -24,6 +29,16 @@ class TestCase(unittest.TestCase): def setUp(self): + # Most tests want this environment variable setup. Need to figure out a less hacky way of doing this. + with patch.dict( + "os.environ", + { + "OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT": "true", + "OTEL_SEMCONV_STABILITY_OPT_IN": "default", + }, + ): + _OpenTelemetrySemanticConventionStability._initialized = False + _OpenTelemetrySemanticConventionStability._initialize() self._otel = OTelMocker() self._otel.install() self._instrumentation_context = None diff --git a/instrumentation-genai/opentelemetry-instrumentation-google-genai/tests/common/otel_mocker.py b/instrumentation-genai/opentelemetry-instrumentation-google-genai/tests/common/otel_mocker.py index d3ef52a9c..d5d1e6d17 100644 --- a/instrumentation-genai/opentelemetry-instrumentation-google-genai/tests/common/otel_mocker.py +++ b/instrumentation-genai/opentelemetry-instrumentation-google-genai/tests/common/otel_mocker.py @@ -22,10 +22,21 @@ ) from opentelemetry.metrics import get_meter_provider, set_meter_provider from opentelemetry.sdk._logs import LoggerProvider -from opentelemetry.sdk._logs.export import ( - InMemoryLogExporter, - SimpleLogRecordProcessor, -) + +# Backward compatibility for InMemoryLogExporter -> InMemoryLogRecordExporter rename +try: + from opentelemetry.sdk._logs.export import ( # pylint: disable=no-name-in-module + InMemoryLogRecordExporter, + SimpleLogRecordProcessor, + ) +except ImportError: + # Fallback to old name for compatibility with older SDK versions + from opentelemetry.sdk._logs.export import ( + InMemoryLogExporter as InMemoryLogRecordExporter, + ) + from opentelemetry.sdk._logs.export import ( + SimpleLogRecordProcessor, + ) from opentelemetry.sdk.metrics import MeterProvider from opentelemetry.sdk.metrics._internal.export import InMemoryMetricReader from opentelemetry.sdk.trace import TracerProvider @@ -114,7 +125,7 @@ def data(self): class OTelMocker: def __init__(self): self._snapshot = None - self._logs = InMemoryLogExporter() + self._logs = InMemoryLogRecordExporter() self._traces = InMemorySpanExporter() self._metrics = InMemoryMetricReader() self._spans = [] @@ -164,9 +175,9 @@ def get_span_named(self, name): def assert_has_span_named(self, name): span = self.get_span_named(name) finished_spans = [span.name for span in self.get_finished_spans()] - assert ( - span is not None - ), f'Could not find span named "{name}"; finished spans: {finished_spans}' + assert span is not None, ( + f'Could not find span named "{name}"; finished spans: {finished_spans}' + ) def assert_does_not_have_span_named(self, name): span = self.get_span_named(name) @@ -192,9 +203,9 @@ def get_events_named(self, event_name): def assert_has_event_named(self, name): event = self.get_event_named(name) finished_logs = self.get_finished_logs() - assert ( - event is not None - ), f'Could not find event named "{name}"; finished logs: {finished_logs}' + assert event is not None, ( + f'Could not find event named "{name}"; finished logs: {finished_logs}' + ) def assert_does_not_have_event_named(self, name): event = self.get_event_named(name) diff --git a/instrumentation-genai/opentelemetry-instrumentation-google-genai/tests/generate_content/base.py b/instrumentation-genai/opentelemetry-instrumentation-google-genai/tests/generate_content/base.py index 59f08a5e4..ebaeeb874 100644 --- a/instrumentation-genai/opentelemetry-instrumentation-google-genai/tests/generate_content/base.py +++ b/instrumentation-genai/opentelemetry-instrumentation-google-genai/tests/generate_content/base.py @@ -94,16 +94,19 @@ def configure_valid_response(self, **kwargs): response = create_response(**kwargs) self._responses.append(response) - def _create_and_install_mocks(self): + def configure_exception(self, e, **kwargs): + self._create_and_install_mocks(e) + + def _create_and_install_mocks(self, e=None): if self._generate_content_mock is not None: return self.reset_client() self.reset_instrumentation() - self._generate_content_mock = self._create_nonstream_mock() - self._generate_content_stream_mock = self._create_stream_mock() + self._generate_content_mock = self._create_nonstream_mock(e) + self._generate_content_stream_mock = self._create_stream_mock(e) self._install_mocks() - def _create_nonstream_mock(self): + def _create_nonstream_mock(self, e=None): mock = unittest.mock.MagicMock() def _default_impl(*args, **kwargs): @@ -114,17 +117,20 @@ def _default_impl(*args, **kwargs): self._response_index += 1 return result - mock.side_effect = _default_impl + mock.side_effect = e or _default_impl return mock - def _create_stream_mock(self): + def _create_stream_mock(self, e=None): mock = unittest.mock.MagicMock() def _default_impl(*args, **kwargs): for response in self._responses: yield response - mock.side_effect = _default_impl + if not e: + mock.side_effect = _default_impl + else: + mock.side_effect = e return mock def _install_mocks(self): diff --git a/instrumentation-genai/opentelemetry-instrumentation-google-genai/tests/generate_content/nonstreaming_base.py b/instrumentation-genai/opentelemetry-instrumentation-google-genai/tests/generate_content/nonstreaming_base.py index 4ae6f0006..897a8716b 100644 --- a/instrumentation-genai/opentelemetry-instrumentation-google-genai/tests/generate_content/nonstreaming_base.py +++ b/instrumentation-genai/opentelemetry-instrumentation-google-genai/tests/generate_content/nonstreaming_base.py @@ -16,9 +16,10 @@ import unittest from unittest.mock import patch +import pytest from google.genai.types import GenerateContentConfig +from pydantic import BaseModel, Field -from opentelemetry._events import Event from opentelemetry.instrumentation._semconv import ( _OpenTelemetrySemanticConventionStability, _OpenTelemetryStabilitySignalType, @@ -31,6 +32,12 @@ from .base import TestCase +# pylint: disable=too-many-public-methods + + +class ExampleResponseSchema(BaseModel): + name: str = Field(description="A Destination's Name") + class NonStreamingTestCase(TestCase): # The "setUp" function is defined by "unittest.TestCase" and thus @@ -92,6 +99,40 @@ def test_generated_span_has_minimal_genai_attributes(self): span.attributes["gen_ai.operation.name"], "generate_content" ) + def test_span_and_event_still_written_when_response_is_exception(self): + self.configure_exception(ValueError("Uh oh!")) + patched_environ = patch.dict( + "os.environ", + { + "OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT": "SPAN_AND_EVENT", + "OTEL_SEMCONV_STABILITY_OPT_IN": "gen_ai_latest_experimental", + }, + ) + with patched_environ: + _OpenTelemetrySemanticConventionStability._initialized = False + _OpenTelemetrySemanticConventionStability._initialize() + with pytest.raises(ValueError): + self.generate_content( + model="gemini-2.0-flash", contents="Does this work?" + ) + self.otel.assert_has_span_named( + "generate_content gemini-2.0-flash" + ) + span = self.otel.get_span_named( + "generate_content gemini-2.0-flash" + ) + self.otel.assert_has_event_named( + "gen_ai.client.inference.operation.details" + ) + event = self.otel.get_event_named( + "gen_ai.client.inference.operation.details" + ) + assert ( + span.attributes["error.type"] + == event.attributes["error.type"] + == "ValueError" + ) + def test_generated_span_has_correct_function_name(self): self.configure_valid_response(text="Yep, it works!") self.generate_content( @@ -215,6 +256,12 @@ def test_does_not_record_response_as_log_if_disabled_by_env(self): self.assertEqual(event_record.attributes["gen_ai.system"], "gemini") self.assertEqual(event_record.body["content"], "") + @patch.dict( + "os.environ", + { + "OTEL_GOOGLE_GENAI_GENERATE_CONTENT_CONFIG_INCLUDES": "gcp.gen_ai.operation.config.response_schema" + }, + ) def test_new_semconv_record_completion_as_log(self): for mode in ContentCapturingMode: patched_environ = patch.dict( @@ -243,7 +290,8 @@ def test_new_semconv_record_completion_as_log(self): model="gemini-2.0-flash", contents=content, config=GenerateContentConfig( - system_instruction=sys_instr + system_instruction=sys_instr, + response_schema=ExampleResponseSchema, ), ) self.otel.assert_has_event_named( @@ -252,6 +300,12 @@ def test_new_semconv_record_completion_as_log(self): event = self.otel.get_event_named( "gen_ai.client.inference.operation.details" ) + assert ( + event.attributes[ + "gcp.gen_ai.operation.config.response_schema" + ] + == "" + ) if mode in [ ContentCapturingMode.NO_CONTENT, ContentCapturingMode.SPAN_ONLY, @@ -269,7 +323,7 @@ def test_new_semconv_record_completion_as_log(self): event.attributes, ) else: - attrs = { + expected_event_attributes = { gen_ai_attributes.GEN_AI_INPUT_MESSAGES: ( { "role": "user", @@ -291,15 +345,11 @@ def test_new_semconv_record_completion_as_log(self): {"content": sys_instr, "type": "text"}, ), } - expected_event = Event( - "gen_ai.client.inference.operation.details", - attributes=attrs, - ) self.assertEqual( event.attributes[ gen_ai_attributes.GEN_AI_INPUT_MESSAGES ], - expected_event.attributes[ + expected_event_attributes[ gen_ai_attributes.GEN_AI_INPUT_MESSAGES ], ) @@ -307,7 +357,7 @@ def test_new_semconv_record_completion_as_log(self): event.attributes[ gen_ai_attributes.GEN_AI_OUTPUT_MESSAGES ], - expected_event.attributes[ + expected_event_attributes[ gen_ai_attributes.GEN_AI_OUTPUT_MESSAGES ], ) @@ -315,7 +365,7 @@ def test_new_semconv_record_completion_as_log(self): event.attributes[ gen_ai_attributes.GEN_AI_SYSTEM_INSTRUCTIONS ], - expected_event.attributes[ + expected_event_attributes[ gen_ai_attributes.GEN_AI_SYSTEM_INSTRUCTIONS ], ) @@ -346,7 +396,8 @@ def test_new_semconv_record_completion_in_span(self): model="gemini-2.0-flash", contents="Some input", config=GenerateContentConfig( - system_instruction="System instruction" + system_instruction="System instruction", + response_schema=ExampleResponseSchema, ), ) span = self.otel.get_span_named( diff --git a/instrumentation-genai/opentelemetry-instrumentation-google-genai/tests/utils/test_dict_util.py b/instrumentation-genai/opentelemetry-instrumentation-google-genai/tests/utils/test_dict_util.py index ef2e64136..7652b7a39 100644 --- a/instrumentation-genai/opentelemetry-instrumentation-google-genai/tests/utils/test_dict_util.py +++ b/instrumentation-genai/opentelemetry-instrumentation-google-genai/tests/utils/test_dict_util.py @@ -172,6 +172,9 @@ def test_flatten_with_pydantic_model_value(): "foo.str_value": "bar", "foo.int_value": 123, } + assert dict_util.flatten_dict({"foo": PydanticModel}) == { + "foo": "" + } def test_flatten_with_model_dumpable_value(): diff --git a/instrumentation-genai/opentelemetry-instrumentation-google-genai/tests/utils/test_tool_call_wrapper.py b/instrumentation-genai/opentelemetry-instrumentation-google-genai/tests/utils/test_tool_call_wrapper.py index af5dcef29..fb94a426d 100644 --- a/instrumentation-genai/opentelemetry-instrumentation-google-genai/tests/utils/test_tool_call_wrapper.py +++ b/instrumentation-genai/opentelemetry-instrumentation-google-genai/tests/utils/test_tool_call_wrapper.py @@ -13,6 +13,7 @@ # limitations under the License. import asyncio +import os import unittest from unittest.mock import patch @@ -21,8 +22,6 @@ from opentelemetry._logs import get_logger_provider from opentelemetry.instrumentation._semconv import ( _OpenTelemetrySemanticConventionStability, - _OpenTelemetryStabilitySignalType, - _StabilityMode, ) from opentelemetry.instrumentation.google_genai import ( otel_wrapper, @@ -44,6 +43,12 @@ def setUp(self): get_logger_provider(), get_meter_provider(), ) + os.environ["OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT"] = ( + "true" + ) + os.environ["OTEL_SEMCONV_STABILITY_OPT_IN"] = "default" + _OpenTelemetrySemanticConventionStability._initialized = False + _OpenTelemetrySemanticConventionStability._initialize() @property def otel(self): @@ -169,10 +174,6 @@ def somefunction(): "An example tool call function.", ) - @patch.dict( - "os.environ", - {"OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT": "true"}, - ) def test_handles_primitive_int_arg(self): def somefunction(arg=None): pass @@ -191,10 +192,6 @@ def somefunction(arg=None): span.attributes["code.function.parameters.arg.value"], 12345 ) - @patch.dict( - "os.environ", - {"OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT": "true"}, - ) def test_handles_primitive_string_arg(self): def somefunction(arg=None): pass @@ -214,10 +211,6 @@ def somefunction(arg=None): "a string value", ) - @patch.dict( - "os.environ", - {"OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT": "true"}, - ) def test_handles_dict_arg(self): def somefunction(arg=None): pass @@ -237,10 +230,6 @@ def somefunction(arg=None): '{"key": "value"}', ) - @patch.dict( - "os.environ", - {"OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT": "true"}, - ) def test_handles_primitive_list_arg(self): def somefunction(arg=None): pass @@ -262,10 +251,6 @@ def somefunction(arg=None): [1, 2, 3], ) - @patch.dict( - "os.environ", - {"OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT": "true"}, - ) def test_handles_heterogenous_list_arg(self): def somefunction(arg=None): pass @@ -290,24 +275,19 @@ def somefunction(arg=None): pass for mode in ContentCapturingMode: - patched_environ = patch.dict( - "os.environ", - { - "OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT": mode.name, - "OTEL_SEMCONV_STABILITY_OPT_IN": "gen_ai_latest_experimental", - }, - ) - patched_otel_mapping = patch.dict( - _OpenTelemetrySemanticConventionStability._OTEL_SEMCONV_STABILITY_SIGNAL_MAPPING, - { - _OpenTelemetryStabilitySignalType.GEN_AI: _StabilityMode.GEN_AI_LATEST_EXPERIMENTAL - }, - ) - with self.subTest( - f"mode: {mode}", patched_environ=patched_environ - ): + with self.subTest(f"mode: {mode}"): self.setUp() - with patched_environ, patched_otel_mapping: + with patch.dict( + "os.environ", + { + "OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT": mode.name, + "OTEL_SEMCONV_STABILITY_OPT_IN": "gen_ai_latest_experimental", + }, + ): + _OpenTelemetrySemanticConventionStability._initialized = ( + False + ) + _OpenTelemetrySemanticConventionStability._initialize() wrapped_somefunction = self.wrap(somefunction) wrapped_somefunction(12345) @@ -328,4 +308,4 @@ def somefunction(arg=None): "code.function.parameters.arg.value", span.attributes, ) - self.tearDown() + self.tearDown() diff --git a/instrumentation-genai/opentelemetry-instrumentation-openai-v2/CHANGELOG.md b/instrumentation-genai/opentelemetry-instrumentation-openai-v2/CHANGELOG.md index 9da677027..1329cee83 100644 --- a/instrumentation-genai/opentelemetry-instrumentation-openai-v2/CHANGELOG.md +++ b/instrumentation-genai/opentelemetry-instrumentation-openai-v2/CHANGELOG.md @@ -7,10 +7,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased +## Version 2.2b0 (2025-11-25) + +- Fix service tier attribute names: use `GEN_AI_OPENAI_REQUEST_SERVICE_TIER` for request + attributes and `GEN_AI_OPENAI_RESPONSE_SERVICE_TIER` for response attributes. + ([#3920](https://github.com/open-telemetry/opentelemetry-python-contrib/issues/3920)) - Added support for OpenAI embeddings instrumentation ([#3461](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3461)) - Record prompt and completion events regardless of span sampling decision. ([#3226](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3226)) +- Filter out attributes with the value of NotGiven instances + ([#3760](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3760)) - Migrate off the deprecated events API to use the logs API ([#3625](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3628)) @@ -34,4 +41,4 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ([#2925](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2925)) - Initial OpenAI instrumentation - ([#2759](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2759)) \ No newline at end of file + ([#2759](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2759)) diff --git a/instrumentation-genai/opentelemetry-instrumentation-openai-v2/src/opentelemetry/instrumentation/openai_v2/__init__.py b/instrumentation-genai/opentelemetry-instrumentation-openai-v2/src/opentelemetry/instrumentation/openai_v2/__init__.py index f82226525..9e4a24855 100644 --- a/instrumentation-genai/opentelemetry-instrumentation-openai-v2/src/opentelemetry/instrumentation/openai_v2/__init__.py +++ b/instrumentation-genai/opentelemetry-instrumentation-openai-v2/src/opentelemetry/instrumentation/openai_v2/__init__.py @@ -129,7 +129,7 @@ def _instrument(self, **kwargs): ) def _uninstrument(self, **kwargs): - import openai # pylint: disable=import-outside-toplevel + import openai # pylint: disable=import-outside-toplevel # noqa: PLC0415 unwrap(openai.resources.chat.completions.Completions, "create") unwrap(openai.resources.chat.completions.AsyncCompletions, "create") diff --git a/instrumentation-genai/opentelemetry-instrumentation-openai-v2/src/opentelemetry/instrumentation/openai_v2/patch.py b/instrumentation-genai/opentelemetry-instrumentation-openai-v2/src/opentelemetry/instrumentation/openai_v2/patch.py index 4d4b4871a..b51a8aa83 100644 --- a/instrumentation-genai/opentelemetry-instrumentation-openai-v2/src/opentelemetry/instrumentation/openai_v2/patch.py +++ b/instrumentation-genai/opentelemetry-instrumentation-openai-v2/src/opentelemetry/instrumentation/openai_v2/patch.py @@ -370,7 +370,7 @@ def _set_response_attributes( if getattr(result, "service_tier", None): set_span_attribute( span, - GenAIAttributes.GEN_AI_OPENAI_REQUEST_SERVICE_TIER, + GenAIAttributes.GEN_AI_OPENAI_RESPONSE_SERVICE_TIER, result.service_tier, ) diff --git a/instrumentation-genai/opentelemetry-instrumentation-openai-v2/src/opentelemetry/instrumentation/openai_v2/utils.py b/instrumentation-genai/opentelemetry-instrumentation-openai-v2/src/opentelemetry/instrumentation/openai_v2/utils.py index 739e6e920..2b723fbad 100644 --- a/instrumentation-genai/opentelemetry-instrumentation-openai-v2/src/opentelemetry/instrumentation/openai_v2/utils.py +++ b/instrumentation-genai/opentelemetry-instrumentation-openai-v2/src/opentelemetry/instrumentation/openai_v2/utils.py @@ -12,12 +12,14 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + from os import environ -from typing import Mapping, Optional, Union +from typing import Mapping from urllib.parse import urlparse from httpx import URL -from openai import NOT_GIVEN +from openai import NotGiven from opentelemetry._logs import LogRecord from opentelemetry.semconv._incubating.attributes import ( @@ -179,8 +181,12 @@ def is_streaming(kwargs): return non_numerical_value_is_set(kwargs.get("stream")) -def non_numerical_value_is_set(value: Optional[Union[bool, str]]): - return bool(value) and value != NOT_GIVEN +def non_numerical_value_is_set(value: bool | str | NotGiven | None): + return bool(value) and value_is_set(value) + + +def value_is_set(value): + return value is not None and not isinstance(value, NotGiven) def get_llm_request_attributes( @@ -230,8 +236,13 @@ def get_llm_request_attributes( GenAIAttributes.GEN_AI_OPENAI_REQUEST_RESPONSE_FORMAT ] = response_format + # service_tier can be passed directly or in extra_body (in SDK 1.26.0 it's via extra_body) service_tier = kwargs.get("service_tier") - attributes[GenAIAttributes.GEN_AI_OPENAI_RESPONSE_SERVICE_TIER] = ( + if service_tier is None: + extra_body = kwargs.get("extra_body") + if isinstance(extra_body, Mapping): + service_tier = extra_body.get("service_tier") + attributes[GenAIAttributes.GEN_AI_OPENAI_REQUEST_SERVICE_TIER] = ( service_tier if service_tier != "auto" else None ) @@ -252,8 +263,8 @@ def get_llm_request_attributes( set_server_address_and_port(client_instance, attributes) - # filter out None values - return {k: v for k, v in attributes.items() if v is not None} + # filter out values not set + return {k: v for k, v in attributes.items() if value_is_set(v)} def handle_span_exception(span, error): diff --git a/instrumentation-genai/opentelemetry-instrumentation-openai-v2/src/opentelemetry/instrumentation/openai_v2/version.py b/instrumentation-genai/opentelemetry-instrumentation-openai-v2/src/opentelemetry/instrumentation/openai_v2/version.py index a6098e22c..1cfac0a2c 100644 --- a/instrumentation-genai/opentelemetry-instrumentation-openai-v2/src/opentelemetry/instrumentation/openai_v2/version.py +++ b/instrumentation-genai/opentelemetry-instrumentation-openai-v2/src/opentelemetry/instrumentation/openai_v2/version.py @@ -12,4 +12,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -__version__ = "2.2b0.dev" +__version__ = "2.3b0.dev" diff --git a/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/README.md b/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/README.md new file mode 100644 index 000000000..5ccd7146d --- /dev/null +++ b/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/README.md @@ -0,0 +1,10 @@ +## Recording calls + +If you need to record calls you need to export the `OPENAI_API_KEY` as environment variable. +Since tox blocks environment variables by default you need to override its configuration to let them pass: + +``` +export TOX_OVERRIDE=testenv.pass_env=OPENAI_API_KEY +``` + +We are not adding it to tox.ini because of security concerns. diff --git a/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/cassettes/test_chat_completion_handles_not_given.yaml b/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/cassettes/test_chat_completion_handles_not_given.yaml new file mode 100644 index 000000000..38199fadd --- /dev/null +++ b/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/cassettes/test_chat_completion_handles_not_given.yaml @@ -0,0 +1,144 @@ +interactions: +- request: + body: |- + { + "messages": [ + { + "role": "user", + "content": "Say this is a test" + } + ], + "model": "gpt-4o-mini", + "stream": false + } + headers: + accept: + - application/json + accept-encoding: + - gzip, deflate + authorization: + - Bearer test_openai_api_key + connection: + - keep-alive + content-length: + - '106' + content-type: + - application/json + host: + - api.openai.com + user-agent: + - OpenAI/Python 1.109.1 + x-stainless-arch: + - x64 + x-stainless-async: + - 'false' + x-stainless-lang: + - python + x-stainless-os: + - Linux + x-stainless-package-version: + - 1.109.1 + x-stainless-read-timeout: + - '600' + x-stainless-retry-count: + - '0' + x-stainless-runtime: + - CPython + x-stainless-runtime-version: + - 3.10.12 + method: POST + uri: https://api.openai.com/v1/chat/completions + response: + body: + string: |- + { + "id": "chatcmpl-CZDvsSHdMnAgYuQ8J81NMgOK2wfam", + "object": "chat.completion", + "created": 1762511072, + "model": "gpt-4o-mini-2024-07-18", + "choices": [ + { + "index": 0, + "message": { + "role": "assistant", + "content": "This is a test. How can I assist you today?", + "refusal": null, + "annotations": [] + }, + "logprobs": null, + "finish_reason": "stop" + } + ], + "usage": { + "prompt_tokens": 12, + "completion_tokens": 12, + "total_tokens": 24, + "prompt_tokens_details": { + "cached_tokens": 0, + "audio_tokens": 0 + }, + "completion_tokens_details": { + "reasoning_tokens": 0, + "audio_tokens": 0, + "accepted_prediction_tokens": 0, + "rejected_prediction_tokens": 0 + } + }, + "service_tier": "default", + "system_fingerprint": "fp_560af6e559" + } + headers: + CF-RAY: + - 99ac1f128834ed5e-MXP + Connection: + - keep-alive + Content-Type: + - application/json + Date: + - Fri, 07 Nov 2025 10:24:32 GMT + Server: + - cloudflare + Set-Cookie: test_set_cookie + Strict-Transport-Security: + - max-age=31536000; includeSubDomains; preload + Transfer-Encoding: + - chunked + X-Content-Type-Options: + - nosniff + access-control-expose-headers: + - X-Request-ID + alt-svc: + - h3=":443"; ma=86400 + cf-cache-status: + - DYNAMIC + content-length: + - '850' + openai-organization: test_openai_org_id + openai-processing-ms: + - '512' + openai-project: + - proj_Pf1eM5R55Z35wBy4rt8PxAGq + openai-version: + - '2020-10-01' + x-envoy-upstream-service-time: + - '797' + x-openai-proxy-wasm: + - v0.1 + x-ratelimit-limit-requests: + - '10000' + x-ratelimit-limit-tokens: + - '10000000' + x-ratelimit-remaining-requests: + - '9999' + x-ratelimit-remaining-tokens: + - '9999993' + x-ratelimit-reset-requests: + - 6ms + x-ratelimit-reset-tokens: + - 0s + x-request-id: + - req_9eac1833161f4ac89019c12f24002ef4 + status: + code: 200 + message: OK +version: 1 diff --git a/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/cassettes/test_embeddings_with_not_given_values[not_given_value0].yaml b/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/cassettes/test_embeddings_with_not_given_values[not_given_value0].yaml new file mode 100644 index 000000000..2aeb81b9c --- /dev/null +++ b/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/cassettes/test_embeddings_with_not_given_values[not_given_value0].yaml @@ -0,0 +1,124 @@ +interactions: +- request: + body: |- + { + "input": "This is a test for embeddings with encoding format", + "model": "text-embedding-3-small", + "encoding_format": "base64" + } + headers: + accept: + - application/json + accept-encoding: + - gzip, deflate + authorization: + - Bearer test_openai_api_key + connection: + - keep-alive + content-length: + - '127' + content-type: + - application/json + host: + - api.openai.com + user-agent: + - OpenAI/Python 1.109.1 + x-stainless-arch: + - x64 + x-stainless-async: + - 'false' + x-stainless-lang: + - python + x-stainless-os: + - Linux + x-stainless-package-version: + - 1.109.1 + x-stainless-read-timeout: + - '600' + x-stainless-retry-count: + - '0' + x-stainless-runtime: + - CPython + x-stainless-runtime-version: + - 3.10.12 + method: POST + uri: https://api.openai.com/v1/embeddings + response: + body: + string: |- + { + "object": "list", + "data": [ + { + "object": "embedding", + "index": 0, + "embedding": "DK6fO8qEirxr7OE8SQSPvXpWIry659e8mJ5BPUKZiTxZdqM76WvmPDaVLT0xwge86TEXvU+lObzFhka8XkUfvNgvd7yb1d08OC0NuzMvyTshVB07MPgsvc7umDzYWhU9SjeBPBQdz7wB2Yu8gYuCPbZ9ST1078a8c4rZvDwyrrxnsRs8BRj8vNv2nrz3bA88HFZZvMXnCT2xSeC8Pp/vOuVimzyN/1K8kc5OvXAk9bvH6AA8UgsePWdMrrxvgQ69+Z8BvEE0HD2w4Ei9FbEEPUY9Zzyd0qq7ZbROvA6Azjx0Ho+8vbKpPITGyLsC3bW7mAMvPTXHKL3QIYu6mNRmvNYnozx078a6l295vQyuHz1frra6bYhrvP/bPj1O17Q8KCSQvJKcUzzXjBA9m9XduwBFVrwJSDs7RTk9vSS6gb2m2Tm8UXfoPL+6/bxyggW9mnDwOxBS/Tx6u4+94JMfvIIqv7xvgQ69/d7xvH1XGb08lxu9H7y9vBvtwTtOdnG8G1IvvJU4Xb06ZCm7+NUmPJg5VLzP8kI8y4i0u35fbbta2xC6arKSvGUVkjwU56m7Y0eNvT7ON7xc3Ae9C39Xu5drT7w4yJ88kmKEPVtEKLwLf1c86ZYEvdP3Y7ujDug7jf/SvHuJFLxWED89LyqovB2FIb1i6fy8IbkKulupFb0O5bu8UXdovOqaLrzeXIO8XNwHPRAYLj36o6u9gYsCvQjfo72zRi083S07vbmCaj1KbaY6oWsBvfaeCjz9Q1+8PgTdvKtHcjwVUMG9qqQLPQhEkTwW7308dLkhvAJ88jtoUFi9v+UbPGIUG73BHDi9H/LivEI4xrypEFY9MWHEu2N9MjmyQgM9NQF4PDsuhDrC6jy7kwFBvBfoID0tLds6o9QYvRVMF7x3VSu8PcqNvPM4przCu/S5kmIEvfVvwjqrqLW892yPvZJiBDvwAQq9mDnUvBAYLj3/2z68HBwKPCMmzLyLMU49mNRmPCGKwjycaRM9wuo8uwJCo7ypENY8r9yePQbiVjvgyUQ8cOolvXIhQj0B2Qu9CRIWPbkd/bzvCGe8tBAIPYTGyLxgsuA8Bd6sPFrbEL1BNBy9RzYKvdPBvrxkSze8e172PKPUmLzAGA48OMgfO3ZRgTv3ByI9fiUevYWQI7xS3FU8izHOvIbDlbyxSeA8nAhQPZg51DyuDpq9KvIUvAt/17zHuTi93sx3PUc2iryjOQa77GzdvNHvjztJo0s8HByKPE3TCrzGVEs7xFf+uyW+q7zT9+M8c+scvVDUAbqHYlI9nAhQu2AXTrx+X+08aRqzO/k+Pr31CtU8dO9GOuEy3DyS/ZY8W6kVvP48Ar1MP9W8c1AKve5pqrymqnE9YnmIPO02uDc6ms66l9C8PAKnkLya0bO6ptWPPFUMlbxMBYY8z1ewO0ptJrqD+EO9c8B+O0nZ8LzellI8L4+VPFWnJ70zL0k9oaGmvCpXgrz0axi7KvKUPK6pLL0xwoe9DuW7O43JLT3C5pK89j3HN052cbxbDgO8saojvfY5HTuoRnu7UXdovQJCIz3uaao8JLoBvbkdfTwTtDc80/fjO7GqIz3rZIm8XHcaPUQ1kzvLUg89FB1PPSj1R71rh/Q8ljGAvSW+Kz1XeVY9GVXiujsDZryx5HI8n2oKu51x5zw4Z1y9MPgsvDD4rDyrR3K8njtCPNBb2rpu7Vi8AnzyOeH8Nj1SQUO9kv2WPH28Bj0Ae/u8XqqMPPrdertGA5g8cyXsvPXULz20Slc9RTm9u6Q9sLwyKx+8RDUTvQwTjTyvd7E7qj+evOkG+bwQs8C8ogo+PWvs4TzIhz29TnJHPeaVjTvb9h49rq3WPBVMl7x7iZS7OfsRvGrot7xPQMw8/QmQPW+BDr0PFIQ8sniovEmjy7yqpAu9OAJvPKaq8bu24ja8Q6HdPIstpDx2wfW8DK4fvf1DXzw3/sQ8tXkfvI4yxbzChc872L+CvJ0MejsNfKS8eIgdOkOdMzmOkwg9IbkKPbewOz1WEL88M5Q2vYbDFb371h29BagHPA4b4Twbt5w8AQ8xPKcIgrxWdSw5h5h3PIpjSbq85KS86ALPvEai1DwvYE09SaPLPIJZB72xD5G6ua2IvGJOarxREnu9jpOIPN3IzbyhBhS7Qc8uPWVPYT2V/g086WvmPBGBRbxmfqm8Hx2BvO1lAD3S87m862SJPAp7LTveMWW56WvmvJloHDyuSOk8V3lWvFupFb3q/5u9WrDyO7lIm7x6VqK8CBlzPQsaarzP8kI7gYuCvRkbE71+igu8aOtqu1cU6TsVTJe8EeYyvfOdEz1iTuo8OMgfvDyXm7vTIgI8wksAPcK79LyhPDm9nXFnvJPLG72SnNO7xecJPQB7+zr3QfG8o3NVvKmraL0A4Oi8fI0+vYKPrDzwAQq9ecJsvLmCajy3FSk9KvKUvC33tTuhBpS91CYsvY4yxTzFvGs9e4mUvOJhJL02lS28EFL9PIHBJ7xhSsC8V3lWPSSPY7uP/B88b4GOPDP5o7yyQoM8oDgPvJdrz7zT9+O87mmqu+T5A73tZQC9gfv2uuZq77xzUAo+VnWsvE48Ir0yKx89HriTu6M5Br3vMwW8H1dQvWCyYLwVTBc8x1j1vJPLm7yBi4K8ZuMWPZ0M+ryMmmW8+zuLPFfewzxOoY8842n4PFQ+kDxAZhe9MV2avAcVybyY1OY7jJrlPIdi0rwB2Qu9iv5bPD83zzt/xNo7yoQKvSosZD1g4Si9DhvhvMa1jjy5fsC8zb9QvFZ1rLxFOb07ExmlPNVZnrzA6cW9CH7gvJeal7wYUbi8jf9SvGgWCT3YlGQ7aIZ9u6zbJ7zZ+dG7wYGlPDJlbjyOMkW8ukzFPO8IZ7wCfPI8MC5SvCAhq7t1vcs84saRPB3qjrnWwrW8CnutPPY5nTpjfbK8elaiPPgLTL3QvJ08FB3PPK2lAj1GPWe8O8kWvePKOztiTmo8c1CKPGKzVz3dkig9wOlFvTpkKT2mD986TAkwvIrINj0W7/28eyQnvYT87brnmTc8mnBwO4GLAr371h08KixkvKVwojyMmuW8A+HfO9sw7rtBNBw8xFd+PDb6GjxREvu86WvmOwbi1jw+BF28tn1JvHBPkzlOcsc80/djPNSLmbtmGTy9brezOw+vFjw+BN08CneDvNhaFb3mlQ09NGK7PDf+xDyKme68HFbZPLUUMjw3YzI9mDnUvC33NT2oRvs8O574uiGO7Lsjhw+8723UvPaeirsCeMg801zRuxyM/jvdkig7EkugPBbv/bugct47D6+WvGJO6rsGER88C39XO/pC6Dybnzg7VUK6O/M4pjwlIxk8CH5gvCGKwjsKdwM9SQQPvf1D37yH/eS7toHzvJ4FHbysrN88KcNMvSMmzLv3QfG42fnRPLQQiDxInyG7+AvMvBKwjTtyIUK8pabHu+DJRLyCj6w88qTwvOT5AzyF9ZC8bekuPHZRAb16u4+8wksAvKBy3rjHgxO9TAWGvKOp+jmuSGk6Ov+7vO02uDu5SJs8l5qXvZxpEzzqNcG8CnstPKM5hrwMSTK7zb9QPKHXS71GotQ8jJY7vexs3bxDZ447wksAvJX+Dby5HX28JPAmPEZohbwX6KC8RZqAPKaq8Tv/QCw9nZyFvC/FOruLLaS70e8Pvd3IzbwI36M8+t36uzZfiDzEHS89RAbLPPMCATymD187ak0lOyYnQzt3HwY9gSYVu64OGr3ezPe5PQAzvdkoGrzezPc8YrPXPH6KC7yWMYA7850TvP+lmTySYoQ8ZRWSPOtos7zn/qQ8Rj1nu7mtCLxm4xY8fI2+vFGiBrzO7pi776N5OmgWibyV02+8BRh8unwoUTv3ojS9st2VvFILHjydnIU8lzWqu1faGby651c8Oy4EPNJYJ7wO5Tu76wPGvC8qKLylcKK6o9hCO/nZUDwRHFg83vcVvaejFD0GER89JIs5vHmMRzwXg7O8gWBkvIBYEDzkM1O8tErXu4JZh7y3sLu8PzMlPePKO7y85KQ6H431PBNT9DuEJww9KFq1OfM8UDxfrja7c4rZO0TQpbwFfem7H/JiPKYP3zxxU707qEb7u1d51rt8KFE8FlRrPaBy3rzvo/m8gFiQPLewO73k+QM9K1ssPFytPzxQ1AE8wrv0vNLzObyRzk48NstSvBdNjjyUNDO8vB50vc2FAb01x6g79wciPe0Ak7vVWR48CnstvCos5LwJrai99weivIuSkTxcTPy80YoiPMlRmDy0gPy7Ca0oPNVZnrxasPI89NCFO1JBw7w7A+a4ntbUvHhdfzzRiqK8d1UrPbdLzrxUE/K8rq1WPMGBJbyNLhs85ceIPSfxnTye1lS9axuquVES+7uuc4e8QTQcvGNHDTuzq5q8F02OvCG5irwD4V88hl6oPOH8trycaRO8kv2WO4L0GT1/xFq6QwKhPGJO6rwJ4828GfB0vFN0NT01LJa7c8D+O4zFA7wCp5A83CkRvW2IazrkM1M5o6n6uI1kwLy5gmo83S07Pdn5UTy5Hf27WRE2vGsXAD3kmMC88qTwPA9KqTtUPpC7MmVuPA6287sl9FC8/UPfPBO0t7x+igu9mnBwPKULNbvuaSo9q0fyO2fnQL2ZzYm8p6MUPWnkDT3Zw6w6Z+dAPbBFNj0ovyI9rHa6uhYanDz3pl68x4OTO5X+jbxJo0u9v0oJvdkomrtAO3k8MWHEvIQnjLwuXKM8H411uBmAgLxOdnE8njtCveGXybo1kYO8yh8dPSOHjzz93nG8OGfcvKgMLL1n50C8RmgFPC2SyDzellK6dFS0OhJPyjsZVWI8MWHEO5pwcLxmfim9/QkQvEo3AbzEghw9G+1BPHcfBrtDPHC8BNqCPdbCtTxREvu88aBGvXAkdTqYA687v+UbOuGXyby8SZI8URJ7vAEPMT3szSC89p6KvQPh3zyWZ6U8uHqWu9z6SDy05Wm9IvPZu/UKVTzdyM28pnTMvHMl7LvH6IC8zfV1PLOrGr2/H+s8sniou5X+Db25fkC8dYemPGIUm7wTtDe8LfOLPAkSlrzeXIO7v0oJPOtoszwtksg80r0UOgp7LTyabMa87NFKvAsa6jxBoGa8IIaYvB8dAT1aFeC8tID8uxhROL2pq2g8wuq8O1gNDLtOdvE8jPuoPBSCvDuSYoQ8elaiPF9N8zrAsyA9qavoPEbYebqcCFA825XbORu3HD2eoC87xYZGvYb5Oru7Gso8J5DavG5SxjyBJpU78m5LPIn6sTuBxdG8bIAXu/emXjya0TM7oaGmvPemXj3S87k7GbrPO95cg7wtksi8tRQyu+zNoDzTIoI8Ov+7vInEjDxnTC482cMsPD6f7zwghpg7v0oJvf3ecTyPNm+9QaBmvEBmlzwB2Ys8WA2MuhtSrzyxSeC8KPXHu1Z1LDykoh08LyooPTJlbrzxzw47nQx6O/vWHb1I1UY7MsaxPEo3Ab1LOys8PcoNPGJO6rzcxCO9VD4QPQtFiLxX3kM87NFKOwh+YDySN+a8A+FfuiqRUbz52VC7KFo1PfRrGDz1b8I8+m0GPGuH9LvgLrK7LyoovSfGfzzGtQ48IIaYvGYZPLy6sTI9rkhpO0w/1TplsKQ8kpzTPAbiVr0Ae3s7aBYJPEo3gbzZ+dG8VnGCvE48IjxeSck88zxQPXOKWTuD+EO71vhau0KZCTuuSGm8a+xhPDqaTj0ZVWI7txWpvNb4Wrx5J9o8WrDyulfewzxjRw297ZulvH+ONTypq2g8QGYXO/pthryvQYy7fMNjvJrRs7xPpTm6WtsQPci94ruE/O28xeuzPKYP3ztiTuo8Qc8uvCS6gTwLGmq8cIU4vBVMlzzoY5I8Pp/vvOH4DL2AWBC8PzOluTWRAzzvo/k8ybYFvKM5Bj3eMeU76v8bOyS6gbxxUz27786Xu6mr6LwE2gK8SzsrvD2bxTzZw6w6nGmTPFLcVT0TU3S8EbfqPIRh2zxmGTw88aBGvJ9qCrsyKx+8j2ENvbDgSDqbOku8IiIivMKFT722gXO8bSP+u/ShvTypdcO8Sm0mO4X1kLyv3B48l295O/TQBT0vKig8YxjFOzbLUj0TfpK8HSA0vKgMLDzlxwg8c4pZu70Xl7zraLM8ptk5vbQQiLyxD5G5hZAjPd4xZbvellK8kpzTPHdVqzw2X4i8rECVvFtEKDxwJHU6zSCUO3G4KrvrZIm6bYhrPD83T7xVQrq7eL7CvEABqjxfE6Q8EFL9PNz6yLzblds8ntZUOQtFCDxUPpA83jFlPP7XFL34OhQ9OfsRu4qZbrytpYK8RZqAvFeve7sST0q8ST7evGexG7yacHC7F4Mzvd0tuzxkSzc8+qfVOgu1/DzI7Cq9CH7gO1gNDD3qmi69723Uuo4yRTyxqiM9qRBWvNP34zwZgAA9mWicvB/y4jyHYlK81pPturJCg7veMWU8wBiOPDPKWz36p9U8UXfoPKbVj7ydcee8iv7bN80glDyeBZ08lgI4vMAYDr37Ows9BhGfvGqDSjpqTaU72JRkPZnNCbxi6fw7SzsrPNEltbxmfqk86pouPaQHC7uY/4S8X01zO8jsqjrvbdS7+HA5vB0gtDuAXLq8ciFCPTRekTtg4ag8LyooPGGrA72VOF09imPJO6DTIbwhVJ29hGFbO4HF0Tv1CtU8CxrqO4X1ELxs5YQ8cLSAunwo0bw0XhE9+DqUvGCy4Drb9p485JjAvDpkqbsvYE08TW4dPWKvrbwHerY88QW0vBm2Jby1FDI7/jwCO9BbWrxV3Uw9XbFpvI3/Uru35uA7cbgqPRvtQTr2PUc8CRIWvRxW2Tsqx/Y8nQz6OwYRnzxk5sk8ISl/POEyXD0PSqm63yqIu/RrmLjTXFG9h2JSO3UiObyhawG9hfWQvJLS+DxJPt48v0qJPFF3aDzqNUE8jcmtPFtEqLzWXci7Y+Ifu9wpkbzun888MPisux5TJjwCfPI8gcGnvJbMEjuhBpQ8P5iSPEBml7sZ8PQ8e7+5PIz7qLwBdB69ee0KPOlr5jzOJD69oHLeuydWizxaek28VHjfvJc1qjuuqay79aXnPGtRzzzVvou8t7C7OrnjLTlLcVA8blLGu5EzvLwARda7blLGvKIKvrt9W0O9THV6vH76f7zyCV48yL3iu2kas7ttiOs8aLVFu1FzPj3raLM8coKFvJGYqTuM+6i8dx+GOgh+YD296M68ST5eOgWoB7sSSyA9mAMvu52cBTwIRJG8oaEmvMjsqryvEsQ738WaPGpNpTxygoW8dLmhvHiIHTzQkX88iV+fvP3e8Tq7ew08nZyFvCcrbb3YvwK97TY4PIb5ujvQW1q83sz3u+/OFz1dsWk89aVnvIGLAr3sMg68fMPjPIFgZDzwAQq7WaxIuoGLgrkBdB48xlChvOH4jLyBJhU8pqrxPIfHv7v63fq8KSg6vdtbjLsahKq8C7X8OjD4rLx3Vau8axcAvUGgZruMxQM976N5uzP5Izx6uw+9qRBWu3QeDzyXNSq81PAGPW1OHDx/xNq8HeoOvW1OnLzrZIm8kpzTPCpXgjwhVJ28NZGDO3bwPb2s2ye7Hu44vBZU67yz4T+7hyiDPGLp/LuYOdQ7paZHPF17RLyMmmW8Hx0BO3nCbLzmMCC9w0+qvIBcurxpf6A8e+6BvHRUtDwG4ta8fVtDvPY9x7zn/iQ9U3ALPYD3TDvTknY6F02OuiIiorxbDgM9HPFrPP1D3zwn8R296jVBPKaq8TuurVY7KPXHPLOrmjx1vUu8eYxHPH/EWjwwLtI8kC8SvEx1ejw0XhE8yh+dujto0zwR4gi94JMfPT7ON7ymqvE8XbHpPE528Ty546282JRkPByMfjw0/U09xFd+vJrRM7tFOb080CGLPKl1Q70kKva86GOSPOc0SjulcCK8RgMYPDvJFrrkzuW8Ps63vCgkkDwWGhy9mJ5BO1qwcjwvZPc8DRe3vIz7qLyKme48/g26PBjsSjq5rQg82PWnuofHP7ww9AK6EFJ9PPg6lLyaNiG8uR39vBlV4rwEdZW7ogq+PCqR0bw2lS2962gzPZ2cBT2abMa8brezNy3zi7xfeJG9J/EdvBeDMzuAXDq9/d5xPN9grTshVB29ylnsui0tW7o1kYM81fQwPMDpxby54y08" + } + ], + "model": "text-embedding-3-small", + "usage": { + "prompt_tokens": 9, + "total_tokens": 9 + } + } + headers: + CF-RAY: + - 99ac1f2bcf0c5a01-MXP + Connection: + - keep-alive + Content-Type: + - application/json + Date: + - Fri, 07 Nov 2025 10:24:35 GMT + Server: + - cloudflare + Set-Cookie: test_set_cookie + Transfer-Encoding: + - chunked + X-Content-Type-Options: + - nosniff + access-control-allow-origin: + - '*' + access-control-expose-headers: + - X-Request-ID + alt-svc: + - h3=":443"; ma=86400 + cf-cache-status: + - DYNAMIC + content-length: + - '8414' + openai-model: + - text-embedding-3-small + openai-organization: test_openai_org_id + openai-processing-ms: + - '90' + openai-project: + - proj_Pf1eM5R55Z35wBy4rt8PxAGq + openai-version: + - '2020-10-01' + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + via: + - envoy-router-545c575f45-dndxq + x-envoy-upstream-service-time: + - '307' + x-openai-proxy-wasm: + - v0.1 + x-ratelimit-limit-requests: + - '10000' + x-ratelimit-limit-tokens: + - '5000000' + x-ratelimit-remaining-requests: + - '9999' + x-ratelimit-remaining-tokens: + - '4999988' + x-ratelimit-reset-requests: + - 6ms + x-ratelimit-reset-tokens: + - 0s + x-request-id: + - req_8c7cc42f2a184e4cbf905a2f3830b7ca + status: + code: 200 + message: OK +version: 1 diff --git a/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/cassettes/test_embeddings_with_not_given_values[not_given_value1].yaml b/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/cassettes/test_embeddings_with_not_given_values[not_given_value1].yaml new file mode 100644 index 000000000..f8c9717b0 --- /dev/null +++ b/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/cassettes/test_embeddings_with_not_given_values[not_given_value1].yaml @@ -0,0 +1,124 @@ +interactions: +- request: + body: |- + { + "input": "This is a test for embeddings with encoding format", + "model": "text-embedding-3-small", + "encoding_format": "base64" + } + headers: + accept: + - application/json + accept-encoding: + - gzip, deflate + authorization: + - Bearer test_openai_api_key + connection: + - keep-alive + content-length: + - '127' + content-type: + - application/json + host: + - api.openai.com + user-agent: + - OpenAI/Python 1.109.1 + x-stainless-arch: + - x64 + x-stainless-async: + - 'false' + x-stainless-lang: + - python + x-stainless-os: + - Linux + x-stainless-package-version: + - 1.109.1 + x-stainless-read-timeout: + - '600' + x-stainless-retry-count: + - '0' + x-stainless-runtime: + - CPython + x-stainless-runtime-version: + - 3.10.12 + method: POST + uri: https://api.openai.com/v1/embeddings + response: + body: + string: |- + { + "object": "list", + "data": [ + { + "object": "embedding", + "index": 0, + "embedding": "DK6fO8qEirxr7OE8SQSPvXpWIry659e8mJ5BPUKZiTxZdqM76WvmPDaVLT0xwge86TEXvU+lObzFhka8XkUfvNgvd7yb1d08OC0NuzMvyTshVB07MPgsvc7umDzYWhU9SjeBPBQdz7wB2Yu8gYuCPbZ9ST1078a8c4rZvDwyrrxnsRs8BRj8vNv2nrz3bA88HFZZvMXnCT2xSeC8Pp/vOuVimzyN/1K8kc5OvXAk9bvH6AA8UgsePWdMrrxvgQ69+Z8BvEE0HD2w4Ei9FbEEPUY9Zzyd0qq7ZbROvA6Azjx0Ho+8vbKpPITGyLsC3bW7mAMvPTXHKL3QIYu6mNRmvNYnozx078a6l295vQyuHz1frra6bYhrvP/bPj1O17Q8KCSQvJKcUzzXjBA9m9XduwBFVrwJSDs7RTk9vSS6gb2m2Tm8UXfoPL+6/bxyggW9mnDwOxBS/Tx6u4+94JMfvIIqv7xvgQ69/d7xvH1XGb08lxu9H7y9vBvtwTtOdnG8G1IvvJU4Xb06ZCm7+NUmPJg5VLzP8kI8y4i0u35fbbta2xC6arKSvGUVkjwU56m7Y0eNvT7ON7xc3Ae9C39Xu5drT7w4yJ88kmKEPVtEKLwLf1c86ZYEvdP3Y7ujDug7jf/SvHuJFLxWED89LyqovB2FIb1i6fy8IbkKulupFb0O5bu8UXdovOqaLrzeXIO8XNwHPRAYLj36o6u9gYsCvQjfo72zRi083S07vbmCaj1KbaY6oWsBvfaeCjz9Q1+8PgTdvKtHcjwVUMG9qqQLPQhEkTwW7308dLkhvAJ88jtoUFi9v+UbPGIUG73BHDi9H/LivEI4xrypEFY9MWHEu2N9MjmyQgM9NQF4PDsuhDrC6jy7kwFBvBfoID0tLds6o9QYvRVMF7x3VSu8PcqNvPM4przCu/S5kmIEvfVvwjqrqLW892yPvZJiBDvwAQq9mDnUvBAYLj3/2z68HBwKPCMmzLyLMU49mNRmPCGKwjycaRM9wuo8uwJCo7ypENY8r9yePQbiVjvgyUQ8cOolvXIhQj0B2Qu9CRIWPbkd/bzvCGe8tBAIPYTGyLxgsuA8Bd6sPFrbEL1BNBy9RzYKvdPBvrxkSze8e172PKPUmLzAGA48OMgfO3ZRgTv3ByI9fiUevYWQI7xS3FU8izHOvIbDlbyxSeA8nAhQPZg51DyuDpq9KvIUvAt/17zHuTi93sx3PUc2iryjOQa77GzdvNHvjztJo0s8HByKPE3TCrzGVEs7xFf+uyW+q7zT9+M8c+scvVDUAbqHYlI9nAhQu2AXTrx+X+08aRqzO/k+Pr31CtU8dO9GOuEy3DyS/ZY8W6kVvP48Ar1MP9W8c1AKve5pqrymqnE9YnmIPO02uDc6ms66l9C8PAKnkLya0bO6ptWPPFUMlbxMBYY8z1ewO0ptJrqD+EO9c8B+O0nZ8LzellI8L4+VPFWnJ70zL0k9oaGmvCpXgrz0axi7KvKUPK6pLL0xwoe9DuW7O43JLT3C5pK89j3HN052cbxbDgO8saojvfY5HTuoRnu7UXdovQJCIz3uaao8JLoBvbkdfTwTtDc80/fjO7GqIz3rZIm8XHcaPUQ1kzvLUg89FB1PPSj1R71rh/Q8ljGAvSW+Kz1XeVY9GVXiujsDZryx5HI8n2oKu51x5zw4Z1y9MPgsvDD4rDyrR3K8njtCPNBb2rpu7Vi8AnzyOeH8Nj1SQUO9kv2WPH28Bj0Ae/u8XqqMPPrdertGA5g8cyXsvPXULz20Slc9RTm9u6Q9sLwyKx+8RDUTvQwTjTyvd7E7qj+evOkG+bwQs8C8ogo+PWvs4TzIhz29TnJHPeaVjTvb9h49rq3WPBVMl7x7iZS7OfsRvGrot7xPQMw8/QmQPW+BDr0PFIQ8sniovEmjy7yqpAu9OAJvPKaq8bu24ja8Q6HdPIstpDx2wfW8DK4fvf1DXzw3/sQ8tXkfvI4yxbzChc872L+CvJ0MejsNfKS8eIgdOkOdMzmOkwg9IbkKPbewOz1WEL88M5Q2vYbDFb371h29BagHPA4b4Twbt5w8AQ8xPKcIgrxWdSw5h5h3PIpjSbq85KS86ALPvEai1DwvYE09SaPLPIJZB72xD5G6ua2IvGJOarxREnu9jpOIPN3IzbyhBhS7Qc8uPWVPYT2V/g086WvmPBGBRbxmfqm8Hx2BvO1lAD3S87m862SJPAp7LTveMWW56WvmvJloHDyuSOk8V3lWvFupFb3q/5u9WrDyO7lIm7x6VqK8CBlzPQsaarzP8kI7gYuCvRkbE71+igu8aOtqu1cU6TsVTJe8EeYyvfOdEz1iTuo8OMgfvDyXm7vTIgI8wksAPcK79LyhPDm9nXFnvJPLG72SnNO7xecJPQB7+zr3QfG8o3NVvKmraL0A4Oi8fI0+vYKPrDzwAQq9ecJsvLmCajy3FSk9KvKUvC33tTuhBpS91CYsvY4yxTzFvGs9e4mUvOJhJL02lS28EFL9PIHBJ7xhSsC8V3lWPSSPY7uP/B88b4GOPDP5o7yyQoM8oDgPvJdrz7zT9+O87mmqu+T5A73tZQC9gfv2uuZq77xzUAo+VnWsvE48Ir0yKx89HriTu6M5Br3vMwW8H1dQvWCyYLwVTBc8x1j1vJPLm7yBi4K8ZuMWPZ0M+ryMmmW8+zuLPFfewzxOoY8842n4PFQ+kDxAZhe9MV2avAcVybyY1OY7jJrlPIdi0rwB2Qu9iv5bPD83zzt/xNo7yoQKvSosZD1g4Si9DhvhvMa1jjy5fsC8zb9QvFZ1rLxFOb07ExmlPNVZnrzA6cW9CH7gvJeal7wYUbi8jf9SvGgWCT3YlGQ7aIZ9u6zbJ7zZ+dG7wYGlPDJlbjyOMkW8ukzFPO8IZ7wCfPI8MC5SvCAhq7t1vcs84saRPB3qjrnWwrW8CnutPPY5nTpjfbK8elaiPPgLTL3QvJ08FB3PPK2lAj1GPWe8O8kWvePKOztiTmo8c1CKPGKzVz3dkig9wOlFvTpkKT2mD986TAkwvIrINj0W7/28eyQnvYT87brnmTc8mnBwO4GLAr371h08KixkvKVwojyMmuW8A+HfO9sw7rtBNBw8xFd+PDb6GjxREvu86WvmOwbi1jw+BF28tn1JvHBPkzlOcsc80/djPNSLmbtmGTy9brezOw+vFjw+BN08CneDvNhaFb3mlQ09NGK7PDf+xDyKme68HFbZPLUUMjw3YzI9mDnUvC33NT2oRvs8O574uiGO7Lsjhw+8723UvPaeirsCeMg801zRuxyM/jvdkig7EkugPBbv/bugct47D6+WvGJO6rsGER88C39XO/pC6Dybnzg7VUK6O/M4pjwlIxk8CH5gvCGKwjsKdwM9SQQPvf1D37yH/eS7toHzvJ4FHbysrN88KcNMvSMmzLv3QfG42fnRPLQQiDxInyG7+AvMvBKwjTtyIUK8pabHu+DJRLyCj6w88qTwvOT5AzyF9ZC8bekuPHZRAb16u4+8wksAvKBy3rjHgxO9TAWGvKOp+jmuSGk6Ov+7vO02uDu5SJs8l5qXvZxpEzzqNcG8CnstPKM5hrwMSTK7zb9QPKHXS71GotQ8jJY7vexs3bxDZ447wksAvJX+Dby5HX28JPAmPEZohbwX6KC8RZqAPKaq8Tv/QCw9nZyFvC/FOruLLaS70e8Pvd3IzbwI36M8+t36uzZfiDzEHS89RAbLPPMCATymD187ak0lOyYnQzt3HwY9gSYVu64OGr3ezPe5PQAzvdkoGrzezPc8YrPXPH6KC7yWMYA7850TvP+lmTySYoQ8ZRWSPOtos7zn/qQ8Rj1nu7mtCLxm4xY8fI2+vFGiBrzO7pi776N5OmgWibyV02+8BRh8unwoUTv3ojS9st2VvFILHjydnIU8lzWqu1faGby651c8Oy4EPNJYJ7wO5Tu76wPGvC8qKLylcKK6o9hCO/nZUDwRHFg83vcVvaejFD0GER89JIs5vHmMRzwXg7O8gWBkvIBYEDzkM1O8tErXu4JZh7y3sLu8PzMlPePKO7y85KQ6H431PBNT9DuEJww9KFq1OfM8UDxfrja7c4rZO0TQpbwFfem7H/JiPKYP3zxxU707qEb7u1d51rt8KFE8FlRrPaBy3rzvo/m8gFiQPLewO73k+QM9K1ssPFytPzxQ1AE8wrv0vNLzObyRzk48NstSvBdNjjyUNDO8vB50vc2FAb01x6g79wciPe0Ak7vVWR48CnstvCos5LwJrai99weivIuSkTxcTPy80YoiPMlRmDy0gPy7Ca0oPNVZnrxasPI89NCFO1JBw7w7A+a4ntbUvHhdfzzRiqK8d1UrPbdLzrxUE/K8rq1WPMGBJbyNLhs85ceIPSfxnTye1lS9axuquVES+7uuc4e8QTQcvGNHDTuzq5q8F02OvCG5irwD4V88hl6oPOH8trycaRO8kv2WO4L0GT1/xFq6QwKhPGJO6rwJ4828GfB0vFN0NT01LJa7c8D+O4zFA7wCp5A83CkRvW2IazrkM1M5o6n6uI1kwLy5gmo83S07Pdn5UTy5Hf27WRE2vGsXAD3kmMC88qTwPA9KqTtUPpC7MmVuPA6287sl9FC8/UPfPBO0t7x+igu9mnBwPKULNbvuaSo9q0fyO2fnQL2ZzYm8p6MUPWnkDT3Zw6w6Z+dAPbBFNj0ovyI9rHa6uhYanDz3pl68x4OTO5X+jbxJo0u9v0oJvdkomrtAO3k8MWHEvIQnjLwuXKM8H411uBmAgLxOdnE8njtCveGXybo1kYO8yh8dPSOHjzz93nG8OGfcvKgMLL1n50C8RmgFPC2SyDzellK6dFS0OhJPyjsZVWI8MWHEO5pwcLxmfim9/QkQvEo3AbzEghw9G+1BPHcfBrtDPHC8BNqCPdbCtTxREvu88aBGvXAkdTqYA687v+UbOuGXyby8SZI8URJ7vAEPMT3szSC89p6KvQPh3zyWZ6U8uHqWu9z6SDy05Wm9IvPZu/UKVTzdyM28pnTMvHMl7LvH6IC8zfV1PLOrGr2/H+s8sniou5X+Db25fkC8dYemPGIUm7wTtDe8LfOLPAkSlrzeXIO7v0oJPOtoszwtksg80r0UOgp7LTyabMa87NFKvAsa6jxBoGa8IIaYvB8dAT1aFeC8tID8uxhROL2pq2g8wuq8O1gNDLtOdvE8jPuoPBSCvDuSYoQ8elaiPF9N8zrAsyA9qavoPEbYebqcCFA825XbORu3HD2eoC87xYZGvYb5Oru7Gso8J5DavG5SxjyBJpU78m5LPIn6sTuBxdG8bIAXu/emXjya0TM7oaGmvPemXj3S87k7GbrPO95cg7wtksi8tRQyu+zNoDzTIoI8Ov+7vInEjDxnTC482cMsPD6f7zwghpg7v0oJvf3ecTyPNm+9QaBmvEBmlzwB2Ys8WA2MuhtSrzyxSeC8KPXHu1Z1LDykoh08LyooPTJlbrzxzw47nQx6O/vWHb1I1UY7MsaxPEo3Ab1LOys8PcoNPGJO6rzcxCO9VD4QPQtFiLxX3kM87NFKOwh+YDySN+a8A+FfuiqRUbz52VC7KFo1PfRrGDz1b8I8+m0GPGuH9LvgLrK7LyoovSfGfzzGtQ48IIaYvGYZPLy6sTI9rkhpO0w/1TplsKQ8kpzTPAbiVr0Ae3s7aBYJPEo3gbzZ+dG8VnGCvE48IjxeSck88zxQPXOKWTuD+EO71vhau0KZCTuuSGm8a+xhPDqaTj0ZVWI7txWpvNb4Wrx5J9o8WrDyulfewzxjRw297ZulvH+ONTypq2g8QGYXO/pthryvQYy7fMNjvJrRs7xPpTm6WtsQPci94ruE/O28xeuzPKYP3ztiTuo8Qc8uvCS6gTwLGmq8cIU4vBVMlzzoY5I8Pp/vvOH4DL2AWBC8PzOluTWRAzzvo/k8ybYFvKM5Bj3eMeU76v8bOyS6gbxxUz27786Xu6mr6LwE2gK8SzsrvD2bxTzZw6w6nGmTPFLcVT0TU3S8EbfqPIRh2zxmGTw88aBGvJ9qCrsyKx+8j2ENvbDgSDqbOku8IiIivMKFT722gXO8bSP+u/ShvTypdcO8Sm0mO4X1kLyv3B48l295O/TQBT0vKig8YxjFOzbLUj0TfpK8HSA0vKgMLDzlxwg8c4pZu70Xl7zraLM8ptk5vbQQiLyxD5G5hZAjPd4xZbvellK8kpzTPHdVqzw2X4i8rECVvFtEKDxwJHU6zSCUO3G4KrvrZIm6bYhrPD83T7xVQrq7eL7CvEABqjxfE6Q8EFL9PNz6yLzblds8ntZUOQtFCDxUPpA83jFlPP7XFL34OhQ9OfsRu4qZbrytpYK8RZqAvFeve7sST0q8ST7evGexG7yacHC7F4Mzvd0tuzxkSzc8+qfVOgu1/DzI7Cq9CH7gO1gNDD3qmi69723Uuo4yRTyxqiM9qRBWvNP34zwZgAA9mWicvB/y4jyHYlK81pPturJCg7veMWU8wBiOPDPKWz36p9U8UXfoPKbVj7ydcee8iv7bN80glDyeBZ08lgI4vMAYDr37Ows9BhGfvGqDSjpqTaU72JRkPZnNCbxi6fw7SzsrPNEltbxmfqk86pouPaQHC7uY/4S8X01zO8jsqjrvbdS7+HA5vB0gtDuAXLq8ciFCPTRekTtg4ag8LyooPGGrA72VOF09imPJO6DTIbwhVJ29hGFbO4HF0Tv1CtU8CxrqO4X1ELxs5YQ8cLSAunwo0bw0XhE9+DqUvGCy4Drb9p485JjAvDpkqbsvYE08TW4dPWKvrbwHerY88QW0vBm2Jby1FDI7/jwCO9BbWrxV3Uw9XbFpvI3/Uru35uA7cbgqPRvtQTr2PUc8CRIWvRxW2Tsqx/Y8nQz6OwYRnzxk5sk8ISl/POEyXD0PSqm63yqIu/RrmLjTXFG9h2JSO3UiObyhawG9hfWQvJLS+DxJPt48v0qJPFF3aDzqNUE8jcmtPFtEqLzWXci7Y+Ifu9wpkbzun888MPisux5TJjwCfPI8gcGnvJbMEjuhBpQ8P5iSPEBml7sZ8PQ8e7+5PIz7qLwBdB69ee0KPOlr5jzOJD69oHLeuydWizxaek28VHjfvJc1qjuuqay79aXnPGtRzzzVvou8t7C7OrnjLTlLcVA8blLGu5EzvLwARda7blLGvKIKvrt9W0O9THV6vH76f7zyCV48yL3iu2kas7ttiOs8aLVFu1FzPj3raLM8coKFvJGYqTuM+6i8dx+GOgh+YD296M68ST5eOgWoB7sSSyA9mAMvu52cBTwIRJG8oaEmvMjsqryvEsQ738WaPGpNpTxygoW8dLmhvHiIHTzQkX88iV+fvP3e8Tq7ew08nZyFvCcrbb3YvwK97TY4PIb5ujvQW1q83sz3u+/OFz1dsWk89aVnvIGLAr3sMg68fMPjPIFgZDzwAQq7WaxIuoGLgrkBdB48xlChvOH4jLyBJhU8pqrxPIfHv7v63fq8KSg6vdtbjLsahKq8C7X8OjD4rLx3Vau8axcAvUGgZruMxQM976N5uzP5Izx6uw+9qRBWu3QeDzyXNSq81PAGPW1OHDx/xNq8HeoOvW1OnLzrZIm8kpzTPCpXgjwhVJ28NZGDO3bwPb2s2ye7Hu44vBZU67yz4T+7hyiDPGLp/LuYOdQ7paZHPF17RLyMmmW8Hx0BO3nCbLzmMCC9w0+qvIBcurxpf6A8e+6BvHRUtDwG4ta8fVtDvPY9x7zn/iQ9U3ALPYD3TDvTknY6F02OuiIiorxbDgM9HPFrPP1D3zwn8R296jVBPKaq8TuurVY7KPXHPLOrmjx1vUu8eYxHPH/EWjwwLtI8kC8SvEx1ejw0XhE8yh+dujto0zwR4gi94JMfPT7ON7ymqvE8XbHpPE528Ty546282JRkPByMfjw0/U09xFd+vJrRM7tFOb080CGLPKl1Q70kKva86GOSPOc0SjulcCK8RgMYPDvJFrrkzuW8Ps63vCgkkDwWGhy9mJ5BO1qwcjwvZPc8DRe3vIz7qLyKme48/g26PBjsSjq5rQg82PWnuofHP7ww9AK6EFJ9PPg6lLyaNiG8uR39vBlV4rwEdZW7ogq+PCqR0bw2lS2962gzPZ2cBT2abMa8brezNy3zi7xfeJG9J/EdvBeDMzuAXDq9/d5xPN9grTshVB29ylnsui0tW7o1kYM81fQwPMDpxby54y08" + } + ], + "model": "text-embedding-3-small", + "usage": { + "prompt_tokens": 9, + "total_tokens": 9 + } + } + headers: + CF-RAY: + - 99ac1f320cac4c6f-MXP + Connection: + - keep-alive + Content-Type: + - application/json + Date: + - Fri, 07 Nov 2025 10:24:37 GMT + Server: + - cloudflare + Set-Cookie: test_set_cookie + Transfer-Encoding: + - chunked + X-Content-Type-Options: + - nosniff + access-control-allow-origin: + - '*' + access-control-expose-headers: + - X-Request-ID + alt-svc: + - h3=":443"; ma=86400 + cf-cache-status: + - DYNAMIC + content-length: + - '8414' + openai-model: + - text-embedding-3-small + openai-organization: test_openai_org_id + openai-processing-ms: + - '58' + openai-project: + - proj_Pf1eM5R55Z35wBy4rt8PxAGq + openai-version: + - '2020-10-01' + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + via: + - envoy-router-7cd555d77c-nhr8p + x-envoy-upstream-service-time: + - '867' + x-openai-proxy-wasm: + - v0.1 + x-ratelimit-limit-requests: + - '10000' + x-ratelimit-limit-tokens: + - '5000000' + x-ratelimit-remaining-requests: + - '9999' + x-ratelimit-remaining-tokens: + - '4999988' + x-ratelimit-reset-requests: + - 6ms + x-ratelimit-reset-tokens: + - 0s + x-request-id: + - req_2d5eb18c0eed49f1b12359871cb8d17d + status: + code: 200 + message: OK +version: 1 diff --git a/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/conftest.py b/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/conftest.py index 83a4ba8c6..af626d799 100644 --- a/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/conftest.py +++ b/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/conftest.py @@ -12,10 +12,21 @@ OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT, ) from opentelemetry.sdk._logs import LoggerProvider -from opentelemetry.sdk._logs.export import ( - InMemoryLogExporter, - SimpleLogRecordProcessor, -) + +# Backward compatibility for InMemoryLogExporter -> InMemoryLogRecordExporter rename +try: + from opentelemetry.sdk._logs.export import ( # pylint: disable=no-name-in-module + InMemoryLogRecordExporter, + SimpleLogRecordProcessor, + ) +except ImportError: + # Fallback to old name for compatibility with older SDK versions + from opentelemetry.sdk._logs.export import ( + InMemoryLogExporter as InMemoryLogRecordExporter, + ) + from opentelemetry.sdk._logs.export import ( + SimpleLogRecordProcessor, + ) from opentelemetry.sdk.metrics import ( MeterProvider, ) @@ -38,7 +49,7 @@ def fixture_span_exporter(): @pytest.fixture(scope="function", name="log_exporter") def fixture_log_exporter(): - exporter = InMemoryLogExporter() + exporter = InMemoryLogRecordExporter() yield exporter diff --git a/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/requirements.latest.txt b/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/requirements.latest.txt index abed97360..2e29be522 100644 --- a/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/requirements.latest.txt +++ b/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/requirements.latest.txt @@ -36,9 +36,11 @@ # This variant of the requirements aims to test the system using # the newest supported version of external dependencies. -openai==1.26.0 +openai==1.109.1 pydantic==2.8.2 httpx==0.27.2 +# older jiter is required for PyPy < 3.11 +jiter==0.11.1 Deprecated==1.2.14 importlib-metadata==6.11.0 packaging==24.0 diff --git a/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/requirements.oldest.txt b/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/requirements.oldest.txt index 774fd85ad..7026f44af 100644 --- a/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/requirements.oldest.txt +++ b/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/requirements.oldest.txt @@ -15,7 +15,7 @@ # This variant of the requirements aims to test the system using # the oldest supported version of external dependencies. -openai==1.26.0 +openai[datalib]==1.26.0 pydantic==2.8.2 httpx==0.27.2 Deprecated==1.2.14 diff --git a/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/test_async_chat_completions.py b/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/test_async_chat_completions.py index f618267e6..59594c028 100644 --- a/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/test_async_chat_completions.py +++ b/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/test_async_chat_completions.py @@ -183,6 +183,8 @@ async def test_async_chat_completion_extra_params( response.model, response.usage.prompt_tokens, response.usage.completion_tokens, + request_service_tier="default", + response_service_tier=getattr(response, "service_tier", None), ) assert ( spans[0].attributes[GenAIAttributes.GEN_AI_OPENAI_REQUEST_SEED] == 42 diff --git a/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/test_chat_completions.py b/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/test_chat_completions.py index 471d70fcd..ce54b96d6 100644 --- a/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/test_chat_completions.py +++ b/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/test_chat_completions.py @@ -13,9 +13,20 @@ # limitations under the License. # pylint: disable=too-many-locals +import logging import pytest -from openai import APIConnectionError, NotFoundError, OpenAI +from openai import ( + NOT_GIVEN, + APIConnectionError, + NotFoundError, + OpenAI, +) + +try: + from openai import not_given # pylint: disable=no-name-in-module +except ImportError: + not_given = NOT_GIVEN from opentelemetry.semconv._incubating.attributes import ( error_attributes as ErrorAttributes, @@ -43,7 +54,9 @@ def test_chat_completion_with_content( messages_value = [{"role": "user", "content": "Say this is a test"}] response = openai_client.chat.completions.create( - messages=messages_value, model=llm_model_value, stream=False + messages=messages_value, + model=llm_model_value, + stream=False, ) spans = span_exporter.get_finished_spans() @@ -75,6 +88,42 @@ def test_chat_completion_with_content( assert_message_in_logs(logs[1], "gen_ai.choice", choice_event, spans[0]) +@pytest.mark.vcr() +def test_chat_completion_handles_not_given( + span_exporter, log_exporter, openai_client, instrument_no_content, caplog +): + caplog.set_level(logging.WARNING) + llm_model_value = "gpt-4o-mini" + messages_value = [{"role": "user", "content": "Say this is a test"}] + + response = openai_client.chat.completions.create( + messages=messages_value, + model=llm_model_value, + stream=False, + top_p=NOT_GIVEN, + max_tokens=not_given, + ) + + (span,) = span_exporter.get_finished_spans() + assert_all_attributes( + span, + llm_model_value, + response.id, + response.model, + response.usage.prompt_tokens, + response.usage.completion_tokens, + response_service_tier="default", + ) + + assert GenAIAttributes.GEN_AI_REQUEST_TOP_P not in span.attributes + assert GenAIAttributes.GEN_AI_REQUEST_MAX_TOKENS not in span.attributes + + logs = log_exporter.get_finished_logs() + assert len(logs) == 2 + + assert_no_invalid_type_warning(caplog) + + @pytest.mark.vcr() def test_chat_completion_no_content( span_exporter, log_exporter, openai_client, instrument_no_content @@ -221,6 +270,8 @@ def test_chat_completion_extra_params( response.model, response.usage.prompt_tokens, response.usage.completion_tokens, + request_service_tier="default", + response_service_tier=getattr(response, "service_tier", None), ) assert ( spans[0].attributes[GenAIAttributes.GEN_AI_OPENAI_REQUEST_SEED] == 42 @@ -889,3 +940,7 @@ def get_current_weather_tool_definition(): }, }, } + + +def assert_no_invalid_type_warning(caplog): + assert "Invalid type" not in caplog.text diff --git a/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/test_embeddings.py b/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/test_embeddings.py index 609c91e08..184d372de 100644 --- a/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/test_embeddings.py +++ b/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/test_embeddings.py @@ -15,7 +15,17 @@ """Unit tests for OpenAI Embeddings API instrumentation.""" import pytest -from openai import APIConnectionError, NotFoundError, OpenAI +from openai import ( + NOT_GIVEN, + APIConnectionError, + NotFoundError, + OpenAI, +) + +try: + from openai import not_given # pylint: disable=no-name-in-module +except ImportError: + not_given = NOT_GIVEN from opentelemetry.sdk.trace import ReadableSpan from opentelemetry.semconv._incubating.attributes import ( @@ -162,6 +172,33 @@ def test_embeddings_with_encoding_format( ) +@pytest.mark.parametrize("not_given_value", [NOT_GIVEN, not_given]) +@pytest.mark.vcr() +def test_embeddings_with_not_given_values( + span_exporter, + metric_reader, + openai_client, + instrument_no_content, + not_given_value, +): + """Test creating embeddings with NOT_GIVEN and not_given values""" + model_name = "text-embedding-3-small" + input_text = "This is a test for embeddings with encoding format" + + response = openai_client.embeddings.create( + model=model_name, + input=input_text, + dimensions=not_given_value, + ) + + # Verify spans + spans = span_exporter.get_finished_spans() + assert len(spans) == 1 + assert_embedding_attributes(spans[0], model_name, response) + + assert "gen_ai.request.dimensions" not in spans[0].attributes + + @pytest.mark.vcr() def test_embeddings_bad_endpoint( span_exporter, metric_reader, instrument_no_content diff --git a/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/test_utils.py b/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/test_utils.py index eb66eb7c7..d4d64c402 100644 --- a/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/test_utils.py +++ b/instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/test_utils.py @@ -25,6 +25,14 @@ ) +def _assert_optional_attribute(span, attribute_name, expected_value): + """Helper to assert optional span attributes.""" + if expected_value is not None: + assert expected_value == span.attributes[attribute_name] + else: + assert attribute_name not in span.attributes + + def assert_all_attributes( span: ReadableSpan, request_model: str, @@ -35,6 +43,8 @@ def assert_all_attributes( operation_name: str = "chat", server_address: str = "api.openai.com", server_port: int = 443, + request_service_tier: Optional[str] = None, + response_service_tier: Optional[str] = None, ): assert span.name == f"{operation_name} {request_model}" assert ( @@ -49,44 +59,35 @@ def assert_all_attributes( request_model == span.attributes[GenAIAttributes.GEN_AI_REQUEST_MODEL] ) - if response_model: - assert ( - response_model - == span.attributes[GenAIAttributes.GEN_AI_RESPONSE_MODEL] - ) - else: - assert GenAIAttributes.GEN_AI_RESPONSE_MODEL not in span.attributes - - if response_id: - assert ( - response_id == span.attributes[GenAIAttributes.GEN_AI_RESPONSE_ID] - ) - else: - assert GenAIAttributes.GEN_AI_RESPONSE_ID not in span.attributes - - if input_tokens: - assert ( - input_tokens - == span.attributes[GenAIAttributes.GEN_AI_USAGE_INPUT_TOKENS] - ) - else: - assert GenAIAttributes.GEN_AI_USAGE_INPUT_TOKENS not in span.attributes - - if output_tokens: - assert ( - output_tokens - == span.attributes[GenAIAttributes.GEN_AI_USAGE_OUTPUT_TOKENS] - ) - else: - assert ( - GenAIAttributes.GEN_AI_USAGE_OUTPUT_TOKENS not in span.attributes - ) + _assert_optional_attribute( + span, GenAIAttributes.GEN_AI_RESPONSE_MODEL, response_model + ) + _assert_optional_attribute( + span, GenAIAttributes.GEN_AI_RESPONSE_ID, response_id + ) + _assert_optional_attribute( + span, GenAIAttributes.GEN_AI_USAGE_INPUT_TOKENS, input_tokens + ) + _assert_optional_attribute( + span, GenAIAttributes.GEN_AI_USAGE_OUTPUT_TOKENS, output_tokens + ) assert server_address == span.attributes[ServerAttributes.SERVER_ADDRESS] if server_port != 443 and server_port > 0: assert server_port == span.attributes[ServerAttributes.SERVER_PORT] + _assert_optional_attribute( + span, + GenAIAttributes.GEN_AI_OPENAI_REQUEST_SERVICE_TIER, + request_service_tier, + ) + _assert_optional_attribute( + span, + GenAIAttributes.GEN_AI_OPENAI_RESPONSE_SERVICE_TIER, + response_service_tier, + ) + def assert_log_parent(log, span): """Assert that the log record has the correct parent span context""" diff --git a/instrumentation-genai/opentelemetry-instrumentation-vertexai/src/opentelemetry/instrumentation/vertexai/__init__.py b/instrumentation-genai/opentelemetry-instrumentation-vertexai/src/opentelemetry/instrumentation/vertexai/__init__.py index 960e35ae4..24d9d46af 100644 --- a/instrumentation-genai/opentelemetry-instrumentation-vertexai/src/opentelemetry/instrumentation/vertexai/__init__.py +++ b/instrumentation-genai/opentelemetry-instrumentation-vertexai/src/opentelemetry/instrumentation/vertexai/__init__.py @@ -68,14 +68,14 @@ def _methods_to_wrap( ): # This import is very slow, do it lazily in case instrument() is not called # pylint: disable=import-outside-toplevel - from google.cloud.aiplatform_v1.services.prediction_service import ( + from google.cloud.aiplatform_v1.services.prediction_service import ( # noqa: PLC0415 async_client, client, ) - from google.cloud.aiplatform_v1beta1.services.prediction_service import ( + from google.cloud.aiplatform_v1beta1.services.prediction_service import ( # noqa: PLC0415 async_client as async_client_v1beta1, ) - from google.cloud.aiplatform_v1beta1.services.prediction_service import ( + from google.cloud.aiplatform_v1beta1.services.prediction_service import ( # noqa: PLC0415 client as client_v1beta1, ) diff --git a/instrumentation-genai/opentelemetry-instrumentation-vertexai/tests/conftest.py b/instrumentation-genai/opentelemetry-instrumentation-vertexai/tests/conftest.py index 3815e20da..0cf7ba0c4 100644 --- a/instrumentation-genai/opentelemetry-instrumentation-vertexai/tests/conftest.py +++ b/instrumentation-genai/opentelemetry-instrumentation-vertexai/tests/conftest.py @@ -39,10 +39,21 @@ OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT, ) from opentelemetry.sdk._logs import LoggerProvider -from opentelemetry.sdk._logs.export import ( - InMemoryLogExporter, - SimpleLogRecordProcessor, -) + +# Backward compatibility for InMemoryLogExporter -> InMemoryLogRecordExporter rename +try: + from opentelemetry.sdk._logs.export import ( # pylint: disable=no-name-in-module + InMemoryLogRecordExporter, + SimpleLogRecordProcessor, + ) +except ImportError: + # Fallback to old name for compatibility with older SDK versions + from opentelemetry.sdk._logs.export import ( + InMemoryLogExporter as InMemoryLogRecordExporter, + ) + from opentelemetry.sdk._logs.export import ( + SimpleLogRecordProcessor, + ) from opentelemetry.sdk.metrics import ( MeterProvider, ) @@ -66,7 +77,7 @@ def fixture_span_exporter(): @pytest.fixture(scope="function", name="log_exporter") def fixture_log_exporter(): - exporter = InMemoryLogExporter() + exporter = InMemoryLogRecordExporter() yield exporter diff --git a/instrumentation-genai/opentelemetry-instrumentation-vertexai/tests/test_chat_completions.py b/instrumentation-genai/opentelemetry-instrumentation-vertexai/tests/test_chat_completions.py index 61bbc0775..9e05a64a4 100644 --- a/instrumentation-genai/opentelemetry-instrumentation-vertexai/tests/test_chat_completions.py +++ b/instrumentation-genai/opentelemetry-instrumentation-vertexai/tests/test_chat_completions.py @@ -13,9 +13,17 @@ ) from opentelemetry.instrumentation.vertexai import VertexAIInstrumentor -from opentelemetry.sdk._logs._internal.export.in_memory_log_exporter import ( - InMemoryLogExporter, -) + +# Backward compatibility for InMemoryLogExporter -> InMemoryLogRecordExporter rename +try: + from opentelemetry.sdk._logs._internal.export.in_memory_log_exporter import ( # pylint: disable=no-name-in-module + InMemoryLogRecordExporter, + ) +except ImportError: + # Fallback to old name for compatibility with older SDK versions + from opentelemetry.sdk._logs._internal.export.in_memory_log_exporter import ( + InMemoryLogExporter as InMemoryLogRecordExporter, + ) from opentelemetry.sdk.trace import ReadableSpan from opentelemetry.sdk.trace.export.in_memory_span_exporter import ( InMemorySpanExporter, @@ -26,7 +34,7 @@ @pytest.mark.vcr() def test_generate_content( span_exporter: InMemorySpanExporter, - log_exporter: InMemoryLogExporter, + log_exporter: InMemoryLogRecordExporter, generate_content: callable, instrument_with_content: VertexAIInstrumentor, ): @@ -94,7 +102,7 @@ def test_generate_content( @pytest.mark.vcr() def test_generate_content_without_events( span_exporter: InMemorySpanExporter, - log_exporter: InMemoryLogExporter, + log_exporter: InMemoryLogRecordExporter, generate_content: callable, instrument_no_content: VertexAIInstrumentor, ): @@ -242,7 +250,7 @@ def test_generate_content_invalid_temperature( @pytest.mark.vcr() def test_generate_content_invalid_role( - log_exporter: InMemoryLogExporter, + log_exporter: InMemoryLogRecordExporter, generate_content: callable, instrument_with_content: VertexAIInstrumentor, ): @@ -335,7 +343,7 @@ def assert_span_error(span: ReadableSpan) -> None: @pytest.mark.vcr() def test_generate_content_all_events( - log_exporter: InMemoryLogExporter, + log_exporter: InMemoryLogRecordExporter, generate_content: callable, instrument_with_content: VertexAIInstrumentor, ): @@ -353,7 +361,7 @@ def test_generate_content_all_events( @pytest.mark.vcr() def test_preview_generate_content_all_input_events( - log_exporter: InMemoryLogExporter, + log_exporter: InMemoryLogRecordExporter, generate_content: callable, instrument_with_content: VertexAIInstrumentor, ): @@ -371,7 +379,7 @@ def test_preview_generate_content_all_input_events( def generate_content_all_input_events( model: GenerativeModel | PreviewGenerativeModel, - log_exporter: InMemoryLogExporter, + log_exporter: InMemoryLogRecordExporter, instrument_with_content: VertexAIInstrumentor, ): model.generate_content( diff --git a/instrumentation-genai/opentelemetry-instrumentation-vertexai/tests/test_chat_completions_experimental.py b/instrumentation-genai/opentelemetry-instrumentation-vertexai/tests/test_chat_completions_experimental.py index fa37f77d0..5613f551a 100644 --- a/instrumentation-genai/opentelemetry-instrumentation-vertexai/tests/test_chat_completions_experimental.py +++ b/instrumentation-genai/opentelemetry-instrumentation-vertexai/tests/test_chat_completions_experimental.py @@ -14,9 +14,17 @@ ) from opentelemetry.instrumentation.vertexai import VertexAIInstrumentor -from opentelemetry.sdk._logs._internal.export.in_memory_log_exporter import ( - InMemoryLogExporter, -) + +# Backward compatibility for InMemoryLogExporter -> InMemoryLogRecordExporter rename +try: + from opentelemetry.sdk._logs._internal.export.in_memory_log_exporter import ( # pylint: disable=no-name-in-module + InMemoryLogRecordExporter, + ) +except ImportError: + # Fallback to old name for compatibility with older SDK versions + from opentelemetry.sdk._logs._internal.export.in_memory_log_exporter import ( + InMemoryLogExporter as InMemoryLogRecordExporter, + ) from opentelemetry.sdk.trace import ReadableSpan from opentelemetry.sdk.trace.export.in_memory_span_exporter import ( InMemorySpanExporter, @@ -27,7 +35,7 @@ @pytest.mark.vcr() def test_generate_content_with_files( span_exporter: InMemorySpanExporter, - log_exporter: InMemoryLogExporter, + log_exporter: InMemoryLogRecordExporter, generate_content: callable, instrument_with_experimental_semconvs: VertexAIInstrumentor, ): @@ -113,7 +121,7 @@ def test_generate_content_with_files( @pytest.mark.vcr() def test_generate_content_without_events( span_exporter: InMemorySpanExporter, - log_exporter: InMemoryLogExporter, + log_exporter: InMemoryLogRecordExporter, generate_content: callable, instrument_with_experimental_semconvs: VertexAIInstrumentor, ): @@ -274,7 +282,7 @@ def test_generate_content_invalid_temperature( @pytest.mark.vcr() def test_generate_content_invalid_role( - log_exporter: InMemoryLogExporter, + log_exporter: InMemoryLogRecordExporter, generate_content: callable, instrument_with_experimental_semconvs: VertexAIInstrumentor, ): @@ -371,7 +379,7 @@ def assert_span_error(span: ReadableSpan) -> None: @pytest.mark.vcr() def test_generate_content_all_events( - log_exporter: InMemoryLogExporter, + log_exporter: InMemoryLogRecordExporter, generate_content: callable, instrument_with_experimental_semconvs: VertexAIInstrumentor, ): @@ -389,7 +397,7 @@ def test_generate_content_all_events( @pytest.mark.vcr() def test_preview_generate_content_all_input_events( - log_exporter: InMemoryLogExporter, + log_exporter: InMemoryLogRecordExporter, generate_content: callable, instrument_with_experimental_semconvs: VertexAIInstrumentor, ): @@ -407,7 +415,7 @@ def test_preview_generate_content_all_input_events( def generate_content_all_input_events( model: GenerativeModel | PreviewGenerativeModel, - log_exporter: InMemoryLogExporter, + log_exporter: InMemoryLogRecordExporter, instrument_with_experimental_semconvs: VertexAIInstrumentor, ): model.generate_content( diff --git a/instrumentation-genai/opentelemetry-instrumentation-vertexai/tests/test_function_calling.py b/instrumentation-genai/opentelemetry-instrumentation-vertexai/tests/test_function_calling.py index 88762d610..74269d699 100644 --- a/instrumentation-genai/opentelemetry-instrumentation-vertexai/tests/test_function_calling.py +++ b/instrumentation-genai/opentelemetry-instrumentation-vertexai/tests/test_function_calling.py @@ -1,22 +1,30 @@ import pytest +from tests.shared_test_utils import ( + ask_about_weather, + ask_about_weather_function_response, +) from opentelemetry.instrumentation.vertexai import VertexAIInstrumentor -from opentelemetry.sdk._logs._internal.export.in_memory_log_exporter import ( - InMemoryLogExporter, -) + +# Backward compatibility for InMemoryLogExporter -> InMemoryLogRecordExporter rename +try: + from opentelemetry.sdk._logs._internal.export.in_memory_log_exporter import ( # pylint: disable=no-name-in-module + InMemoryLogRecordExporter, + ) +except ImportError: + # Fallback to old name for compatibility with older SDK versions + from opentelemetry.sdk._logs._internal.export.in_memory_log_exporter import ( + InMemoryLogExporter as InMemoryLogRecordExporter, + ) from opentelemetry.sdk.trace.export.in_memory_span_exporter import ( InMemorySpanExporter, ) -from tests.shared_test_utils import ( - ask_about_weather, - ask_about_weather_function_response, -) @pytest.mark.vcr() def test_function_call_choice( span_exporter: InMemorySpanExporter, - log_exporter: InMemoryLogExporter, + log_exporter: InMemoryLogRecordExporter, instrument_with_content: VertexAIInstrumentor, generate_content: callable, ): @@ -96,7 +104,7 @@ def test_function_call_choice( @pytest.mark.vcr() def test_function_call_choice_no_content( - log_exporter: InMemoryLogExporter, + log_exporter: InMemoryLogRecordExporter, instrument_no_content: VertexAIInstrumentor, generate_content: callable, ): @@ -136,7 +144,7 @@ def test_function_call_choice_no_content( @pytest.mark.vcr() def test_tool_events( span_exporter: InMemorySpanExporter, - log_exporter: InMemoryLogExporter, + log_exporter: InMemoryLogRecordExporter, instrument_with_content: VertexAIInstrumentor, generate_content: callable, ): @@ -228,7 +236,7 @@ def test_tool_events( @pytest.mark.vcr() def test_tool_events_no_content( span_exporter: InMemorySpanExporter, - log_exporter: InMemoryLogExporter, + log_exporter: InMemoryLogRecordExporter, instrument_no_content: VertexAIInstrumentor, generate_content: callable, ): diff --git a/instrumentation-genai/opentelemetry-instrumentation-vertexai/tests/test_function_calling_experimental.py b/instrumentation-genai/opentelemetry-instrumentation-vertexai/tests/test_function_calling_experimental.py index 9cfc0222a..e25136306 100644 --- a/instrumentation-genai/opentelemetry-instrumentation-vertexai/tests/test_function_calling_experimental.py +++ b/instrumentation-genai/opentelemetry-instrumentation-vertexai/tests/test_function_calling_experimental.py @@ -4,23 +4,31 @@ import fsspec import pytest +from tests.shared_test_utils import ( + ask_about_weather, + ask_about_weather_function_response, +) from opentelemetry.instrumentation.vertexai import VertexAIInstrumentor -from opentelemetry.sdk._logs._internal.export.in_memory_log_exporter import ( - InMemoryLogExporter, -) + +# Backward compatibility for InMemoryLogExporter -> InMemoryLogRecordExporter rename +try: + from opentelemetry.sdk._logs._internal.export.in_memory_log_exporter import ( # pylint: disable=no-name-in-module + InMemoryLogRecordExporter, + ) +except ImportError: + # Fallback to old name for compatibility with older SDK versions + from opentelemetry.sdk._logs._internal.export.in_memory_log_exporter import ( + InMemoryLogExporter as InMemoryLogRecordExporter, + ) from opentelemetry.sdk.trace.export.in_memory_span_exporter import ( InMemorySpanExporter, ) -from tests.shared_test_utils import ( - ask_about_weather, - ask_about_weather_function_response, -) def test_function_call_choice( span_exporter: InMemorySpanExporter, - log_exporter: InMemoryLogExporter, + log_exporter: InMemoryLogRecordExporter, instrument_with_experimental_semconvs: VertexAIInstrumentor, generate_content: callable, ): @@ -92,7 +100,7 @@ def test_function_call_choice( @pytest.mark.vcr() def test_function_call_choice_no_content( - log_exporter: InMemoryLogExporter, + log_exporter: InMemoryLogRecordExporter, instrument_no_content_with_experimental_semconvs: VertexAIInstrumentor, generate_content: callable, ): @@ -117,7 +125,7 @@ def test_function_call_choice_no_content( @pytest.mark.vcr() def test_tool_events( span_exporter: InMemorySpanExporter, - log_exporter: InMemoryLogExporter, + log_exporter: InMemoryLogRecordExporter, instrument_with_experimental_semconvs: VertexAIInstrumentor, generate_content: callable, ): @@ -216,7 +224,7 @@ def test_tool_events( @pytest.mark.vcr() def test_tool_events_no_content( span_exporter: InMemorySpanExporter, - log_exporter: InMemoryLogExporter, + log_exporter: InMemoryLogRecordExporter, instrument_no_content_with_experimental_semconvs: VertexAIInstrumentor, generate_content: callable, ): @@ -261,7 +269,7 @@ def assert_fsspec_equal(path: str, value: Any) -> None: @pytest.mark.vcr() def test_tool_events_with_completion_hook( span_exporter: InMemorySpanExporter, - log_exporter: InMemoryLogExporter, + log_exporter: InMemoryLogRecordExporter, instrument_with_upload_hook: VertexAIInstrumentor, generate_content: callable, ): diff --git a/instrumentation/opentelemetry-instrumentation-aiohttp-client/src/opentelemetry/instrumentation/aiohttp_client/__init__.py b/instrumentation/opentelemetry-instrumentation-aiohttp-client/src/opentelemetry/instrumentation/aiohttp_client/__init__.py index d6869bcd3..5db778a06 100644 --- a/instrumentation/opentelemetry-instrumentation-aiohttp-client/src/opentelemetry/instrumentation/aiohttp_client/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-aiohttp-client/src/opentelemetry/instrumentation/aiohttp_client/__init__.py @@ -137,7 +137,7 @@ def response_hook(span: Span, params: typing.Union[ from opentelemetry.instrumentation.aiohttp_client.version import __version__ from opentelemetry.instrumentation.instrumentor import BaseInstrumentor from opentelemetry.instrumentation.utils import ( - is_instrumentation_enabled, + is_http_instrumentation_enabled, unwrap, ) from opentelemetry.metrics import MeterProvider, get_meter @@ -287,8 +287,6 @@ def create_trace_config( explicit_bucket_boundaries_advisory=HTTP_DURATION_HISTOGRAM_BUCKETS_NEW, ) - metric_attributes = {} - excluded_urls = get_excluded_urls("AIOHTTP_CLIENT") def _end_trace(trace_config_ctx: types.SimpleNamespace): @@ -299,7 +297,7 @@ def _end_trace(trace_config_ctx: types.SimpleNamespace): if trace_config_ctx.duration_histogram_old is not None: duration_attrs_old = _filter_semconv_duration_attrs( - metric_attributes, + trace_config_ctx.metric_attributes, _client_duration_attrs_old, _client_duration_attrs_new, _StabilityMode.DEFAULT, @@ -310,7 +308,7 @@ def _end_trace(trace_config_ctx: types.SimpleNamespace): ) if trace_config_ctx.duration_histogram_new is not None: duration_attrs_new = _filter_semconv_duration_attrs( - metric_attributes, + trace_config_ctx.metric_attributes, _client_duration_attrs_old, _client_duration_attrs_new, _StabilityMode.HTTP, @@ -325,7 +323,7 @@ async def on_request_start( params: aiohttp.TraceRequestStartParams, ): if ( - not is_instrumentation_enabled() + not is_http_instrumentation_enabled() or trace_config_ctx.excluded_urls.url_disabled(str(params.url)) ): trace_config_ctx.span = None @@ -348,7 +346,7 @@ async def on_request_start( sem_conv_opt_in_mode, ) _set_http_method( - metric_attributes, + trace_config_ctx.metric_attributes, method, sanitize_method(method), sem_conv_opt_in_mode, @@ -359,12 +357,12 @@ async def on_request_start( parsed_url = urlparse(request_url) if parsed_url.hostname: _set_http_host_client( - metric_attributes, + trace_config_ctx.metric_attributes, parsed_url.hostname, sem_conv_opt_in_mode, ) _set_http_net_peer_name_client( - metric_attributes, + trace_config_ctx.metric_attributes, parsed_url.hostname, sem_conv_opt_in_mode, ) @@ -376,7 +374,9 @@ async def on_request_start( ) if parsed_url.port: _set_http_peer_port_client( - metric_attributes, parsed_url.port, sem_conv_opt_in_mode + trace_config_ctx.metric_attributes, + parsed_url.port, + sem_conv_opt_in_mode, ) if _report_new(sem_conv_opt_in_mode): _set_http_peer_port_client( @@ -411,7 +411,7 @@ async def on_request_end( _set_http_status_code_attribute( trace_config_ctx.span, params.response.status, - metric_attributes, + trace_config_ctx.metric_attributes, sem_conv_opt_in_mode, ) @@ -429,7 +429,7 @@ async def on_request_exception( exc_type = type(params.exception).__qualname__ if _report_new(sem_conv_opt_in_mode): trace_config_ctx.span.set_attribute(ERROR_TYPE, exc_type) - metric_attributes[ERROR_TYPE] = exc_type + trace_config_ctx.metric_attributes[ERROR_TYPE] = exc_type trace_config_ctx.span.set_status( Status(StatusCode.ERROR, exc_type) @@ -450,6 +450,7 @@ def _trace_config_ctx_factory(**kwargs): duration_histogram_old=duration_histogram_old, duration_histogram_new=duration_histogram_new, excluded_urls=excluded_urls, + metric_attributes={}, **kwargs, ) @@ -485,9 +486,6 @@ def _instrument( # pylint:disable=unused-argument def instrumented_init(wrapped, instance, args, kwargs): - if not is_instrumentation_enabled(): - return wrapped(*args, **kwargs) - client_trace_configs = list(kwargs.get("trace_configs") or []) client_trace_configs.extend(trace_configs) diff --git a/instrumentation/opentelemetry-instrumentation-aiohttp-client/tests/test_aiohttp_client_integration.py b/instrumentation/opentelemetry-instrumentation-aiohttp-client/tests/test_aiohttp_client_integration.py index 87d75afc7..f9a7325ea 100644 --- a/instrumentation/opentelemetry-instrumentation-aiohttp-client/tests/test_aiohttp_client_integration.py +++ b/instrumentation/opentelemetry-instrumentation-aiohttp-client/tests/test_aiohttp_client_integration.py @@ -40,7 +40,10 @@ from opentelemetry.instrumentation.aiohttp_client import ( AioHttpClientInstrumentor, ) -from opentelemetry.instrumentation.utils import suppress_instrumentation +from opentelemetry.instrumentation.utils import ( + suppress_http_instrumentation, + suppress_instrumentation, +) from opentelemetry.semconv._incubating.attributes.http_attributes import ( HTTP_HOST, HTTP_METHOD, @@ -828,6 +831,56 @@ async def request_handler(request): self._assert_spans([], 0) self._assert_metrics(0) + def test_metric_attributes_isolation(self): + async def success_handler(request): + assert "traceparent" in request.headers + return aiohttp.web.Response(status=HTTPStatus.OK) + + async def timeout_handler(request): + await asyncio.sleep(60) + assert "traceparent" in request.headers + return aiohttp.web.Response(status=HTTPStatus.OK) + + trace_config: aiohttp.TraceConfig = ( + aiohttp_client.create_trace_config() + ) + + success_host, success_port = self._http_request( + trace_config=trace_config, + url="/success", + request_handler=success_handler, + ) + + timeout_host, timeout_port = self._http_request( + trace_config=trace_config, + url="/timeout", + request_handler=timeout_handler, + timeout=aiohttp.ClientTimeout(sock_read=0.01), + ) + + metrics = self._assert_metrics(1) + duration_dp_attributes = [ + dict(dp.attributes) for dp in metrics[0].data.data_points + ] + self.assertEqual( + [ + { + HTTP_METHOD: "GET", + HTTP_HOST: success_host, + HTTP_STATUS_CODE: int(HTTPStatus.OK), + NET_PEER_NAME: success_host, + NET_PEER_PORT: success_port, + }, + { + HTTP_METHOD: "GET", + HTTP_HOST: timeout_host, + NET_PEER_NAME: timeout_host, + NET_PEER_PORT: timeout_port, + }, + ], + duration_dp_attributes, + ) + class TestAioHttpClientInstrumentor(TestBase): URL = "/test-path" @@ -1068,33 +1121,60 @@ async def uninstrument_request(server: aiohttp.test_utils.TestServer): self._assert_spans(1) def test_suppress_instrumentation(self): - with suppress_instrumentation(): - run_with_test_server( - self.get_default_request(), self.URL, self.default_handler - ) - self._assert_spans(0) + for suppress_ctx in ( + suppress_instrumentation, + suppress_http_instrumentation, + ): + with self.subTest(suppress_ctx=suppress_ctx.__name__): + with suppress_ctx(): + run_with_test_server( + self.get_default_request(), + self.URL, + self.default_handler, + ) + self._assert_spans(0) + self._assert_metrics(0) @staticmethod - async def suppressed_request(server: aiohttp.test_utils.TestServer): - async with aiohttp.test_utils.TestClient(server) as client: - with suppress_instrumentation(): - await client.get(TestAioHttpClientInstrumentor.URL) + def make_suppressed_request(suppress_ctx): + async def suppressed_request(server: aiohttp.test_utils.TestServer): + async with aiohttp.test_utils.TestClient(server) as client: + with suppress_ctx(): + await client.get(TestAioHttpClientInstrumentor.URL) + + return suppressed_request def test_suppress_instrumentation_after_creation(self): - run_with_test_server( - self.suppressed_request, self.URL, self.default_handler - ) - self._assert_spans(0) + for suppress_ctx in ( + suppress_instrumentation, + suppress_http_instrumentation, + ): + with self.subTest(suppress_ctx=suppress_ctx.__name__): + run_with_test_server( + self.make_suppressed_request(suppress_ctx), + self.URL, + self.default_handler, + ) + self._assert_spans(0) + self._assert_metrics(0) def test_suppress_instrumentation_with_server_exception(self): # pylint:disable=unused-argument async def raising_handler(request): raise aiohttp.web.HTTPFound(location=self.URL) - run_with_test_server( - self.suppressed_request, self.URL, raising_handler - ) - self._assert_spans(0) + for suppress_ctx in ( + suppress_instrumentation, + suppress_http_instrumentation, + ): + with self.subTest(suppress_ctx=suppress_ctx.__name__): + run_with_test_server( + self.make_suppressed_request(suppress_ctx), + self.URL, + raising_handler, + ) + self._assert_spans(0) + self._assert_metrics(0) def test_url_filter(self): def strip_query_params(url: yarl.URL) -> str: diff --git a/instrumentation/opentelemetry-instrumentation-aiokafka/src/opentelemetry/instrumentation/aiokafka/__init__.py b/instrumentation/opentelemetry-instrumentation-aiokafka/src/opentelemetry/instrumentation/aiokafka/__init__.py index 32837b23c..d4662c932 100644 --- a/instrumentation/opentelemetry-instrumentation-aiokafka/src/opentelemetry/instrumentation/aiokafka/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-aiokafka/src/opentelemetry/instrumentation/aiokafka/__init__.py @@ -95,7 +95,7 @@ async def produce(): from __future__ import annotations -from asyncio import iscoroutinefunction +from inspect import iscoroutinefunction from typing import TYPE_CHECKING, Collection import aiokafka diff --git a/instrumentation/opentelemetry-instrumentation-aiokafka/tests/test_instrumentation.py b/instrumentation/opentelemetry-instrumentation-aiokafka/tests/test_instrumentation.py index c00592036..8905c2a68 100644 --- a/instrumentation/opentelemetry-instrumentation-aiokafka/tests/test_instrumentation.py +++ b/instrumentation/opentelemetry-instrumentation-aiokafka/tests/test_instrumentation.py @@ -261,11 +261,21 @@ async def async_consume_hook(span, *_) -> None: async def test_getone_consume_hook(self) -> None: async_consume_hook_mock = mock.AsyncMock() + def is_async_consume_hook_mock(obj: Any) -> bool: + return obj is async_consume_hook_mock + AIOKafkaInstrumentor().uninstrument() - AIOKafkaInstrumentor().instrument( - tracer_provider=self.tracer_provider, - async_consume_hook=async_consume_hook_mock, - ) + + # TODO: remove mock.patch when we drop Python 3.9 support + with mock.patch( + "opentelemetry.instrumentation.aiokafka.iscoroutinefunction" + ) as iscoro: + iscoro.side_effect = is_async_consume_hook_mock + + AIOKafkaInstrumentor().instrument( + tracer_provider=self.tracer_provider, + async_consume_hook=async_consume_hook_mock, + ) consumer = await self.consumer_factory() self.addAsyncCleanup(consumer.stop) @@ -448,11 +458,20 @@ async def test_send_baggage(self) -> None: async def test_send_produce_hook(self) -> None: async_produce_hook_mock = mock.AsyncMock() + def is_async_produce_hook_mock(obj: Any) -> bool: + return obj is async_produce_hook_mock + AIOKafkaInstrumentor().uninstrument() - AIOKafkaInstrumentor().instrument( - tracer_provider=self.tracer_provider, - async_produce_hook=async_produce_hook_mock, - ) + # TODO: remove mock.patch when we drop Python 3.9 support + with mock.patch( + "opentelemetry.instrumentation.aiokafka.iscoroutinefunction" + ) as iscoro: + iscoro.side_effect = is_async_produce_hook_mock + + AIOKafkaInstrumentor().instrument( + tracer_provider=self.tracer_provider, + async_produce_hook=async_produce_hook_mock, + ) producer = await self.producer_factory() self.addAsyncCleanup(producer.stop) diff --git a/instrumentation/opentelemetry-instrumentation-aiopg/src/opentelemetry/instrumentation/aiopg/aiopg_integration.py b/instrumentation/opentelemetry-instrumentation-aiopg/src/opentelemetry/instrumentation/aiopg/aiopg_integration.py index 4e6257fbb..948050818 100644 --- a/instrumentation/opentelemetry-instrumentation-aiopg/src/opentelemetry/instrumentation/aiopg/aiopg_integration.py +++ b/instrumentation/opentelemetry-instrumentation-aiopg/src/opentelemetry/instrumentation/aiopg/aiopg_integration.py @@ -1,4 +1,4 @@ -import asyncio +import inspect import typing from collections.abc import Coroutine @@ -197,7 +197,7 @@ async def __aenter__(self): async def __aexit__(self, exc_type, exc, t_b): try: - if asyncio.iscoroutinefunction(self._obj.close): + if inspect.iscoroutinefunction(self._obj.close): await self._obj.close() else: self._obj.close() diff --git a/instrumentation/opentelemetry-instrumentation-asgi/src/opentelemetry/instrumentation/asgi/__init__.py b/instrumentation/opentelemetry-instrumentation-asgi/src/opentelemetry/instrumentation/asgi/__init__.py index bb232b39d..fb809e683 100644 --- a/instrumentation/opentelemetry-instrumentation-asgi/src/opentelemetry/instrumentation/asgi/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-asgi/src/opentelemetry/instrumentation/asgi/__init__.py @@ -258,6 +258,9 @@ def client_response_hook(span: Span, scope: Scope, message: dict[str, Any]): from opentelemetry.instrumentation.utils import _start_internal_or_server_span from opentelemetry.metrics import get_meter from opentelemetry.propagators.textmap import Getter, Setter +from opentelemetry.semconv._incubating.attributes.user_agent_attributes import ( + USER_AGENT_SYNTHETIC_TYPE, +) from opentelemetry.semconv._incubating.metrics.http_metrics import ( create_http_server_active_requests, create_http_server_request_body_size, @@ -276,6 +279,7 @@ def client_response_hook(span: Span, scope: Scope, message: dict[str, Any]): ExcludeList, SanitizeValue, _parse_url_query, + detect_synthetic_user_agent, get_custom_headers, normalise_request_header_name, normalise_response_header_name, @@ -397,7 +401,13 @@ def collect_request_attributes( ) http_user_agent = asgi_getter.get(scope, "user-agent") if http_user_agent: - _set_http_user_agent(result, http_user_agent[0], sem_conv_opt_in_mode) + user_agent_value = http_user_agent[0] + _set_http_user_agent(result, user_agent_value, sem_conv_opt_in_mode) + + # Check for synthetic user agent type + synthetic_type = detect_synthetic_user_agent(user_agent_value) + if synthetic_type: + result[USER_AGENT_SYNTHETIC_TYPE] = synthetic_type if "client" in scope and scope["client"] is not None: _set_http_peer_ip_server( diff --git a/instrumentation/opentelemetry-instrumentation-asgi/tests/test_asgi_middleware.py b/instrumentation/opentelemetry-instrumentation-asgi/tests/test_asgi_middleware.py index 0da3014f5..fdf328498 100644 --- a/instrumentation/opentelemetry-instrumentation-asgi/tests/test_asgi_middleware.py +++ b/instrumentation/opentelemetry-instrumentation-asgi/tests/test_asgi_middleware.py @@ -42,6 +42,9 @@ HistogramDataPoint, NumberDataPoint, ) +from opentelemetry.semconv._incubating.attributes.user_agent_attributes import ( + USER_AGENT_SYNTHETIC_TYPE, +) from opentelemetry.semconv.attributes.client_attributes import ( CLIENT_ADDRESS, CLIENT_PORT, @@ -883,6 +886,145 @@ def update_expected_user_agent(expected): new_sem_conv=True, ) + async def test_user_agent_synthetic_bot_detection(self): + """Test that bot user agents are detected as synthetic with type 'bot'""" + test_cases = [ + b"Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)", + b"Mozilla/5.0 (compatible; bingbot/2.0; +http://www.bing.com/bingbot.htm)", + b"googlebot/1.0", + b"bingbot/1.0", + ] + + # Test each user agent case separately to avoid span accumulation + for user_agent in test_cases: + with self.subTest(user_agent=user_agent): + # Clear headers first + self.scope["headers"] = [] + + def update_expected_synthetic_bot( + expected, ua: bytes = user_agent + ): + expected[3]["attributes"].update( + { + SpanAttributes.HTTP_USER_AGENT: ua.decode("utf8"), + USER_AGENT_SYNTHETIC_TYPE: "bot", + } + ) + return expected + + self.scope["headers"].append([b"user-agent", user_agent]) + app = otel_asgi.OpenTelemetryMiddleware(simple_asgi) + self.seed_app(app) + await self.send_default_request() + outputs = await self.get_all_output() + self.validate_outputs( + outputs, modifiers=[update_expected_synthetic_bot] + ) + + # Clear spans after each test case to prevent accumulation + self.memory_exporter.clear() + + async def test_user_agent_synthetic_test_detection(self): + """Test that test user agents are detected as synthetic with type 'test'""" + test_cases = [ + b"alwayson/1.0", + b"AlwaysOn/2.0", + b"test-alwayson-client", + ] + + # Test each user agent case separately to avoid span accumulation + for user_agent in test_cases: + with self.subTest(user_agent=user_agent): + # Clear headers first + self.scope["headers"] = [] + + def update_expected_synthetic_test( + expected, ua: bytes = user_agent + ): + expected[3]["attributes"].update( + { + SpanAttributes.HTTP_USER_AGENT: ua.decode("utf8"), + USER_AGENT_SYNTHETIC_TYPE: "test", + } + ) + return expected + + self.scope["headers"].append([b"user-agent", user_agent]) + app = otel_asgi.OpenTelemetryMiddleware(simple_asgi) + self.seed_app(app) + await self.send_default_request() + outputs = await self.get_all_output() + self.validate_outputs( + outputs, modifiers=[update_expected_synthetic_test] + ) + + # Clear spans after each test case to prevent accumulation + self.memory_exporter.clear() + + async def test_user_agent_non_synthetic(self): + """Test that normal user agents are not marked as synthetic""" + test_cases = [ + b"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36", + b"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.1.1 Safari/605.1.15", + b"PostmanRuntime/7.28.4", + b"curl/7.68.0", + ] + + # Test each user agent case separately to avoid span accumulation + for user_agent in test_cases: + with self.subTest(user_agent=user_agent): + # Clear headers first + self.scope["headers"] = [] + + def update_expected_non_synthetic( + expected, ua: bytes = user_agent + ): + # Should only have the user agent, not synthetic type + expected[3]["attributes"].update( + { + SpanAttributes.HTTP_USER_AGENT: ua.decode("utf8"), + } + ) + return expected + + self.scope["headers"].append([b"user-agent", user_agent]) + app = otel_asgi.OpenTelemetryMiddleware(simple_asgi) + self.seed_app(app) + await self.send_default_request() + outputs = await self.get_all_output() + self.validate_outputs( + outputs, modifiers=[update_expected_non_synthetic] + ) + + # Clear spans after each test case to prevent accumulation + self.memory_exporter.clear() + + async def test_user_agent_synthetic_new_semconv(self): + """Test synthetic user agent detection with new semantic conventions""" + user_agent = b"Mozilla/5.0 (compatible; Googlebot/2.1)" + + def update_expected_synthetic_new_semconv(expected): + expected[3]["attributes"].update( + { + USER_AGENT_ORIGINAL: user_agent.decode("utf8"), + USER_AGENT_SYNTHETIC_TYPE: "bot", + } + ) + return expected + + self.scope["headers"] = [] + self.scope["headers"].append([b"user-agent", user_agent]) + app = otel_asgi.OpenTelemetryMiddleware(simple_asgi) + self.seed_app(app) + await self.send_default_request() + outputs = await self.get_all_output() + self.validate_outputs( + outputs, + modifiers=[update_expected_synthetic_new_semconv], + old_sem_conv=False, + new_sem_conv=True, + ) + async def test_traceresponse_header(self): """Test a traceresponse header is sent when a global propagator is set.""" diff --git a/instrumentation/opentelemetry-instrumentation-asyncclick/tests/test_asyncclick.py b/instrumentation/opentelemetry-instrumentation-asyncclick/tests/test_asyncclick.py index d7104d505..989752f68 100644 --- a/instrumentation/opentelemetry-instrumentation-asyncclick/tests/test_asyncclick.py +++ b/instrumentation/opentelemetry-instrumentation-asyncclick/tests/test_asyncclick.py @@ -351,9 +351,9 @@ async def command1() -> None: async def command2() -> None: pass - async def run_both() -> ( - tuple[asyncclick.testing.Result, asyncclick.testing.Result] - ): + async def run_both() -> tuple[ + asyncclick.testing.Result, asyncclick.testing.Result + ]: runner = CliRunner() task1 = asyncio.create_task(runner.invoke(command1)) task2 = asyncio.create_task(runner.invoke(command2)) diff --git a/instrumentation/opentelemetry-instrumentation-botocore/tests/bedrock_utils.py b/instrumentation/opentelemetry-instrumentation-botocore/tests/bedrock_utils.py index 4f4613707..cd357f959 100644 --- a/instrumentation/opentelemetry-instrumentation-botocore/tests/bedrock_utils.py +++ b/instrumentation/opentelemetry-instrumentation-botocore/tests/bedrock_utils.py @@ -279,9 +279,9 @@ def remove_none_values(body): def assert_log_parent(log, span): if span: - assert ( - log.log_record.trace_id == span.get_span_context().trace_id - ), f"{span.get_span_context().trace_id} does not equal {log.log_record.trace_id}" + assert log.log_record.trace_id == span.get_span_context().trace_id, ( + f"{span.get_span_context().trace_id} does not equal {log.log_record.trace_id}" + ) assert log.log_record.span_id == span.get_span_context().span_id assert ( log.log_record.trace_flags == span.get_span_context().trace_flags diff --git a/instrumentation/opentelemetry-instrumentation-botocore/tests/conftest.py b/instrumentation/opentelemetry-instrumentation-botocore/tests/conftest.py index 36ed79baa..205c8083f 100644 --- a/instrumentation/opentelemetry-instrumentation-botocore/tests/conftest.py +++ b/instrumentation/opentelemetry-instrumentation-botocore/tests/conftest.py @@ -13,7 +13,7 @@ ) from opentelemetry.sdk._logs import LoggerProvider from opentelemetry.sdk._logs.export import ( - InMemoryLogExporter, + InMemoryLogRecordExporter, SimpleLogRecordProcessor, ) from opentelemetry.sdk.metrics import ( @@ -37,7 +37,7 @@ def fixture_span_exporter(): @pytest.fixture(scope="function", name="log_exporter") def fixture_log_exporter(): - exporter = InMemoryLogExporter() + exporter = InMemoryLogRecordExporter() yield exporter diff --git a/instrumentation/opentelemetry-instrumentation-django/src/opentelemetry/instrumentation/django/__init__.py b/instrumentation/opentelemetry-instrumentation-django/src/opentelemetry/instrumentation/django/__init__.py index 4a1f3feb9..d8bd93c05 100644 --- a/instrumentation/opentelemetry-instrumentation-django/src/opentelemetry/instrumentation/django/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-django/src/opentelemetry/instrumentation/django/__init__.py @@ -45,7 +45,7 @@ will exclude requests such as ``https://site/client/123/info`` and ``https://site/xyz/healthcheck``. Request attributes -******************** +****************** To extract attributes from Django's request object and use them as span attributes, set the environment variable ``OTEL_PYTHON_DJANGO_TRACED_REQUEST_ATTRS`` to a comma delimited list of request attribute names. @@ -57,10 +57,10 @@ will extract the ``path_info`` and ``content_type`` attributes from every traced request and add them as span attributes. -Django Request object reference: https://docs.djangoproject.com/en/3.1/ref/request-response/#attributes +* `Django Request object reference `_ Request and Response hooks -*************************** +************************** This instrumentation supports request and response hooks. These are functions that get called right after a span is created for a request and right before the span is finished for the response. The hooks can be configured as follows: @@ -77,8 +77,76 @@ def response_hook(span, request, response): DjangoInstrumentor().instrument(request_hook=request_hook, response_hook=response_hook) -Django Request object: https://docs.djangoproject.com/en/3.1/ref/request-response/#httprequest-objects -Django Response object: https://docs.djangoproject.com/en/3.1/ref/request-response/#httpresponse-objects +* `Django Request object `_ +* `Django Response object `_ + +Adding attributes from middleware context +######################################### +In many Django applications, certain request attributes become available only *after* +specific middlewares have executed. For example: + +- ``django.contrib.auth.middleware.AuthenticationMiddleware`` populates ``request.user`` +- ``django.contrib.sites.middleware.CurrentSiteMiddleware`` populates ``request.site`` + +Because the OpenTelemetry instrumentation creates the span **before** Django middlewares run, +these attributes are **not yet available** in the ``request_hook`` stage. + +Therefore, such attributes should be safely attached in the **response_hook**, which executes +after Django finishes processing the request (and after all middlewares have completed). + +Example: Attaching the authenticated user and current site to the span: + +.. code:: python + + def response_hook(span, request, response): + # Attach user information if available + if request.user.is_authenticated: + span.set_attribute("enduser.id", request.user.pk) + span.set_attribute("enduser.username", request.user.get_username()) + + # Attach current site (if provided by CurrentSiteMiddleware) + if hasattr(request, "site"): + span.set_attribute("site.id", getattr(request.site, "pk", None)) + span.set_attribute("site.domain", getattr(request.site, "domain", None)) + + DjangoInstrumentor().instrument(response_hook=response_hook) + +This ensures that middleware-dependent context (like user or site information) is properly +recorded once Django’s middleware stack has finished execution. + +Custom Django middleware can also attach arbitrary data to the ``request`` object, +which can later be included as span attributes in the ``response_hook``. + +* `Django middleware reference `_ + +Best practices +############## +- Use **response_hook** (not request_hook) when accessing attributes added by Django middlewares. +- Common middleware-provided attributes include: + + - ``request.user`` (AuthenticationMiddleware) + - ``request.site`` (CurrentSiteMiddleware) + +- Avoid adding large or sensitive data (e.g., passwords, session tokens, PII) to spans. +- Use **namespaced attribute keys**, e.g., ``enduser.*``, ``site.*``, or ``custom.*``, for clarity. +- Hooks should execute quickly — avoid blocking or long-running operations. +- Hooks can be safely combined with OpenTelemetry **Context propagation** or **Baggage** + for consistent tracing across services. + +* `OpenTelemetry semantic conventions `_ + +Middleware execution order +########################## +In Django’s request lifecycle, the OpenTelemetry `request_hook` is executed before +the first middleware runs. Therefore: + +- At `request_hook` time → only the bare `HttpRequest` object is available. +- After middlewares → `request.user`, `request.site` etc. become available. +- At `response_hook` time → all middlewares (including authentication and site middlewares) + have already run, making it the correct place to attach these attributes. + +Developers who need to trace attributes from middlewares should always use `response_hook` +to ensure complete and accurate span data. Capture HTTP request and response headers ***************************************** diff --git a/instrumentation/opentelemetry-instrumentation-fastapi/tests/test_fastapi_instrumentation.py b/instrumentation/opentelemetry-instrumentation-fastapi/tests/test_fastapi_instrumentation.py index 661c7097c..d60b169fe 100644 --- a/instrumentation/opentelemetry-instrumentation-fastapi/tests/test_fastapi_instrumentation.py +++ b/instrumentation/opentelemetry-instrumentation-fastapi/tests/test_fastapi_instrumentation.py @@ -287,9 +287,9 @@ def test_fastapi_unhandled_exception(self): """If the application has an unhandled error the instrumentation should capture that a 500 response is returned.""" try: resp = self._client.get("/error") - assert ( - resp.status_code == 500 - ), resp.content # pragma: no cover, for debugging this test if an exception is _not_ raised + assert resp.status_code == 500, ( + resp.content + ) # pragma: no cover, for debugging this test if an exception is _not_ raised except UnhandledException: pass else: @@ -2442,9 +2442,9 @@ def test_fastapi_unhandled_exception_both_semconv(self): """If the application has an unhandled error the instrumentation should capture that a 500 response is returned.""" try: resp = self._client.get("/error") - assert ( - resp.status_code == 500 - ), resp.content # pragma: no cover, for debugging this test if an exception is _not_ raised + assert resp.status_code == 500, ( + resp.content + ) # pragma: no cover, for debugging this test if an exception is _not_ raised except UnhandledException: pass else: diff --git a/instrumentation/opentelemetry-instrumentation-flask/src/opentelemetry/instrumentation/flask/__init__.py b/instrumentation/opentelemetry-instrumentation-flask/src/opentelemetry/instrumentation/flask/__init__.py index 0f023df21..8d0bf8a84 100644 --- a/instrumentation/opentelemetry-instrumentation-flask/src/opentelemetry/instrumentation/flask/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-flask/src/opentelemetry/instrumentation/flask/__init__.py @@ -342,6 +342,7 @@ def _rewrapped_app( sem_conv_opt_in_mode=_StabilityMode.DEFAULT, duration_histogram_new=None, ): + # pylint: disable=too-many-statements def _wrapped_app(wrapped_app_environ, start_response): # We want to measure the time for route matching, etc. # In theory, we could start the span here and use @@ -410,6 +411,11 @@ def _start_response(status, response_headers, *args, **kwargs): result = wsgi_app(wrapped_app_environ, _start_response) if should_trace: duration_s = default_timer() - start + # Get the span from wrapped_app_environ and re-create context manually + # to pass to histogram for exemplars generation + span = wrapped_app_environ.get(_ENVIRON_SPAN_KEY) + metrics_context = trace.set_span_in_context(span) + if duration_histogram_old: duration_attrs_old = otel_wsgi._parse_duration_attrs( attributes, _StabilityMode.DEFAULT @@ -418,9 +424,10 @@ def _start_response(status, response_headers, *args, **kwargs): if request_route: # http.target to be included in old semantic conventions duration_attrs_old[HTTP_TARGET] = str(request_route) - duration_histogram_old.record( - max(round(duration_s * 1000), 0), duration_attrs_old + max(round(duration_s * 1000), 0), + duration_attrs_old, + context=metrics_context, ) if duration_histogram_new: duration_attrs_new = otel_wsgi._parse_duration_attrs( @@ -431,7 +438,9 @@ def _start_response(status, response_headers, *args, **kwargs): duration_attrs_new[HTTP_ROUTE] = str(request_route) duration_histogram_new.record( - max(duration_s, 0), duration_attrs_new + max(duration_s, 0), + duration_attrs_new, + context=metrics_context, ) active_requests_counter.add(-1, active_requests_count_attrs) return result diff --git a/instrumentation/opentelemetry-instrumentation-flask/tests/test_programmatic.py b/instrumentation/opentelemetry-instrumentation-flask/tests/test_programmatic.py index 2da579add..922f2c1eb 100644 --- a/instrumentation/opentelemetry-instrumentation-flask/tests/test_programmatic.py +++ b/instrumentation/opentelemetry-instrumentation-flask/tests/test_programmatic.py @@ -810,6 +810,48 @@ def test_flask_metrics_excluded_urls_new_semconv(self): self.assertTrue(number_data_point_seen) self.assertFalse(histogram_data_point_seen) + def test_duration_histogram_old_record_with_context(self): + with patch("opentelemetry.trace.set_span_in_context") as mock_set_span: + self.client.get("/hello/123") + + # Verify that trace.set_span_in_context was called for metrics exemplar context + # with same trace, span ID as trace + mock_set_span.assert_called() + call_args = mock_set_span.call_args + self.assertEqual(len(call_args[0]), 1) + span_arg = call_args[0][0] + self.assertIsNotNone(span_arg) + finished_spans = self.memory_exporter.get_finished_spans() + self.assertEqual(len(finished_spans), 1) + finished_span = finished_spans[0] + self.assertEqual( + span_arg.context.trace_id, finished_span.context.trace_id + ) + self.assertEqual( + span_arg.context.span_id, finished_span.context.span_id + ) + + def test_duration_histogram_new_record_with_context_new_semconv(self): + with patch("opentelemetry.trace.set_span_in_context") as mock_set_span: + self.client.get("/hello/123") + + # Verify that trace.set_span_in_context was called for metrics exemplar context + # with same trace, span ID as trace + mock_set_span.assert_called() + call_args = mock_set_span.call_args + self.assertEqual(len(call_args[0]), 1) + span_arg = call_args[0][0] + self.assertIsNotNone(span_arg) + finished_spans = self.memory_exporter.get_finished_spans() + self.assertEqual(len(finished_spans), 1) + finished_span = finished_spans[0] + self.assertEqual( + span_arg.context.trace_id, finished_span.context.trace_id + ) + self.assertEqual( + span_arg.context.span_id, finished_span.context.span_id + ) + class TestProgrammaticHooks(InstrumentationTest, WsgiTestBase): def setUp(self): @@ -879,7 +921,7 @@ def response_hook_test(span, environ, response_headers): request_hook=request_hook_test, response_hook=response_hook_test ) # pylint: disable=import-outside-toplevel,reimported,redefined-outer-name - from flask import Flask + from flask import Flask # noqa: PLC0415 self.app = Flask(__name__) @@ -949,7 +991,7 @@ def setUp(self): FlaskInstrumentor().instrument(tracer_provider=tracer_provider) # pylint: disable=import-outside-toplevel,reimported,redefined-outer-name - from flask import Flask + from flask import Flask # noqa: PLC0415 self.app = Flask(__name__) diff --git a/instrumentation/opentelemetry-instrumentation-grpc/src/opentelemetry/instrumentation/grpc/__init__.py b/instrumentation/opentelemetry-instrumentation-grpc/src/opentelemetry/instrumentation/grpc/__init__.py index 37610d062..ee838a87e 100644 --- a/instrumentation/opentelemetry-instrumentation-grpc/src/opentelemetry/instrumentation/grpc/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-grpc/src/opentelemetry/instrumentation/grpc/__init__.py @@ -584,7 +584,7 @@ def client_interceptor( Returns: An invocation-side interceptor object. """ - from . import _client + from . import _client # noqa: PLC0415 tracer = trace.get_tracer( __name__, @@ -614,7 +614,7 @@ def server_interceptor(tracer_provider=None, filter_=None): Returns: A service-side interceptor object. """ - from . import _server + from . import _server # noqa: PLC0415 tracer = trace.get_tracer( __name__, @@ -637,7 +637,7 @@ def aio_client_interceptors( Returns: An invocation-side interceptor object. """ - from . import _aio_client + from . import _aio_client # noqa: PLC0415 tracer = trace.get_tracer( __name__, @@ -683,7 +683,7 @@ def aio_server_interceptor(tracer_provider=None, filter_=None): Returns: A service-side interceptor object. """ - from . import _aio_server + from . import _aio_server # noqa: PLC0415 tracer = trace.get_tracer( __name__, diff --git a/instrumentation/opentelemetry-instrumentation-grpc/src/opentelemetry/instrumentation/grpc/grpcext/__init__.py b/instrumentation/opentelemetry-instrumentation-grpc/src/opentelemetry/instrumentation/grpc/grpcext/__init__.py index d5e2549ba..298778f89 100644 --- a/instrumentation/opentelemetry-instrumentation-grpc/src/opentelemetry/instrumentation/grpc/grpcext/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-grpc/src/opentelemetry/instrumentation/grpc/grpcext/__init__.py @@ -112,7 +112,7 @@ def intercept_channel(channel, *interceptors): TypeError: If an interceptor derives from neither UnaryClientInterceptor nor StreamClientInterceptor. """ - from . import _interceptor + from . import _interceptor # noqa: PLC0415 return _interceptor.intercept_channel(channel, *interceptors) diff --git a/instrumentation/opentelemetry-instrumentation-httpx/src/opentelemetry/instrumentation/httpx/__init__.py b/instrumentation/opentelemetry-instrumentation-httpx/src/opentelemetry/instrumentation/httpx/__init__.py index 2a82a123f..67dc9aa96 100644 --- a/instrumentation/opentelemetry-instrumentation-httpx/src/opentelemetry/instrumentation/httpx/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-httpx/src/opentelemetry/instrumentation/httpx/__init__.py @@ -225,8 +225,8 @@ async def async_response_hook(span, request, response): import logging import typing -from asyncio import iscoroutinefunction from functools import partial +from inspect import iscoroutinefunction from timeit import default_timer from types import TracebackType diff --git a/instrumentation/opentelemetry-instrumentation-httpx/tests/test_httpx_integration.py b/instrumentation/opentelemetry-instrumentation-httpx/tests/test_httpx_integration.py index e6506d290..909ec3300 100644 --- a/instrumentation/opentelemetry-instrumentation-httpx/tests/test_httpx_integration.py +++ b/instrumentation/opentelemetry-instrumentation-httpx/tests/test_httpx_integration.py @@ -16,6 +16,7 @@ import abc import asyncio +import inspect import typing from unittest import mock @@ -1086,7 +1087,7 @@ def test_custom_tracer_provider(self): def test_response_hook(self): response_hook_key = ( "async_response_hook" - if asyncio.iscoroutinefunction(self.response_hook) + if inspect.iscoroutinefunction(self.response_hook) else "response_hook" ) response_hook_kwargs = {response_hook_key: self.response_hook} @@ -1133,7 +1134,7 @@ def test_response_hook_sync_async_kwargs(self): def test_request_hook(self): request_hook_key = ( "async_request_hook" - if asyncio.iscoroutinefunction(self.request_hook) + if inspect.iscoroutinefunction(self.request_hook) else "request_hook" ) request_hook_kwargs = {request_hook_key: self.request_hook} diff --git a/instrumentation/opentelemetry-instrumentation-pika/tests/test_pika_instrumentation.py b/instrumentation/opentelemetry-instrumentation-pika/tests/test_pika_instrumentation.py index 2733140ff..f2a82a4d3 100644 --- a/instrumentation/opentelemetry-instrumentation-pika/tests/test_pika_instrumentation.py +++ b/instrumentation/opentelemetry-instrumentation-pika/tests/test_pika_instrumentation.py @@ -59,9 +59,9 @@ def test_instrument_api(self) -> None: _QueueConsumerGeneratorInfo.__init__, BoundFunctionWrapper ) ) - assert hasattr( - instrumentation, "__opentelemetry_tracer_provider" - ), "Tracer not stored for the object!" + assert hasattr(instrumentation, "__opentelemetry_tracer_provider"), ( + "Tracer not stored for the object!" + ) instrumentation.uninstrument() self.assertFalse( isinstance(BlockingConnection.channel, BoundFunctionWrapper) @@ -115,9 +115,9 @@ def test_instrument_channel( instrument_channel_functions: mock.MagicMock, ): PikaInstrumentor.instrument_channel(channel=self.channel) - assert hasattr( - self.channel, "_is_instrumented_by_opentelemetry" - ), "channel is not marked as instrumented!" + assert hasattr(self.channel, "_is_instrumented_by_opentelemetry"), ( + "channel is not marked as instrumented!" + ) instrument_channel_consumers.assert_called_once() instrument_basic_consume.assert_called_once() instrument_channel_functions.assert_called_once() diff --git a/instrumentation/opentelemetry-instrumentation-pymemcache/tests/test_pymemcache.py b/instrumentation/opentelemetry-instrumentation-pymemcache/tests/test_pymemcache.py index f88800901..b9ad94881 100644 --- a/instrumentation/opentelemetry-instrumentation-pymemcache/tests/test_pymemcache.py +++ b/instrumentation/opentelemetry-instrumentation-pymemcache/tests/test_pymemcache.py @@ -552,7 +552,7 @@ def make_client(self, *mock_socket_values, **kwargs): current_port = TEST_PORT # pylint: disable=import-outside-toplevel - from pymemcache.client.hash import HashClient + from pymemcache.client.hash import HashClient # noqa: PLC0415 # pylint: disable=attribute-defined-outside-init self.client = HashClient([], **kwargs) diff --git a/instrumentation/opentelemetry-instrumentation-pymongo/src/opentelemetry/instrumentation/pymongo/__init__.py b/instrumentation/opentelemetry-instrumentation-pymongo/src/opentelemetry/instrumentation/pymongo/__init__.py index 7ada8d789..2a9eeed28 100644 --- a/instrumentation/opentelemetry-instrumentation-pymongo/src/opentelemetry/instrumentation/pymongo/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-pymongo/src/opentelemetry/instrumentation/pymongo/__init__.py @@ -138,7 +138,7 @@ def started(self, event: monitoring.CommandStartedEvent): command_name = event.command_name span_name = f"{event.database_name}.{command_name}" statement = self._get_statement_by_command_name(command_name, event) - collection = event.command.get(event.command_name) + collection = _get_command_collection_name(event) try: span = self._tracer.start_span(span_name, kind=SpanKind.CLIENT) @@ -198,7 +198,12 @@ def failed(self, event: monitoring.CommandFailedEvent): if span is None: return if span.is_recording(): - span.set_status(Status(StatusCode.ERROR, event.failure)) + span.set_status( + Status( + StatusCode.ERROR, + event.failure.get("errmsg", "Unknown error"), + ) + ) try: self.failed_hook(span, event) except ( @@ -221,6 +226,13 @@ def _get_statement_by_command_name( return statement +def _get_command_collection_name(event: CommandEvent) -> str | None: + collection_name = event.command.get(event.command_name) + if not collection_name or not isinstance(collection_name, str): + return None + return collection_name + + def _get_span_dict_key( event: CommandEvent, ) -> int | tuple[int, tuple[str, int | None]]: diff --git a/instrumentation/opentelemetry-instrumentation-pymongo/tests/test_pymongo.py b/instrumentation/opentelemetry-instrumentation-pymongo/tests/test_pymongo.py index 8b082a4a1..06c5d5bb8 100644 --- a/instrumentation/opentelemetry-instrumentation-pymongo/tests/test_pymongo.py +++ b/instrumentation/opentelemetry-instrumentation-pymongo/tests/test_pymongo.py @@ -127,6 +127,7 @@ def test_failed(self): failed_hook=self.failed_callback, ) command_tracer.started(event=mock_event) + mock_event.mark_as_failed() command_tracer.failed(event=mock_event) spans_list = self.memory_exporter.get_finished_spans() @@ -137,7 +138,7 @@ def test_failed(self): span.status.status_code, trace_api.StatusCode.ERROR, ) - self.assertEqual(span.status.description, "failure") + self.assertEqual(span.status.description, "operation failed") self.assertIsNotNone(span.end_time) self.start_callback.assert_called_once() self.failed_callback.assert_called_once() @@ -149,6 +150,7 @@ def test_multiple_commands(self): command_tracer.started(event=first_mock_event) command_tracer.started(event=second_mock_event) command_tracer.succeeded(event=first_mock_event) + second_mock_event.mark_as_failed() command_tracer.failed(event=second_mock_event) spans_list = self.memory_exporter.get_finished_spans() @@ -276,6 +278,43 @@ def test_capture_statement_disabled_aggregate(self): span.attributes[SpanAttributes.DB_STATEMENT], "aggregate" ) + def test_collection_name_attribute(self): + scenarios = [ + ( + { + "command_name": "find", + "find": "test_collection", + }, + "test_collection", + ), + ({"command_name": "find"}, None), + ({"command_name": "find", "find": b"invalid"}, None), + ] + for command_attrs, expected in scenarios: + with self.subTest(command_attrs=command_attrs, expected=expected): + mock_event = MockEvent(command_attrs) + + command_tracer = CommandTracer( + self.tracer, capture_statement=True + ) + command_tracer.started(event=mock_event) + command_tracer.succeeded(event=mock_event) + + spans_list = self.memory_exporter.get_finished_spans() + + self.assertEqual(len(spans_list), 1) + span = spans_list[0] + + self.assertEqual( + span.attributes[SpanAttributes.DB_STATEMENT], "find" + ) + + self.assertEqual( + span.attributes.get(SpanAttributes.DB_MONGODB_COLLECTION), + expected, + ) + self.memory_exporter.clear() + class MockCommand: def __init__(self, command_attrs): @@ -291,6 +330,16 @@ def __init__(self, command_attrs, connection_id=None, request_id=""): self.command_name = self.command.get("command_name") self.connection_id = connection_id self.request_id = request_id + self.failure = None + + def mark_as_failed(self): + # CommandFailedEvent.failure is type _DocumentOut, which pymongo defines as: + # ``` + # _DocumentOut = Union[MutableMapping[str, Any], "RawBSONDocument"] + # ``` + # we go with the former, but both provide a `.get(key, default)` method. + # + self.failure = {"errmsg": "operation failed"} def __getattr__(self, item): return item diff --git a/instrumentation/opentelemetry-instrumentation-redis/src/opentelemetry/instrumentation/redis/__init__.py b/instrumentation/opentelemetry-instrumentation-redis/src/opentelemetry/instrumentation/redis/__init__.py index ef61ecec2..8a4f93329 100644 --- a/instrumentation/opentelemetry-instrumentation-redis/src/opentelemetry/instrumentation/redis/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-redis/src/opentelemetry/instrumentation/redis/__init__.py @@ -110,6 +110,35 @@ def response_hook(span, instance, response): client = redis.StrictRedis(host="localhost", port=6379) client.get("my-key") +Suppress Instrumentation +------------------------ + +You can use the ``suppress_instrumentation`` context manager to prevent instrumentation +from being applied to specific Redis operations. This is useful when you want to avoid +creating spans for internal operations, health checks, or during specific code paths. + +.. code:: python + + from opentelemetry.instrumentation.redis import RedisInstrumentor + from opentelemetry.instrumentation.utils import suppress_instrumentation + import redis + + # Instrument redis + RedisInstrumentor().instrument() + + client = redis.StrictRedis(host="localhost", port=6379) + + # This will report a span + client.get("my-key") + + # This will NOT report a span + with suppress_instrumentation(): + client.get("internal-key") + client.set("cache-key", "value") + + # This will report a span again + client.get("another-key") + API --- """ @@ -134,7 +163,10 @@ def response_hook(span, instance, response): _set_connection_attributes, ) from opentelemetry.instrumentation.redis.version import __version__ -from opentelemetry.instrumentation.utils import unwrap +from opentelemetry.instrumentation.utils import ( + is_instrumentation_enabled, + unwrap, +) from opentelemetry.semconv._incubating.attributes.db_attributes import ( DB_STATEMENT, ) @@ -196,6 +228,9 @@ def _traced_execute_command( args: tuple[Any, ...], kwargs: dict[str, Any], ) -> R: + if not is_instrumentation_enabled(): + return func(*args, **kwargs) + query = _format_command_args(args) name = _build_span_name(instance, args) with tracer.start_as_current_span( @@ -231,6 +266,9 @@ def _traced_execute_pipeline( args: tuple[Any, ...], kwargs: dict[str, Any], ) -> R: + if not is_instrumentation_enabled(): + return func(*args, **kwargs) + ( command_stack, resource, @@ -276,6 +314,9 @@ async def _async_traced_execute_command( args: tuple[Any, ...], kwargs: dict[str, Any], ) -> Awaitable[R]: + if not is_instrumentation_enabled(): + return await func(*args, **kwargs) + query = _format_command_args(args) name = _build_span_name(instance, args) @@ -307,6 +348,9 @@ async def _async_traced_execute_pipeline( args: tuple[Any, ...], kwargs: dict[str, Any], ) -> Awaitable[R]: + if not is_instrumentation_enabled(): + return await func(*args, **kwargs) + ( command_stack, resource, diff --git a/instrumentation/opentelemetry-instrumentation-redis/src/opentelemetry/instrumentation/redis/custom_types.py b/instrumentation/opentelemetry-instrumentation-redis/src/opentelemetry/instrumentation/redis/custom_types.py index 6c67f1ac2..2e8850422 100644 --- a/instrumentation/opentelemetry-instrumentation-redis/src/opentelemetry/instrumentation/redis/custom_types.py +++ b/instrumentation/opentelemetry-instrumentation-redis/src/opentelemetry/instrumentation/redis/custom_types.py @@ -1,3 +1,17 @@ +# 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. + from __future__ import annotations from typing import Any, Callable, TypeVar diff --git a/instrumentation/opentelemetry-instrumentation-redis/src/opentelemetry/instrumentation/redis/util.py b/instrumentation/opentelemetry-instrumentation-redis/src/opentelemetry/instrumentation/redis/util.py index 03cc0a5dc..320758f84 100644 --- a/instrumentation/opentelemetry-instrumentation-redis/src/opentelemetry/instrumentation/redis/util.py +++ b/instrumentation/opentelemetry-instrumentation-redis/src/opentelemetry/instrumentation/redis/util.py @@ -207,4 +207,4 @@ def _build_span_meta_data_for_pipeline( resource = "" span_name = "" - return command_stack, resource, span_name + return command_stack, resource, span_name or "redis" diff --git a/instrumentation/opentelemetry-instrumentation-redis/tests/test_redis.py b/instrumentation/opentelemetry-instrumentation-redis/tests/test_redis.py index 7d5a3b3dc..3e649fcef 100644 --- a/instrumentation/opentelemetry-instrumentation-redis/tests/test_redis.py +++ b/instrumentation/opentelemetry-instrumentation-redis/tests/test_redis.py @@ -25,6 +25,7 @@ from opentelemetry import trace from opentelemetry.instrumentation.redis import RedisInstrumentor +from opentelemetry.instrumentation.utils import suppress_instrumentation from opentelemetry.semconv._incubating.attributes.db_attributes import ( DB_REDIS_DATABASE_INDEX, DB_SYSTEM, @@ -40,6 +41,7 @@ from opentelemetry.trace import SpanKind +# pylint: disable=too-many-public-methods class TestRedis(TestBase): def assert_span_count(self, count: int): """ @@ -390,6 +392,86 @@ def redis_operations(): self.assertEqual(span.kind, SpanKind.CLIENT) self.assertEqual(span.status.status_code, trace.StatusCode.UNSET) + def test_span_name_empty_pipeline(self): + redis_client = fakeredis.FakeStrictRedis() + pipe = redis_client.pipeline() + pipe.execute() + + spans = self.memory_exporter.get_finished_spans() + self.assertEqual(len(spans), 1) + self.assertEqual(spans[0].name, "redis") + self.assertEqual(spans[0].kind, SpanKind.CLIENT) + self.assertEqual(spans[0].status.status_code, trace.StatusCode.UNSET) + + def test_suppress_instrumentation_command(self): + redis_client = redis.Redis() + + with mock.patch.object(redis_client, "connection"): + # Execute command with suppression + with suppress_instrumentation(): + redis_client.get("key") + + # No spans should be created + spans = self.memory_exporter.get_finished_spans() + self.assertEqual(len(spans), 0) + + # Verify that instrumentation works again after exiting the context + with mock.patch.object(redis_client, "connection"): + redis_client.get("key") + + spans = self.memory_exporter.get_finished_spans() + self.assertEqual(len(spans), 1) + + def test_suppress_instrumentation_pipeline(self): + redis_client = fakeredis.FakeStrictRedis() + + with suppress_instrumentation(): + pipe = redis_client.pipeline() + pipe.set("key1", "value1") + pipe.set("key2", "value2") + pipe.get("key1") + pipe.execute() + + # No spans should be created + spans = self.memory_exporter.get_finished_spans() + self.assertEqual(len(spans), 0) + + # Verify that instrumentation works again after exiting the context + pipe = redis_client.pipeline() + pipe.set("key3", "value3") + pipe.execute() + + spans = self.memory_exporter.get_finished_spans() + self.assertEqual(len(spans), 1) + # Pipeline span could be "SET" or "redis.pipeline" depending on implementation + self.assertIn(spans[0].name, ["SET", "redis.pipeline"]) + + def test_suppress_instrumentation_mixed(self): + redis_client = redis.Redis() + + # Regular instrumented call + with mock.patch.object(redis_client, "connection"): + redis_client.set("key1", "value1") + + spans = self.memory_exporter.get_finished_spans() + self.assertEqual(len(spans), 1) + self.memory_exporter.clear() + + # Suppressed call + with suppress_instrumentation(): + with mock.patch.object(redis_client, "connection"): + redis_client.set("key2", "value2") + + spans = self.memory_exporter.get_finished_spans() + self.assertEqual(len(spans), 0) + + # Another regular instrumented call + with mock.patch.object(redis_client, "connection"): + redis_client.get("key1") + + spans = self.memory_exporter.get_finished_spans() + self.assertEqual(len(spans), 1) + class TestRedisAsync(TestBase, IsolatedAsyncioTestCase): def assert_span_count(self, count: int): @@ -543,6 +625,86 @@ def response_hook(span, conn, args): await self.client.set("key", "value") spans = self.assert_span_count(0) + @pytest.mark.asyncio + async def test_span_name_empty_pipeline(self): + redis_client = fakeredis.aioredis.FakeRedis() + self.instrumentor.instrument_client( + client=redis_client, tracer_provider=self.tracer_provider + ) + async with redis_client.pipeline() as pipe: + await pipe.execute() + + spans = self.memory_exporter.get_finished_spans() + self.assertEqual(len(spans), 1) + self.assertEqual(spans[0].name, "redis") + self.assertEqual(spans[0].kind, SpanKind.CLIENT) + self.assertEqual(spans[0].status.status_code, trace.StatusCode.UNSET) + self.instrumentor.uninstrument_client(client=redis_client) + + @pytest.mark.asyncio + async def test_suppress_instrumentation_async_command(self): + self.instrumentor.instrument(tracer_provider=self.tracer_provider) + redis_client = FakeRedis() + + # Execute command with suppression + with suppress_instrumentation(): + await redis_client.get("key") + + # No spans should be created + self.assert_span_count(0) + + # Verify that instrumentation works again after exiting the context + await redis_client.set("key", "value") + self.assert_span_count(1) + self.instrumentor.uninstrument() + + @pytest.mark.asyncio + async def test_suppress_instrumentation_async_pipeline(self): + self.instrumentor.instrument(tracer_provider=self.tracer_provider) + redis_client = FakeRedis() + + # Execute pipeline with suppression + with suppress_instrumentation(): + async with redis_client.pipeline() as pipe: + await pipe.set("key1", "value1") + await pipe.set("key2", "value2") + await pipe.get("key1") + await pipe.execute() + + # No spans should be created + self.assert_span_count(0) + + # Verify that instrumentation works again after exiting the context + async with redis_client.pipeline() as pipe: + await pipe.set("key3", "value3") + await pipe.execute() + + spans = self.assert_span_count(1) + # Pipeline span could be "SET" or "redis.pipeline" depending on implementation + self.assertIn(spans[0].name, ["SET", "redis.pipeline"]) + self.instrumentor.uninstrument() + + @pytest.mark.asyncio + async def test_suppress_instrumentation_async_mixed(self): + self.instrumentor.instrument(tracer_provider=self.tracer_provider) + redis_client = FakeRedis() + + # Regular instrumented call + await redis_client.set("key1", "value1") + self.assert_span_count(1) + self.memory_exporter.clear() + + # Suppressed call + with suppress_instrumentation(): + await redis_client.set("key2", "value2") + + self.assert_span_count(0) + + # Another regular instrumented call + await redis_client.get("key1") + self.assert_span_count(1) + self.instrumentor.uninstrument() + class TestRedisInstance(TestBase): def assert_span_count(self, count: int): diff --git a/instrumentation/opentelemetry-instrumentation-requests/src/opentelemetry/instrumentation/requests/__init__.py b/instrumentation/opentelemetry-instrumentation-requests/src/opentelemetry/instrumentation/requests/__init__.py index 7cfc3a4fe..d834c1bb6 100644 --- a/instrumentation/opentelemetry-instrumentation-requests/src/opentelemetry/instrumentation/requests/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-requests/src/opentelemetry/instrumentation/requests/__init__.py @@ -132,11 +132,17 @@ def response_hook(span, request_obj, response): ) from opentelemetry.metrics import Histogram, get_meter from opentelemetry.propagate import inject +from opentelemetry.semconv._incubating.attributes.user_agent_attributes import ( + USER_AGENT_SYNTHETIC_TYPE, +) from opentelemetry.semconv.attributes.error_attributes import ERROR_TYPE from opentelemetry.semconv.attributes.network_attributes import ( NETWORK_PEER_ADDRESS, NETWORK_PEER_PORT, ) +from opentelemetry.semconv.attributes.user_agent_attributes import ( + USER_AGENT_ORIGINAL, +) from opentelemetry.semconv.metrics import MetricInstruments from opentelemetry.semconv.metrics.http_metrics import ( HTTP_CLIENT_REQUEST_DURATION, @@ -145,6 +151,7 @@ def response_hook(span, request_obj, response): from opentelemetry.trace.span import Span from opentelemetry.util.http import ( ExcludeList, + detect_synthetic_user_agent, get_excluded_urls, parse_excluded_urls, redact_url, @@ -243,6 +250,15 @@ def get_or_create_headers(): ) _set_http_url(span_attributes, url, sem_conv_opt_in_mode) + # Check for synthetic user agent type + headers = get_or_create_headers() + user_agent = headers.get("User-Agent") + synthetic_type = detect_synthetic_user_agent(user_agent) + if synthetic_type: + span_attributes[USER_AGENT_SYNTHETIC_TYPE] = synthetic_type + if user_agent: + span_attributes[USER_AGENT_ORIGINAL] = user_agent + metric_labels = {} _set_http_method( metric_labels, @@ -297,7 +313,6 @@ def get_or_create_headers(): if callable(request_hook): request_hook(span, request) - headers = get_or_create_headers() inject(headers) with suppress_http_instrumentation(): diff --git a/instrumentation/opentelemetry-instrumentation-requests/tests/test_requests_integration.py b/instrumentation/opentelemetry-instrumentation-requests/tests/test_requests_integration.py index ac3d41294..ac9c0529f 100644 --- a/instrumentation/opentelemetry-instrumentation-requests/tests/test_requests_integration.py +++ b/instrumentation/opentelemetry-instrumentation-requests/tests/test_requests_integration.py @@ -63,6 +63,9 @@ SERVER_PORT, ) from opentelemetry.semconv.attributes.url_attributes import URL_FULL +from opentelemetry.semconv.attributes.user_agent_attributes import ( + USER_AGENT_ORIGINAL, +) from opentelemetry.test.mock_textmap import MockTextMapPropagator from opentelemetry.test.test_base import TestBase from opentelemetry.trace import StatusCode @@ -175,6 +178,7 @@ def test_basic(self): HTTP_METHOD: "GET", HTTP_URL: self.URL, HTTP_STATUS_CODE: 200, + USER_AGENT_ORIGINAL: "python-requests/2.32.3", }, ) @@ -211,6 +215,7 @@ def test_basic_new_semconv(self): NETWORK_PROTOCOL_VERSION: "1.1", SERVER_PORT: 80, NETWORK_PEER_PORT: 80, + USER_AGENT_ORIGINAL: "python-requests/2.32.3", }, ) @@ -253,6 +258,7 @@ def test_basic_both_semconv(self): NETWORK_PROTOCOL_VERSION: "1.1", SERVER_PORT: 80, NETWORK_PEER_PORT: 80, + USER_AGENT_ORIGINAL: "python-requests/2.32.3", }, ) @@ -276,6 +282,7 @@ def test_nonstandard_http_method(self): HTTP_METHOD: "_OTHER", HTTP_URL: self.URL, HTTP_STATUS_CODE: 405, + USER_AGENT_ORIGINAL: "python-requests/2.32.3", }, ) @@ -300,6 +307,7 @@ def test_nonstandard_http_method_new_semconv(self): NETWORK_PROTOCOL_VERSION: "1.1", ERROR_TYPE: "405", HTTP_REQUEST_METHOD_ORIGINAL: "NONSTANDARD", + USER_AGENT_ORIGINAL: "python-requests/2.32.3", }, ) self.assertIs(span.status.status_code, trace.StatusCode.ERROR) @@ -534,6 +542,7 @@ def response_hook( HTTP_URL: self.URL, HTTP_STATUS_CODE: 200, "http.response.body": "Hello!", + USER_AGENT_ORIGINAL: "python-requests/2.32.3", }, ) @@ -564,6 +573,7 @@ def test_requests_exception_without_response(self, *_, **__): { HTTP_METHOD: "GET", HTTP_URL: self.URL, + USER_AGENT_ORIGINAL: "python-requests/2.32.3", }, ) self.assertEqual(span.status.status_code, StatusCode.ERROR) @@ -591,6 +601,7 @@ def test_requests_exception_new_semconv(self, *_, **__): NETWORK_PEER_PORT: 80, NETWORK_PEER_ADDRESS: "mock", ERROR_TYPE: "RequestException", + USER_AGENT_ORIGINAL: "python-requests/2.32.3", }, ) self.assertEqual(span.status.status_code, StatusCode.ERROR) @@ -613,6 +624,7 @@ def test_requests_exception_without_proper_response_type(self, *_, **__): { HTTP_METHOD: "GET", HTTP_URL: self.URL, + USER_AGENT_ORIGINAL: "python-requests/2.32.3", }, ) self.assertEqual(span.status.status_code, StatusCode.ERROR) @@ -636,6 +648,7 @@ def test_requests_exception_with_response(self, *_, **__): HTTP_METHOD: "GET", HTTP_URL: self.URL, HTTP_STATUS_CODE: 500, + USER_AGENT_ORIGINAL: "python-requests/2.32.3", }, ) self.assertEqual(span.status.status_code, StatusCode.ERROR) @@ -675,6 +688,7 @@ def test_adapter_with_custom_response(self): "http.method": "GET", "http.url": self.URL, "http.status_code": 210, + USER_AGENT_ORIGINAL: "python-requests/2.32.3", }, ) diff --git a/instrumentation/opentelemetry-instrumentation-requests/tests/test_user_agent_synthetic.py b/instrumentation/opentelemetry-instrumentation-requests/tests/test_user_agent_synthetic.py new file mode 100644 index 000000000..4adcc2146 --- /dev/null +++ b/instrumentation/opentelemetry-instrumentation-requests/tests/test_user_agent_synthetic.py @@ -0,0 +1,167 @@ +# 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. + +import httpretty +import requests + +from opentelemetry.instrumentation.requests import RequestsInstrumentor +from opentelemetry.semconv._incubating.attributes.user_agent_attributes import ( + USER_AGENT_SYNTHETIC_TYPE, + UserAgentSyntheticTypeValues, +) +from opentelemetry.test.test_base import TestBase + + +class TestUserAgentSynthetic(TestBase): + URL = "http://mock/status/200" + + def setUp(self): + super().setUp() + RequestsInstrumentor().instrument() + httpretty.enable() + httpretty.register_uri(httpretty.GET, self.URL, body="Hello!") + + def tearDown(self): + super().tearDown() + RequestsInstrumentor().uninstrument() + httpretty.disable() + + def assert_span(self, num_spans=1): + span_list = self.memory_exporter.get_finished_spans() + self.assertEqual(num_spans, len(span_list)) + if num_spans == 0: + return None + if num_spans == 1: + return span_list[0] + return span_list + + def test_user_agent_bot_googlebot(self): + """Test that googlebot user agent is marked as 'bot'""" + headers = { + "User-Agent": "Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)" + } + requests.get(self.URL, headers=headers, timeout=5) + + span = self.assert_span() + self.assertEqual( + span.attributes.get(USER_AGENT_SYNTHETIC_TYPE), + UserAgentSyntheticTypeValues.BOT.value, + ) + + def test_user_agent_bot_bingbot(self): + """Test that bingbot user agent is marked as 'bot'""" + headers = { + "User-Agent": "Mozilla/5.0 (compatible; bingbot/2.0; +http://www.bing.com/bingbot.htm)" + } + requests.get(self.URL, headers=headers, timeout=5) + + span = self.assert_span() + self.assertEqual( + span.attributes.get(USER_AGENT_SYNTHETIC_TYPE), + UserAgentSyntheticTypeValues.BOT.value, + ) + + def test_user_agent_test_alwayson(self): + """Test that alwayson user agent is marked as 'test'""" + headers = {"User-Agent": "AlwaysOn-Monitor/1.0"} + requests.get(self.URL, headers=headers, timeout=5) + + span = self.assert_span() + self.assertEqual( + span.attributes.get(USER_AGENT_SYNTHETIC_TYPE), + UserAgentSyntheticTypeValues.TEST.value, + ) + + def test_user_agent_case_insensitive(self): + """Test that detection is case insensitive""" + headers = {"User-Agent": "GOOGLEBOT/2.1"} + requests.get(self.URL, headers=headers, timeout=5) + + span = self.assert_span() + self.assertEqual( + span.attributes.get(USER_AGENT_SYNTHETIC_TYPE), + UserAgentSyntheticTypeValues.BOT.value, + ) + + self.memory_exporter.clear() + + headers = {"User-Agent": "ALWAYSON-Monitor/1.0"} + requests.get(self.URL, headers=headers, timeout=5) + + span = self.assert_span() + self.assertEqual( + span.attributes.get(USER_AGENT_SYNTHETIC_TYPE), + UserAgentSyntheticTypeValues.TEST.value, + ) + + def test_user_agent_normal_browser(self): + """Test that normal browser user agents don't get synthetic type""" + headers = { + "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36" + } + requests.get(self.URL, headers=headers, timeout=5) + + span = self.assert_span() + self.assertNotIn(USER_AGENT_SYNTHETIC_TYPE, span.attributes) + + def test_no_user_agent_header(self): + """Test that requests without user agent don't get synthetic type""" + requests.get(self.URL, timeout=5) + + span = self.assert_span() + self.assertNotIn(USER_AGENT_SYNTHETIC_TYPE, span.attributes) + + def test_empty_user_agent_header(self): + """Test that empty user agent doesn't get synthetic type""" + headers = {"User-Agent": ""} + requests.get(self.URL, headers=headers, timeout=5) + + span = self.assert_span() + self.assertNotIn(USER_AGENT_SYNTHETIC_TYPE, span.attributes) + + def test_user_agent_substring_match(self): + """Test that substrings are detected correctly""" + # Test googlebot in middle of string + headers = {"User-Agent": "MyApp/1.0 googlebot crawler"} + requests.get(self.URL, headers=headers, timeout=5) + + span = self.assert_span() + self.assertEqual( + span.attributes.get(USER_AGENT_SYNTHETIC_TYPE), + UserAgentSyntheticTypeValues.BOT.value, + ) + + self.memory_exporter.clear() + + # Test alwayson in middle of string + headers = {"User-Agent": "TestFramework/1.0 alwayson monitoring"} + requests.get(self.URL, headers=headers, timeout=5) + + span = self.assert_span() + self.assertEqual( + span.attributes.get(USER_AGENT_SYNTHETIC_TYPE), + UserAgentSyntheticTypeValues.TEST.value, + ) + + def test_user_agent_priority_alwayson_over_bot(self): + """Test that alwayson takes priority if both patterns match""" + headers = {"User-Agent": "alwayson-googlebot/1.0"} + requests.get(self.URL, headers=headers, timeout=5) + + span = self.assert_span() + # alwayson should be checked first and return 'test' + self.assertEqual( + span.attributes.get(USER_AGENT_SYNTHETIC_TYPE), + UserAgentSyntheticTypeValues.TEST.value, + ) diff --git a/instrumentation/opentelemetry-instrumentation-sqlalchemy/tests/test_sqlalchemy.py b/instrumentation/opentelemetry-instrumentation-sqlalchemy/tests/test_sqlalchemy.py index 770a3e945..136f737ce 100644 --- a/instrumentation/opentelemetry-instrumentation-sqlalchemy/tests/test_sqlalchemy.py +++ b/instrumentation/opentelemetry-instrumentation-sqlalchemy/tests/test_sqlalchemy.py @@ -111,7 +111,7 @@ def test_instrument_engine_connect(self): ) def test_async_trace_integration(self): async def run(): - from sqlalchemy.ext.asyncio import ( # pylint: disable-all + from sqlalchemy.ext.asyncio import ( # pylint: disable-all # noqa: PLC0415 create_async_engine, ) @@ -161,7 +161,9 @@ def test_not_recording(self): def test_create_engine_wrapper(self): SQLAlchemyInstrumentor().instrument() - from sqlalchemy import create_engine # pylint: disable-all + from sqlalchemy import ( # noqa: PLC0415 + create_engine, # pylint: disable-all + ) engine = create_engine("sqlite:///:memory:") cnx = engine.connect() @@ -188,7 +190,9 @@ def test_create_engine_wrapper(self): def test_instrument_engine_from_config(self): SQLAlchemyInstrumentor().instrument() - from sqlalchemy import engine_from_config # pylint: disable-all + from sqlalchemy import ( # noqa: PLC0415 + engine_from_config, # pylint: disable-all + ) engine = engine_from_config({"sqlalchemy.url": "sqlite:///:memory:"}) cnx = engine.connect() @@ -203,7 +207,9 @@ def test_create_engine_wrapper_enable_commenter(self): enable_commenter=True, commenter_options={"db_framework": False}, ) - from sqlalchemy import create_engine # pylint: disable-all + from sqlalchemy import ( # noqa: PLC0415 + create_engine, # pylint: disable-all + ) engine = create_engine("sqlite:///:memory:") cnx = engine.connect() @@ -231,7 +237,9 @@ def test_create_engine_wrapper_enable_commenter_stmt_enabled(self): commenter_options={"db_framework": False}, enable_attribute_commenter=True, ) - from sqlalchemy import create_engine # pylint: disable-all + from sqlalchemy import ( # noqa: PLC0415 + create_engine, # pylint: disable-all + ) engine = create_engine("sqlite:///:memory:") cnx = engine.connect() @@ -261,7 +269,9 @@ def test_create_engine_wrapper_enable_commenter_otel_values_false(self): "opentelemetry_values": False, }, ) - from sqlalchemy import create_engine # pylint: disable-all + from sqlalchemy import ( # noqa: PLC0415 + create_engine, # pylint: disable-all + ) engine = create_engine("sqlite:///:memory:") cnx = engine.connect() @@ -294,7 +304,9 @@ def test_create_engine_wrapper_enable_commenter_stmt_enabled_otel_values_false( }, enable_attribute_commenter=True, ) - from sqlalchemy import create_engine # pylint: disable-all + from sqlalchemy import ( # noqa: PLC0415 + create_engine, # pylint: disable-all + ) engine = create_engine("sqlite:///:memory:") cnx = engine.connect() @@ -330,7 +342,9 @@ def test_custom_tracer_provider(self): ) SQLAlchemyInstrumentor().instrument(tracer_provider=provider) - from sqlalchemy import create_engine # pylint: disable-all + from sqlalchemy import ( # noqa: PLC0415 + create_engine, # pylint: disable-all + ) engine = create_engine("sqlite:///:memory:") cnx = engine.connect() @@ -353,7 +367,7 @@ def test_custom_tracer_provider(self): def test_create_async_engine_wrapper(self): async def run(): SQLAlchemyInstrumentor().instrument() - from sqlalchemy.ext.asyncio import ( # pylint: disable-all + from sqlalchemy.ext.asyncio import ( # pylint: disable-all # noqa: PLC0415 create_async_engine, ) @@ -394,7 +408,7 @@ async def run(): "db_framework": False, }, ) - from sqlalchemy.ext.asyncio import ( # pylint: disable-all + from sqlalchemy.ext.asyncio import ( # pylint: disable-all # noqa: PLC0415 create_async_engine, ) @@ -433,7 +447,7 @@ async def run(): }, enable_attribute_commenter=True, ) - from sqlalchemy.ext.asyncio import ( # pylint: disable-all + from sqlalchemy.ext.asyncio import ( # pylint: disable-all # noqa: PLC0415 create_async_engine, ) @@ -474,7 +488,7 @@ async def run(): "opentelemetry_values": False, }, ) - from sqlalchemy.ext.asyncio import ( # pylint: disable-all + from sqlalchemy.ext.asyncio import ( # pylint: disable-all # noqa: PLC0415 create_async_engine, ) @@ -516,7 +530,7 @@ async def run(): }, enable_attribute_commenter=True, ) - from sqlalchemy.ext.asyncio import ( # pylint: disable-all + from sqlalchemy.ext.asyncio import ( # pylint: disable-all # noqa: PLC0415 create_async_engine, ) @@ -581,7 +595,7 @@ def test_uninstrument_without_engine(self): SQLAlchemyInstrumentor().instrument( tracer_provider=self.tracer_provider ) - from sqlalchemy import create_engine + from sqlalchemy import create_engine # noqa: PLC0415 engine = create_engine("sqlite:///:memory:") @@ -609,12 +623,12 @@ def test_no_op_tracer_provider(self): def test_no_memory_leakage_if_engine_diposed(self): SQLAlchemyInstrumentor().instrument() - import gc - import weakref + import gc # noqa: PLC0415 + import weakref # noqa: PLC0415 - from sqlalchemy import create_engine + from sqlalchemy import create_engine # noqa: PLC0415 - from opentelemetry.instrumentation.sqlalchemy.engine import ( + from opentelemetry.instrumentation.sqlalchemy.engine import ( # noqa: PLC0415 EngineTracer, ) @@ -638,7 +652,7 @@ def make_shortlived_engine(): def test_suppress_instrumentation_create_engine(self): SQLAlchemyInstrumentor().instrument() - from sqlalchemy import create_engine + from sqlalchemy import create_engine # noqa: PLC0415 with suppress_instrumentation(): engine = create_engine("sqlite:///:memory:") @@ -652,7 +666,7 @@ def test_suppress_instrumentation_create_engine(self): def test_suppress_instrumentation_create_async_engine(self): async def run(): SQLAlchemyInstrumentor().instrument() - from sqlalchemy.ext.asyncio import ( # pylint: disable-all + from sqlalchemy.ext.asyncio import ( # pylint: disable-all # noqa: PLC0415 create_async_engine, ) diff --git a/instrumentation/opentelemetry-instrumentation-sqlalchemy/tests/test_sqlcommenter.py b/instrumentation/opentelemetry-instrumentation-sqlalchemy/tests/test_sqlcommenter.py index d8144dadc..b81eede1b 100644 --- a/instrumentation/opentelemetry-instrumentation-sqlalchemy/tests/test_sqlcommenter.py +++ b/instrumentation/opentelemetry-instrumentation-sqlalchemy/tests/test_sqlcommenter.py @@ -304,7 +304,9 @@ def test_sqlcommenter_enabled_create_engine_after_instrumentation(self): tracer_provider=self.tracer_provider, enable_commenter=True, ) - from sqlalchemy import create_engine # pylint: disable-all + from sqlalchemy import ( # noqa: PLC0415 + create_engine, # pylint: disable-all + ) engine = create_engine("sqlite:///:memory:") cnx = engine.connect() @@ -332,7 +334,9 @@ def test_sqlcommenter_enabled_stmt_enabled_create_engine_after_instrumentation( enable_commenter=True, enable_attribute_commenter=True, ) - from sqlalchemy import create_engine # pylint: disable-all + from sqlalchemy import ( # noqa: PLC0415 + create_engine, # pylint: disable-all + ) engine = create_engine("sqlite:///:memory:") cnx = engine.connect() @@ -357,7 +361,9 @@ def test_sqlcommenter_disabled_create_engine_after_instrumentation(self): tracer_provider=self.tracer_provider, enable_commenter=False, ) - from sqlalchemy import create_engine # pylint: disable-all + from sqlalchemy import ( # noqa: PLC0415 + create_engine, # pylint: disable-all + ) engine = create_engine("sqlite:///:memory:") cnx = engine.connect() diff --git a/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py b/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py index bfbf5b6db..1107287b6 100644 --- a/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py @@ -258,6 +258,9 @@ def response_hook(span: Span, environ: WSGIEnvironment, status: str, response_he HTTP_SERVER_NAME, HTTP_URL, ) +from opentelemetry.semconv._incubating.attributes.user_agent_attributes import ( + USER_AGENT_SYNTHETIC_TYPE, +) from opentelemetry.semconv.attributes.error_attributes import ERROR_TYPE from opentelemetry.semconv.metrics import MetricInstruments from opentelemetry.semconv.metrics.http_metrics import ( @@ -271,6 +274,7 @@ def response_hook(span: Span, environ: WSGIEnvironment, status: str, response_he OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_RESPONSE, SanitizeValue, _parse_url_query, + detect_synthetic_user_agent, get_custom_headers, normalise_request_header_name, normalise_response_header_name, @@ -391,6 +395,11 @@ def collect_request_attributes( if user_agent is not None and len(user_agent) > 0: _set_http_user_agent(result, user_agent, sem_conv_opt_in_mode) + # Check for synthetic user agent type + synthetic_type = detect_synthetic_user_agent(user_agent) + if synthetic_type: + result[USER_AGENT_SYNTHETIC_TYPE] = synthetic_type + flavor = environ.get("SERVER_PROTOCOL", "") if flavor.upper().startswith(_HTTP_VERSION_PREFIX): flavor = flavor[len(_HTTP_VERSION_PREFIX) :] diff --git a/instrumentation/opentelemetry-instrumentation-wsgi/tests/test_wsgi_middleware.py b/instrumentation/opentelemetry-instrumentation-wsgi/tests/test_wsgi_middleware.py index 5a6e2d21f..bb6c3aca2 100644 --- a/instrumentation/opentelemetry-instrumentation-wsgi/tests/test_wsgi_middleware.py +++ b/instrumentation/opentelemetry-instrumentation-wsgi/tests/test_wsgi_middleware.py @@ -52,6 +52,9 @@ NET_HOST_NAME, NET_HOST_PORT, ) +from opentelemetry.semconv._incubating.attributes.user_agent_attributes import ( + USER_AGENT_SYNTHETIC_TYPE, +) from opentelemetry.semconv.attributes.http_attributes import ( HTTP_REQUEST_METHOD, HTTP_RESPONSE_STATUS_CODE, @@ -527,6 +530,7 @@ def test_default_span_name_missing_path_info(self): self.validate_response(response, span_name=method) +# pylint: disable=too-many-public-methods class TestWsgiAttributes(unittest.TestCase): def setUp(self): self.environ = {} @@ -791,6 +795,83 @@ def test_http_user_agent_attribute(self): expected_new.items(), ) + def test_http_user_agent_synthetic_bot_detection(self): + """Test that bot user agents are detected as synthetic with type 'bot'""" + test_cases = [ + "Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)", + "Mozilla/5.0 (compatible; bingbot/2.0; +http://www.bing.com/bingbot.htm)", + "googlebot/1.0", + "bingbot/1.0", + ] + + for user_agent in test_cases: + with self.subTest(user_agent=user_agent): + self.environ["HTTP_USER_AGENT"] = user_agent + attributes = otel_wsgi.collect_request_attributes(self.environ) + + # Should have both the original user agent and synthetic type + self.assertIn(HTTP_USER_AGENT, attributes) + self.assertEqual(attributes[HTTP_USER_AGENT], user_agent) + self.assertIn(USER_AGENT_SYNTHETIC_TYPE, attributes) + self.assertEqual(attributes[USER_AGENT_SYNTHETIC_TYPE], "bot") + + def test_http_user_agent_synthetic_test_detection(self): + """Test that test user agents are detected as synthetic with type 'test'""" + test_cases = [ + "alwayson/1.0", + "AlwaysOn/2.0", + "test-alwayson-client", + ] + + for user_agent in test_cases: + with self.subTest(user_agent=user_agent): + self.environ["HTTP_USER_AGENT"] = user_agent + attributes = otel_wsgi.collect_request_attributes(self.environ) + + # Should have both the original user agent and synthetic type + self.assertIn(HTTP_USER_AGENT, attributes) + self.assertEqual(attributes[HTTP_USER_AGENT], user_agent) + self.assertIn(USER_AGENT_SYNTHETIC_TYPE, attributes) + self.assertEqual(attributes[USER_AGENT_SYNTHETIC_TYPE], "test") + + def test_http_user_agent_non_synthetic(self): + """Test that normal user agents are not marked as synthetic""" + test_cases = [ + "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36", + "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.1.1 Safari/605.1.15", + "PostmanRuntime/7.28.4", + "curl/7.68.0", + ] + + for user_agent in test_cases: + with self.subTest(user_agent=user_agent): + self.environ["HTTP_USER_AGENT"] = user_agent + attributes = otel_wsgi.collect_request_attributes(self.environ) + + # Should have the original user agent but no synthetic type + self.assertIn(HTTP_USER_AGENT, attributes) + self.assertEqual(attributes[HTTP_USER_AGENT], user_agent) + self.assertNotIn(USER_AGENT_SYNTHETIC_TYPE, attributes) + + def test_http_user_agent_synthetic_new_semconv(self): + """Test synthetic user agent detection with new semantic conventions""" + self.environ["HTTP_USER_AGENT"] = ( + "Mozilla/5.0 (compatible; Googlebot/2.1)" + ) + attributes = otel_wsgi.collect_request_attributes( + self.environ, + _StabilityMode.HTTP, + ) + + # Should have both the new semconv user agent and synthetic type + self.assertIn(USER_AGENT_ORIGINAL, attributes) + self.assertEqual( + attributes[USER_AGENT_ORIGINAL], + "Mozilla/5.0 (compatible; Googlebot/2.1)", + ) + self.assertIn(USER_AGENT_SYNTHETIC_TYPE, attributes) + self.assertEqual(attributes[USER_AGENT_SYNTHETIC_TYPE], "bot") + def test_response_attributes(self): otel_wsgi.add_response_attributes(self.span, "404 Not Found", {}) otel_wsgi.add_response_attributes( diff --git a/opentelemetry-instrumentation/README.rst b/opentelemetry-instrumentation/README.rst index a342aa188..5664bccc1 100644 --- a/opentelemetry-instrumentation/README.rst +++ b/opentelemetry-instrumentation/README.rst @@ -106,6 +106,8 @@ check `here None: else: try: # pylint: disable=import-outside-toplevel - from gevent import monkey + from gevent import monkey # noqa: PLC0415 getattr(monkey, gevent_patch)() except ImportError: diff --git a/opentelemetry-instrumentation/src/opentelemetry/instrumentation/auto_instrumentation/_load.py b/opentelemetry-instrumentation/src/opentelemetry/instrumentation/auto_instrumentation/_load.py index fb4698a72..fab95bf84 100644 --- a/opentelemetry-instrumentation/src/opentelemetry/instrumentation/auto_instrumentation/_load.py +++ b/opentelemetry-instrumentation/src/opentelemetry/instrumentation/auto_instrumentation/_load.py @@ -35,6 +35,8 @@ _logger = getLogger(__name__) +SKIPPED_INSTRUMENTATIONS_WILDCARD = "*" + class _EntryPointDistFinder: @cached_property @@ -94,6 +96,9 @@ def _load_instrumentors(distro): entry_point.load()() for entry_point in entry_points(group="opentelemetry_instrumentor"): + if SKIPPED_INSTRUMENTATIONS_WILDCARD in package_to_exclude: + break + if entry_point.name in package_to_exclude: _logger.debug( "Instrumentation skipped for library %s", entry_point.name diff --git a/opentelemetry-instrumentation/tests/auto_instrumentation/test_load.py b/opentelemetry-instrumentation/tests/auto_instrumentation/test_load.py index e1d98669f..c192c819b 100644 --- a/opentelemetry-instrumentation/tests/auto_instrumentation/test_load.py +++ b/opentelemetry-instrumentation/tests/auto_instrumentation/test_load.py @@ -492,3 +492,54 @@ def test_entry_point_dist_finder(self): ) # dist are not comparable, being truthy is enough self.assertTrue(new_entry_point_dist) + + @patch.dict( + "os.environ", + {OTEL_PYTHON_DISABLED_INSTRUMENTATIONS: "*"}, + ) + @patch( + "opentelemetry.instrumentation.auto_instrumentation._load.get_dist_dependency_conflicts" + ) + @patch( + "opentelemetry.instrumentation.auto_instrumentation._load.entry_points" + ) + def test_no_instrumentor_called_with_wildcard(self, iter_mock, mock_dep): + # Mock opentelemetry_pre_instrument entry points + # pylint: disable=too-many-locals + pre_ep_mock1 = Mock() + pre_ep_mock1.name = "pre1" + pre_mock1 = Mock() + pre_ep_mock1.load.return_value = pre_mock1 + + # Mock opentelemetry_instrumentor entry points + ep_mock1 = Mock() + ep_mock1.name = "instr1" + + # Mock opentelemetry_instrumentor entry points + post_ep_mock1 = Mock() + post_ep_mock1.name = "post1" + post_mock1 = Mock() + post_ep_mock1.load.return_value = post_mock1 + + distro_mock = Mock() + + # Mock entry points in order + iter_mock.side_effect = [ + (pre_ep_mock1,), + (ep_mock1,), + (post_ep_mock1,), + ] + _load._load_instrumentors(distro_mock) + + self.assertEqual(iter_mock.call_count, 3) + + # All opentelemetry_pre_instrument entry points should be loaded + pre_mock1.assert_called_once() + + # No instrumentations should be loaded + mock_dep.assert_not_called() + distro_mock.load_instrumentor.assert_not_called() + self.assertEqual(distro_mock.load_instrumentor.call_count, 0) + + # All opentelemetry_post_instrument entry points should be loaded + post_mock1.assert_called_once() diff --git a/tests/opentelemetry-docker-tests/tests/flask/test_flask_functional.py b/tests/opentelemetry-docker-tests/tests/flask/test_flask_functional.py new file mode 100644 index 000000000..88d60e7cb --- /dev/null +++ b/tests/opentelemetry-docker-tests/tests/flask/test_flask_functional.py @@ -0,0 +1,118 @@ +# 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. + +from flask import Flask + +from opentelemetry import metrics as metrics_api +from opentelemetry.instrumentation.flask import FlaskInstrumentor +from opentelemetry.sdk.metrics import AlwaysOnExemplarFilter +from opentelemetry.test.globals_test import ( + reset_metrics_globals, +) +from opentelemetry.test.test_base import TestBase +from opentelemetry.trace import ( + INVALID_SPAN_ID, + INVALID_TRACE_ID, +) + + +class TestFunctionalFlask(TestBase): + def setUp(self): + super().setUp() + self.memory_exporter.clear() + # This is done because set_meter_provider cannot override the + # current meter provider. + reset_metrics_globals() + ( + self.meter_provider, + self.memory_metrics_reader, + ) = self.create_meter_provider( + exemplar_filter=AlwaysOnExemplarFilter(), + ) + metrics_api.set_meter_provider(self.meter_provider) + + self._app = Flask(__name__) + + @self._app.route("/test/") + def test_route(): + return "Test response" + + self._client = self._app.test_client() + + FlaskInstrumentor().instrument_app( + self._app, + meter_provider=self.meter_provider, + ) + + def tearDown(self): + FlaskInstrumentor().uninstrument() + super().tearDown() + + def test_duration_metrics_exemplars(self): + """Should generate exemplars with trace and span IDs for Flask HTTP requests.""" + self._client.get("/test/") + self._client.get("/test/") + self._client.get("/test/") + + metrics_data = self.memory_metrics_reader.get_metrics_data() + self.assertIsNotNone(metrics_data) + self.assertTrue(len(metrics_data.resource_metrics) > 0) + + duration_metric = None + metric_names = [] + for resource_metric in metrics_data.resource_metrics: + for scope_metric in resource_metric.scope_metrics: + for metric in scope_metric.metrics: + metric_names.append(metric.name) + if metric.name in [ + "http.server.request.duration", + "http.server.duration", + ]: + duration_metric = metric + break + if duration_metric: + break + if duration_metric: + break + + self.assertIsNotNone(duration_metric) + data_points = list(duration_metric.data.data_points) + self.assertTrue(len(data_points) > 0) + + exemplar_count = 0 + for data_point in data_points: + if hasattr(data_point, "exemplars") and data_point.exemplars: + for exemplar in data_point.exemplars: + exemplar_count += 1 + # Exemplar has required fields and valid span context + self.assertIsNotNone(exemplar.value) + self.assertIsNotNone(exemplar.time_unix_nano) + self.assertIsNotNone(exemplar.span_id) + self.assertNotEqual(exemplar.span_id, INVALID_SPAN_ID) + self.assertIsNotNone(exemplar.trace_id) + self.assertNotEqual(exemplar.trace_id, INVALID_TRACE_ID) + + # Trace and span ID of exemplar are part of finished spans + finished_spans = self.memory_exporter.get_finished_spans() + finished_span_ids = [ + span.context.span_id for span in finished_spans + ] + finished_trace_ids = [ + span.context.trace_id for span in finished_spans + ] + self.assertIn(exemplar.span_id, finished_span_ids) + self.assertIn(exemplar.trace_id, finished_trace_ids) + + # At least one exemplar was generated + self.assertGreater(exemplar_count, 0) diff --git a/tests/opentelemetry-docker-tests/tests/test-requirements.txt b/tests/opentelemetry-docker-tests/tests/test-requirements.txt index 4bee47eed..365239d96 100644 --- a/tests/opentelemetry-docker-tests/tests/test-requirements.txt +++ b/tests/opentelemetry-docker-tests/tests/test-requirements.txt @@ -24,6 +24,7 @@ dockerpty==0.4.1 docopt==0.6.2 exceptiongroup==1.2.0 flaky==3.7.0 +flask==3.0.2 greenlet==3.0.3 grpcio==1.63.2 idna==2.10 diff --git a/tox.ini b/tox.ini index b631971e6..3d69e77d8 100644 --- a/tox.ini +++ b/tox.ini @@ -1008,6 +1008,7 @@ deps = -e {toxinidir}/instrumentation/opentelemetry-instrumentation-kafka-python -e {toxinidir}/instrumentation/opentelemetry-instrumentation-confluent-kafka -e {toxinidir}/instrumentation/opentelemetry-instrumentation-dbapi + -e {toxinidir}/instrumentation/opentelemetry-instrumentation-flask -e {toxinidir}/instrumentation/opentelemetry-instrumentation-mysql -e {toxinidir}/instrumentation/opentelemetry-instrumentation-mysqlclient -e {toxinidir}/instrumentation/opentelemetry-instrumentation-psycopg @@ -1019,6 +1020,8 @@ deps = -e {toxinidir}/instrumentation/opentelemetry-instrumentation-aiopg -e {toxinidir}/instrumentation/opentelemetry-instrumentation-redis -e {toxinidir}/instrumentation/opentelemetry-instrumentation-remoulade + -e {toxinidir}/instrumentation/opentelemetry-instrumentation-wsgi + -e {toxinidir}/util/opentelemetry-util-http opentelemetry-exporter-opencensus@{env:CORE_REPO}\#egg=opentelemetry-exporter-opencensus&subdirectory=exporter/opentelemetry-exporter-opencensus changedir = diff --git a/util/opentelemetry-util-genai/CHANGELOG.md b/util/opentelemetry-util-genai/CHANGELOG.md index 873df73a1..e6be4999f 100644 --- a/util/opentelemetry-util-genai/CHANGELOG.md +++ b/util/opentelemetry-util-genai/CHANGELOG.md @@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased +- Minor change to check LRU cache in Completion Hook before acquiring semaphore/thread ([#3907](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3907)). +- Add environment variable for genai upload hook queue size + ([https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3943](#3943)) +- Add more Semconv attributes to LLMInvocation spans. + ([https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3862](#3862)) + ## Version 0.2b0 (2025-10-14) - Add jsonlines support to fsspec uploader diff --git a/util/opentelemetry-util-genai/pyproject.toml b/util/opentelemetry-util-genai/pyproject.toml index cba9252f6..407820396 100644 --- a/util/opentelemetry-util-genai/pyproject.toml +++ b/util/opentelemetry-util-genai/pyproject.toml @@ -25,8 +25,8 @@ classifiers = [ "Programming Language :: Python :: 3.13", ] dependencies = [ - "opentelemetry-instrumentation ~= 0.57b0", - "opentelemetry-semantic-conventions ~= 0.57b0", + "opentelemetry-instrumentation ~= 0.58b0", + "opentelemetry-semantic-conventions ~= 0.58b0", "opentelemetry-api>=1.31.0", ] diff --git a/util/opentelemetry-util-genai/src/opentelemetry/util/genai/_upload/__init__.py b/util/opentelemetry-util-genai/src/opentelemetry/util/genai/_upload/__init__.py index 92316192b..9ef0a08ef 100644 --- a/util/opentelemetry-util-genai/src/opentelemetry/util/genai/_upload/__init__.py +++ b/util/opentelemetry-util-genai/src/opentelemetry/util/genai/_upload/__init__.py @@ -14,6 +14,7 @@ from __future__ import annotations +import logging from os import environ from opentelemetry.util.genai.completion_hook import ( @@ -22,21 +23,65 @@ ) from opentelemetry.util.genai.environment_variables import ( OTEL_INSTRUMENTATION_GENAI_UPLOAD_BASE_PATH, + OTEL_INSTRUMENTATION_GENAI_UPLOAD_FORMAT, + OTEL_INSTRUMENTATION_GENAI_UPLOAD_MAX_QUEUE_SIZE, ) +_logger = logging.getLogger(__name__) + def upload_completion_hook() -> CompletionHook: + base_path = environ.get(OTEL_INSTRUMENTATION_GENAI_UPLOAD_BASE_PATH) + if not base_path: + _logger.warning( + "%s is required but not set, using no-op instead", + OTEL_INSTRUMENTATION_GENAI_UPLOAD_BASE_PATH, + ) + return _NoOpCompletionHook() + # If fsspec is not installed the hook will be a no-op. try: # pylint: disable=import-outside-toplevel - from opentelemetry.util.genai._upload.completion_hook import ( + from opentelemetry.util.genai._upload.completion_hook import ( # noqa: PLC0415 + _DEFAULT_FORMAT, + _DEFAULT_MAX_QUEUE_SIZE, + _FORMATS, UploadCompletionHook, ) except ImportError: return _NoOpCompletionHook() - base_path = environ.get(OTEL_INSTRUMENTATION_GENAI_UPLOAD_BASE_PATH) - if not base_path: - return _NoOpCompletionHook() + environ_max_queue_size = environ.get( + OTEL_INSTRUMENTATION_GENAI_UPLOAD_MAX_QUEUE_SIZE, + _DEFAULT_MAX_QUEUE_SIZE, + ) + try: + environ_max_queue_size = int(environ_max_queue_size) + except ValueError: + _logger.warning( + "%s is not a valid integer for %s. Defaulting to %s.", + environ_max_queue_size, + OTEL_INSTRUMENTATION_GENAI_UPLOAD_MAX_QUEUE_SIZE, + _DEFAULT_MAX_QUEUE_SIZE, + ) + environ_max_queue_size = _DEFAULT_MAX_QUEUE_SIZE + + environ_format = environ.get( + OTEL_INSTRUMENTATION_GENAI_UPLOAD_FORMAT, _DEFAULT_FORMAT + ).lower() + + if environ_format not in _FORMATS: + _logger.warning( + "%s is not a valid option for %s, should be be one of %s. Defaulting to %s.", + environ_format, + OTEL_INSTRUMENTATION_GENAI_UPLOAD_FORMAT, + _FORMATS, + _DEFAULT_FORMAT, + ) + environ_format = _DEFAULT_FORMAT - return UploadCompletionHook(base_path=base_path) + return UploadCompletionHook( + base_path=base_path, + max_queue_size=environ_max_queue_size, + upload_format=environ_format, + ) diff --git a/util/opentelemetry-util-genai/src/opentelemetry/util/genai/_upload/completion_hook.py b/util/opentelemetry-util-genai/src/opentelemetry/util/genai/_upload/completion_hook.py index 6e88b5e38..d2b18fd6b 100644 --- a/util/opentelemetry-util-genai/src/opentelemetry/util/genai/_upload/completion_hook.py +++ b/util/opentelemetry-util-genai/src/opentelemetry/util/genai/_upload/completion_hook.py @@ -24,7 +24,6 @@ from contextlib import ExitStack from dataclasses import asdict, dataclass from functools import partial -from os import environ from time import time from typing import Any, Callable, Final, Literal from uuid import uuid4 @@ -36,9 +35,6 @@ from opentelemetry.trace import Span from opentelemetry.util.genai import types from opentelemetry.util.genai.completion_hook import CompletionHook -from opentelemetry.util.genai.environment_variables import ( - OTEL_INSTRUMENTATION_GENAI_UPLOAD_FORMAT, -) from opentelemetry.util.genai.utils import gen_ai_json_dump GEN_AI_INPUT_MESSAGES_REF: Final = ( @@ -52,6 +48,9 @@ ) _MESSAGE_INDEX_KEY = "index" +_DEFAULT_MAX_QUEUE_SIZE = 20 +_DEFAULT_FORMAT = "json" + Format = Literal["json", "jsonl"] _FORMATS: tuple[Format, ...] = ("json", "jsonl") @@ -105,36 +104,26 @@ def __init__( self, *, base_path: str, - max_size: int = 20, - upload_format: Format | None = None, + max_queue_size: int = _DEFAULT_MAX_QUEUE_SIZE, + upload_format: Format = _DEFAULT_FORMAT, lru_cache_max_size: int = 1024, ) -> None: - self._max_size = max_size + self._max_queue_size = max_queue_size self._fs, base_path = fsspec.url_to_fs(base_path) self._base_path = self._fs.unstrip_protocol(base_path) self.lru_dict: OrderedDict[str, bool] = OrderedDict() self.lru_cache_max_size = lru_cache_max_size - if upload_format not in _FORMATS + (None,): + if upload_format not in _FORMATS: raise ValueError( f"Invalid {upload_format=}. Must be one of {_FORMATS}" ) - - if upload_format is None: - environ_format = environ.get( - OTEL_INSTRUMENTATION_GENAI_UPLOAD_FORMAT, "json" - ).lower() - if environ_format not in _FORMATS: - upload_format = "json" - else: - upload_format = environ_format - - self._format: Final[Literal["json", "jsonl"]] = upload_format + self._format = upload_format # Use a ThreadPoolExecutor for its queueing and thread management. The semaphore # limits the number of queued tasks. If the queue is full, data will be dropped. - self._executor = ThreadPoolExecutor(max_workers=max_size) - self._semaphore = threading.BoundedSemaphore(max_size) + self._executor = ThreadPoolExecutor(max_workers=self._max_queue_size) + self._semaphore = threading.BoundedSemaphore(self._max_queue_size) def _submit_all(self, upload_data: UploadData) -> None: def done(future: Future[None]) -> None: @@ -326,7 +315,7 @@ def shutdown(self, *, timeout_sec: float = 10.0) -> None: # Wait for all tasks to finish to flush the queue with ExitStack() as stack: - for _ in range(self._max_size): + for _ in range(self._max_queue_size): remaining = deadline - time() if not self._semaphore.acquire(timeout=remaining): # pylint: disable=consider-using-with # Couldn't finish flushing all uploads before timeout diff --git a/util/opentelemetry-util-genai/src/opentelemetry/util/genai/environment_variables.py b/util/opentelemetry-util-genai/src/opentelemetry/util/genai/environment_variables.py index 3fde0b984..4a8dde216 100644 --- a/util/opentelemetry-util-genai/src/opentelemetry/util/genai/environment_variables.py +++ b/util/opentelemetry-util-genai/src/opentelemetry/util/genai/environment_variables.py @@ -50,3 +50,13 @@ The format to use when uploading prompt and response data. Must be one of ``json`` or ``jsonl``. Defaults to ``json``. """ + +OTEL_INSTRUMENTATION_GENAI_UPLOAD_MAX_QUEUE_SIZE = ( + "OTEL_INSTRUMENTATION_GENAI_UPLOAD_MAX_QUEUE_SIZE" +) +""" +.. envvar:: OTEL_INSTRUMENTATION_GENAI_UPLOAD_MAX_QUEUE_SIZE + +The maximum number of concurrent uploads to queue. New uploads will be dropped if the queue is +full. Defaults to 20. +""" diff --git a/util/opentelemetry-util-genai/src/opentelemetry/util/genai/handler.py b/util/opentelemetry-util-genai/src/opentelemetry/util/genai/handler.py index 0fb0771bc..d7758e16f 100644 --- a/util/opentelemetry-util-genai/src/opentelemetry/util/genai/handler.py +++ b/util/opentelemetry-util-genai/src/opentelemetry/util/genai/handler.py @@ -61,7 +61,7 @@ from __future__ import annotations from contextlib import contextmanager -from typing import Iterator, Optional +from typing import Iterator from opentelemetry import context as otel_context from opentelemetry.semconv._incubating.attributes import ( @@ -93,7 +93,7 @@ def __init__(self, tracer_provider: TracerProvider | None = None): __name__, __version__, tracer_provider, - schema_url=Schemas.V1_36_0.value, + schema_url=Schemas.V1_37_0.value, ) def start_llm( @@ -132,6 +132,7 @@ def fail_llm( # pylint: disable=no-self-use # TODO: Provide feedback that this invocation was not started return invocation + _apply_finish_attributes(invocation.span, invocation) _apply_error_attributes(invocation.span, error) # Detach context and end span otel_context.detach(invocation.context_token) @@ -140,7 +141,7 @@ def fail_llm( # pylint: disable=no-self-use @contextmanager def llm( - self, invocation: Optional[LLMInvocation] = None + self, invocation: LLMInvocation | None = None ) -> Iterator[LLMInvocation]: """Context manager for LLM invocations. @@ -169,7 +170,7 @@ def get_telemetry_handler( """ Returns a singleton TelemetryHandler instance. """ - handler: Optional[TelemetryHandler] = getattr( + handler: TelemetryHandler | None = getattr( get_telemetry_handler, "_default_handler", None ) if handler is None: diff --git a/util/opentelemetry-util-genai/src/opentelemetry/util/genai/span_utils.py b/util/opentelemetry-util-genai/src/opentelemetry/util/genai/span_utils.py index 5eb02436f..b9b8777ec 100644 --- a/util/opentelemetry-util-genai/src/opentelemetry/util/genai/span_utils.py +++ b/util/opentelemetry-util-genai/src/opentelemetry/util/genai/span_utils.py @@ -12,8 +12,10 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + from dataclasses import asdict -from typing import List +from typing import Any from opentelemetry.semconv._incubating.attributes import ( gen_ai_attributes as GenAI, @@ -60,32 +62,13 @@ def _apply_common_span_attributes( # TODO: clean provider name to match GenAiProviderNameValues? span.set_attribute(GenAI.GEN_AI_PROVIDER_NAME, invocation.provider) - if invocation.output_messages: - span.set_attribute( - GenAI.GEN_AI_RESPONSE_FINISH_REASONS, - [gen.finish_reason for gen in invocation.output_messages], - ) - - if invocation.response_model_name is not None: - span.set_attribute( - GenAI.GEN_AI_RESPONSE_MODEL, invocation.response_model_name - ) - if invocation.response_id is not None: - span.set_attribute(GenAI.GEN_AI_RESPONSE_ID, invocation.response_id) - if invocation.input_tokens is not None: - span.set_attribute( - GenAI.GEN_AI_USAGE_INPUT_TOKENS, invocation.input_tokens - ) - if invocation.output_tokens is not None: - span.set_attribute( - GenAI.GEN_AI_USAGE_OUTPUT_TOKENS, invocation.output_tokens - ) + _apply_response_attributes(span, invocation) def _maybe_set_span_messages( span: Span, - input_messages: List[InputMessage], - output_messages: List[OutputMessage], + input_messages: list[InputMessage], + output_messages: list[OutputMessage], ) -> None: if not is_experimental_mode() or get_content_capturing_mode() not in ( ContentCapturingMode.SPAN_ONLY, @@ -112,6 +95,8 @@ def _apply_finish_attributes(span: Span, invocation: LLMInvocation) -> None: _maybe_set_span_messages( span, invocation.input_messages, invocation.output_messages ) + _apply_request_attributes(span, invocation) + _apply_response_attributes(span, invocation) span.set_attributes(invocation.attributes) @@ -122,7 +107,75 @@ def _apply_error_attributes(span: Span, error: Error) -> None: span.set_attribute(ErrorAttributes.ERROR_TYPE, error.type.__qualname__) +def _apply_request_attributes(span: Span, invocation: LLMInvocation) -> None: + """Attach GenAI request semantic convention attributes to the span.""" + attributes: dict[str, Any] = {} + if invocation.temperature is not None: + attributes[GenAI.GEN_AI_REQUEST_TEMPERATURE] = invocation.temperature + if invocation.top_p is not None: + attributes[GenAI.GEN_AI_REQUEST_TOP_P] = invocation.top_p + if invocation.frequency_penalty is not None: + attributes[GenAI.GEN_AI_REQUEST_FREQUENCY_PENALTY] = ( + invocation.frequency_penalty + ) + if invocation.presence_penalty is not None: + attributes[GenAI.GEN_AI_REQUEST_PRESENCE_PENALTY] = ( + invocation.presence_penalty + ) + if invocation.max_tokens is not None: + attributes[GenAI.GEN_AI_REQUEST_MAX_TOKENS] = invocation.max_tokens + if invocation.stop_sequences is not None: + attributes[GenAI.GEN_AI_REQUEST_STOP_SEQUENCES] = ( + invocation.stop_sequences + ) + if invocation.seed is not None: + attributes[GenAI.GEN_AI_REQUEST_SEED] = invocation.seed + if attributes: + span.set_attributes(attributes) + + +def _apply_response_attributes(span: Span, invocation: LLMInvocation) -> None: + """Attach GenAI response semantic convention attributes to the span.""" + attributes: dict[str, Any] = {} + + finish_reasons: list[str] | None + if invocation.finish_reasons is not None: + finish_reasons = invocation.finish_reasons + elif invocation.output_messages: + finish_reasons = [ + message.finish_reason + for message in invocation.output_messages + if message.finish_reason + ] + else: + finish_reasons = None + + if finish_reasons: + # De-duplicate finish reasons + unique_finish_reasons = sorted(set(finish_reasons)) + if unique_finish_reasons: + attributes[GenAI.GEN_AI_RESPONSE_FINISH_REASONS] = ( + unique_finish_reasons + ) + + if invocation.response_model_name is not None: + attributes[GenAI.GEN_AI_RESPONSE_MODEL] = ( + invocation.response_model_name + ) + if invocation.response_id is not None: + attributes[GenAI.GEN_AI_RESPONSE_ID] = invocation.response_id + if invocation.input_tokens is not None: + attributes[GenAI.GEN_AI_USAGE_INPUT_TOKENS] = invocation.input_tokens + if invocation.output_tokens is not None: + attributes[GenAI.GEN_AI_USAGE_OUTPUT_TOKENS] = invocation.output_tokens + + if attributes: + span.set_attributes(attributes) + + __all__ = [ "_apply_finish_attributes", "_apply_error_attributes", + "_apply_request_attributes", + "_apply_response_attributes", ] diff --git a/util/opentelemetry-util-genai/src/opentelemetry/util/genai/types.py b/util/opentelemetry-util-genai/src/opentelemetry/util/genai/types.py index 0ae5bde00..558807022 100644 --- a/util/opentelemetry-util-genai/src/opentelemetry/util/genai/types.py +++ b/util/opentelemetry-util-genai/src/opentelemetry/util/genai/types.py @@ -12,11 +12,12 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations from contextvars import Token from dataclasses import dataclass, field from enum import Enum -from typing import Any, Dict, List, Literal, Optional, Type, Union +from typing import Any, Literal, Type, Union from typing_extensions import TypeAlias @@ -41,14 +42,14 @@ class ContentCapturingMode(Enum): class ToolCall: arguments: Any name: str - id: Optional[str] + id: str | None type: Literal["tool_call"] = "tool_call" @dataclass() class ToolCallResponse: response: Any - id: Optional[str] + id: str | None type: Literal["tool_call_response"] = "tool_call_response" @@ -76,18 +77,18 @@ class InputMessage: class OutputMessage: role: str parts: list[MessagePart] - finish_reason: Union[str, FinishReason] + finish_reason: str | FinishReason -def _new_input_messages() -> List[InputMessage]: +def _new_input_messages() -> list[InputMessage]: return [] -def _new_output_messages() -> List[OutputMessage]: +def _new_output_messages() -> list[OutputMessage]: return [] -def _new_str_any_dict() -> Dict[str, Any]: +def _new_str_any_dict() -> dict[str, Any]: return {} @@ -100,20 +101,28 @@ class LLMInvocation: """ request_model: str - context_token: Optional[ContextToken] = None - span: Optional[Span] = None - input_messages: List[InputMessage] = field( + context_token: ContextToken | None = None + span: Span | None = None + input_messages: list[InputMessage] = field( default_factory=_new_input_messages ) - output_messages: List[OutputMessage] = field( + output_messages: list[OutputMessage] = field( default_factory=_new_output_messages ) - provider: Optional[str] = None - response_model_name: Optional[str] = None - response_id: Optional[str] = None - input_tokens: Optional[int] = None - output_tokens: Optional[int] = None - attributes: Dict[str, Any] = field(default_factory=_new_str_any_dict) + provider: str | None = None + response_model_name: str | None = None + response_id: str | None = None + finish_reasons: list[str] | None = None + input_tokens: int | None = None + output_tokens: int | None = None + attributes: dict[str, Any] = field(default_factory=_new_str_any_dict) + temperature: float | None = None + top_p: float | None = None + frequency_penalty: float | None = None + presence_penalty: float | None = None + max_tokens: int | None = None + stop_sequences: list[str] | None = None + seed: int | None = None @dataclass diff --git a/util/opentelemetry-util-genai/tests/test_upload.py b/util/opentelemetry-util-genai/tests/test_upload.py index baa0696d1..b7a1f9900 100644 --- a/util/opentelemetry-util-genai/tests/test_upload.py +++ b/util/opentelemetry-util-genai/tests/test_upload.py @@ -13,10 +13,8 @@ # limitations under the License. -# pylint: disable=import-outside-toplevel,no-name-in-module -import importlib +# pylint: disable=no-name-in-module import logging -import sys import threading import time from contextlib import contextmanager @@ -33,42 +31,10 @@ from opentelemetry.util.genai._upload.completion_hook import ( UploadCompletionHook, ) -from opentelemetry.util.genai.completion_hook import ( - _NoOpCompletionHook, - load_completion_hook, -) # Use MemoryFileSystem for testing # https://filesystem-spec.readthedocs.io/en/latest/api.html#fsspec.implementations.memory.MemoryFileSystem BASE_PATH = "memory://" - - -@patch.dict( - "os.environ", - { - "OTEL_INSTRUMENTATION_GENAI_COMPLETION_HOOK": "upload", - "OTEL_INSTRUMENTATION_GENAI_UPLOAD_BASE_PATH": BASE_PATH, - }, - clear=True, -) -class TestUploadEntryPoint(TestCase): - def test_upload_entry_point(self): - self.assertIsInstance(load_completion_hook(), UploadCompletionHook) - - def test_upload_entry_point_no_fsspec(self): - """Tests that the a no-op uploader is used when fsspec is not installed""" - - from opentelemetry.util.genai import _upload - - # Simulate fsspec imports failing - with patch.dict( - sys.modules, - {"opentelemetry.util.genai._upload.completion_hook": None}, - ): - importlib.reload(_upload) - self.assertIsInstance(load_completion_hook(), _NoOpCompletionHook) - - MAXSIZE = 5 FAKE_INPUTS = [ types.InputMessage( @@ -125,7 +91,7 @@ def setUp(self): self.mock_fs.exists.return_value = False self.hook = UploadCompletionHook( - base_path=BASE_PATH, max_size=MAXSIZE, lru_cache_max_size=5 + base_path=BASE_PATH, max_queue_size=MAXSIZE, lru_cache_max_size=5 ) def tearDown(self) -> None: @@ -305,42 +271,6 @@ def test_upload_format_sets_content_type(self): ANY, "w", content_type=expect_content_type ) - def test_parse_upload_format_envvar(self): - for envvar_value, expect in ( - ("", "json"), - ("json", "json"), - ("invalid", "json"), - ("jsonl", "jsonl"), - ("jSoNl", "jsonl"), - ): - with patch.dict( - "os.environ", - {"OTEL_INSTRUMENTATION_GENAI_UPLOAD_FORMAT": envvar_value}, - clear=True, - ): - hook = UploadCompletionHook(base_path=BASE_PATH) - self.addCleanup(hook.shutdown) - self.assertEqual( - hook._format, - expect, - f"expected upload format {expect=} with {envvar_value=} got {hook._format}", - ) - - with patch.dict( - "os.environ", - {"OTEL_INSTRUMENTATION_GENAI_UPLOAD_FORMAT": "json"}, - clear=True, - ): - hook = UploadCompletionHook( - base_path=BASE_PATH, upload_format="jsonl" - ) - self.addCleanup(hook.shutdown) - self.assertEqual( - hook._format, - "jsonl", - "upload_format kwarg should take precedence", - ) - def test_upload_after_shutdown_logs(self): self.hook.shutdown() with self.assertLogs(level=logging.INFO) as logs: diff --git a/util/opentelemetry-util-genai/tests/test_upload_entrypoint.py b/util/opentelemetry-util-genai/tests/test_upload_entrypoint.py new file mode 100644 index 000000000..f7a66fe20 --- /dev/null +++ b/util/opentelemetry-util-genai/tests/test_upload_entrypoint.py @@ -0,0 +1,159 @@ +# 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. + + +# pylint: disable=import-outside-toplevel,no-name-in-module +import importlib +import logging +import sys +from contextlib import ExitStack +from unittest.mock import patch + +import pytest + +from opentelemetry.util.genai._upload.completion_hook import ( + UploadCompletionHook, +) +from opentelemetry.util.genai.completion_hook import ( + _NoOpCompletionHook, +) +from opentelemetry.util.genai.completion_hook import ( + load_completion_hook as real_load_completion_hook, +) + +# Use MemoryFileSystem for testing +# https://filesystem-spec.readthedocs.io/en/latest/api.html#fsspec.implementations.memory.MemoryFileSystem +BASE_PATH = "memory://" + + +@pytest.fixture(name="load_completion_hook") +def fixture_load_completion_hook(): + with ExitStack() as stack: + + def load_completion_hook(): + hook = real_load_completion_hook() + if isinstance(hook, UploadCompletionHook): + stack.callback(hook.shutdown) + return hook + + yield load_completion_hook + + +@pytest.fixture(name="upload_environ", autouse=True) +def fixture_upload_environ(): + with patch.dict( + "os.environ", + { + "OTEL_INSTRUMENTATION_GENAI_COMPLETION_HOOK": "upload", + "OTEL_INSTRUMENTATION_GENAI_UPLOAD_BASE_PATH": BASE_PATH, + }, + clear=True, + ): + yield + + +def test_upload_entry_point(load_completion_hook): + assert isinstance(load_completion_hook(), UploadCompletionHook) + + +def test_upload_entry_point_no_fsspec(load_completion_hook): + """Tests that the a no-op uploader is used when fsspec is not installed""" + + from opentelemetry.util.genai import _upload # noqa: PLC0415 + + # Simulate fsspec imports failing + with patch.dict( + sys.modules, + {"opentelemetry.util.genai._upload.completion_hook": None}, + ): + importlib.reload(_upload) + assert isinstance(load_completion_hook(), _NoOpCompletionHook) + + +def test_upload_no_base_path(load_completion_hook, caplog): + with patch.dict( + "os.environ", {"OTEL_INSTRUMENTATION_GENAI_UPLOAD_BASE_PATH": ""} + ): + assert isinstance(load_completion_hook(), _NoOpCompletionHook) + + assert caplog.records[0].levelno == logging.WARNING + assert ( + caplog.records[0].message + == "OTEL_INSTRUMENTATION_GENAI_UPLOAD_BASE_PATH is required but not set, using no-op instead" + ) + + +@pytest.mark.parametrize( + "envvar_value,expect_format,expect_warn", + ( + (None, "json", False), + ("", "json", False), + ("json", "json", False), + ("invalid", "json", True), + ("jsonl", "jsonl", False), + ("jSoNl", "jsonl", False), + ), +) +def test_parse_upload_format_envvar( + envvar_value, expect_format, expect_warn, load_completion_hook, caplog +): + with patch.dict( + "os.environ", + {"OTEL_INSTRUMENTATION_GENAI_UPLOAD_FORMAT": envvar_value} + if envvar_value is not None + else {}, + ): + assert isinstance(hook := load_completion_hook(), UploadCompletionHook) + assert hook._format == expect_format, ( + f"expected upload format {expect_format=} with {envvar_value=} got {hook._format}" + ) + + if expect_warn: + assert caplog.records[0].levelno == logging.WARNING + assert ( + "is not a valid option for OTEL_INSTRUMENTATION_GENAI_UPLOAD_FORMAT, should be be one of ('json', 'jsonl'). Defaulting to json." + in caplog.records[0].message + ) + + +@pytest.mark.parametrize( + "envvar_value,expect_size,expect_warn", + ( + ("", 20, False), + ("foobar", 20, True), + ("1", 1, False), + ("100", 100, False), + ), +) +def test_parse_max_queue_size_envvar( + envvar_value, expect_size, expect_warn, load_completion_hook, caplog +): + with patch.dict( + "os.environ", + {"OTEL_INSTRUMENTATION_GENAI_UPLOAD_MAX_QUEUE_SIZE": envvar_value} + if envvar_value is not None + else {}, + ): + assert isinstance(hook := load_completion_hook(), UploadCompletionHook) + assert hook._max_queue_size == expect_size, ( + f"expected max_queue_size {expect_size=} with {envvar_value=} got {hook._format}" + ) + + if expect_warn: + assert caplog.records + assert caplog.records[0].levelno == logging.WARNING + assert ( + "is not a valid integer for OTEL_INSTRUMENTATION_GENAI_UPLOAD_MAX_QUEUE_SIZE. Defaulting to 20." + in caplog.records[0].message + ) diff --git a/util/opentelemetry-util-genai/tests/test_utils.py b/util/opentelemetry-util-genai/tests/test_utils.py index 4e94cde5c..aecb16c54 100644 --- a/util/opentelemetry-util-genai/tests/test_utils.py +++ b/util/opentelemetry-util-genai/tests/test_utils.py @@ -15,6 +15,7 @@ import json import os import unittest +from typing import Any, Mapping, Optional from unittest.mock import patch from opentelemetry import trace @@ -22,14 +23,18 @@ OTEL_SEMCONV_STABILITY_OPT_IN, _OpenTelemetrySemanticConventionStability, ) -from opentelemetry.sdk.trace import TracerProvider +from opentelemetry.sdk.trace import ReadableSpan, TracerProvider from opentelemetry.sdk.trace.export import SimpleSpanProcessor from opentelemetry.sdk.trace.export.in_memory_span_exporter import ( InMemorySpanExporter, ) +from opentelemetry.semconv._incubating.attributes import ( + gen_ai_attributes as GenAI, +) from opentelemetry.semconv.attributes import ( error_attributes as ErrorAttributes, ) +from opentelemetry.semconv.schemas import Schemas from opentelemetry.trace.status import StatusCode from opentelemetry.util.genai.environment_variables import ( OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT, @@ -65,6 +70,76 @@ def wrapper(*args, **kwargs): return decorator +def _create_input_message( + content: str = "hello world", role: str = "Human" +) -> InputMessage: + return InputMessage(role=role, parts=[Text(content=content)]) + + +def _create_output_message( + content: str = "hello back", finish_reason: str = "stop", role: str = "AI" +) -> OutputMessage: + return OutputMessage( + role=role, parts=[Text(content=content)], finish_reason=finish_reason + ) + + +def _get_single_span(span_exporter: InMemorySpanExporter) -> ReadableSpan: + spans = span_exporter.get_finished_spans() + assert len(spans) == 1 + return spans[0] + + +def _assert_span_time_order(span: ReadableSpan) -> None: + assert span.start_time is not None + assert span.end_time is not None + assert span.end_time >= span.start_time + + +def _get_span_attributes(span: ReadableSpan) -> Mapping[str, Any]: + attrs = span.attributes + assert attrs is not None + return attrs + + +def _assert_span_attributes( + span_attrs: Mapping[str, Any], expected_values: Mapping[str, Any] +) -> None: + for key, value in expected_values.items(): + assert span_attrs.get(key) == value + + +def _get_messages_from_attr( + span_attrs: Mapping[str, Any], attribute_name: str +) -> list[dict[str, Any]]: + payload = span_attrs.get(attribute_name) + assert payload is not None + assert isinstance(payload, str) + return json.loads(payload) + + +def _get_single_message( + span_attrs: Mapping[str, Any], attribute_name: str +) -> dict[str, Any]: + messages = _get_messages_from_attr(span_attrs, attribute_name) + assert len(messages) == 1 + return messages[0] + + +def _assert_text_message( + message: Mapping[str, Any], + role: str, + content: str, + finish_reason: Optional[str] = None, +) -> None: + assert message.get("role") == role + parts = message.get("parts") + assert isinstance(parts, list) and parts + assert parts[0].get("content") == content + if finish_reason is not None: + assert message.get("finish_reason") == finish_reason + + class TestVersion(unittest.TestCase): @patch_env_vars( stability_mode="gen_ai_latest_experimental", @@ -123,72 +198,71 @@ def tearDown(self): content_capturing="SPAN_ONLY", ) def test_llm_start_and_stop_creates_span(self): # pylint: disable=no-self-use - message = InputMessage( - role="Human", parts=[Text(content="hello world")] - ) - chat_generation = OutputMessage( - role="AI", parts=[Text(content="hello back")], finish_reason="stop" - ) + message = _create_input_message("hello world") + chat_generation = _create_output_message("hello back") - # Start and stop LLM invocation using context manager with self.telemetry_handler.llm() as invocation: - invocation.request_model = "test-model" - invocation.input_messages = [message] - invocation.provider = "test-provider" - invocation.attributes = {"custom_attr": "value"} + for attr, value in { + "request_model": "test-model", + "input_messages": [message], + "provider": "test-provider", + "attributes": {"custom_attr": "value"}, + "temperature": 0.5, + "top_p": 0.9, + "stop_sequences": ["stop"], + "finish_reasons": ["stop"], + "response_model_name": "test-response-model", + "response_id": "response-id", + "input_tokens": 321, + "output_tokens": 654, + }.items(): + setattr(invocation, attr, value) assert invocation.span is not None invocation.output_messages = [chat_generation] invocation.attributes.update({"extra": "info"}) - # Get the spans that were created - spans = self.span_exporter.get_finished_spans() - assert len(spans) == 1 - span = spans[0] - assert span.name == "chat test-model" - assert span.kind == trace.SpanKind.CLIENT + span = _get_single_span(self.span_exporter) + self.assertEqual(span.name, "chat test-model") + self.assertEqual(span.kind, trace.SpanKind.CLIENT) + _assert_span_time_order(span) - # Verify span attributes - assert span.attributes is not None - span_attrs = span.attributes - assert span_attrs.get("gen_ai.operation.name") == "chat" - assert span_attrs.get("gen_ai.provider.name") == "test-provider" - assert span.start_time is not None - assert span.end_time is not None - assert span.end_time >= span.start_time - assert invocation.attributes.get("custom_attr") == "value" - assert invocation.attributes.get("extra") == "info" - - # Check messages captured on span - input_messages_json = span_attrs.get("gen_ai.input.messages") - output_messages_json = span_attrs.get("gen_ai.output.messages") - assert input_messages_json is not None - assert output_messages_json is not None - assert isinstance(input_messages_json, str) - assert isinstance(output_messages_json, str) - input_messages = json.loads(input_messages_json) - output_messages = json.loads(output_messages_json) - assert len(input_messages) == 1 - assert len(output_messages) == 1 - assert input_messages[0].get("role") == "Human" - assert output_messages[0].get("role") == "AI" - assert output_messages[0].get("finish_reason") == "stop" - assert ( - output_messages[0].get("parts")[0].get("content") == "hello back" + span_attrs = _get_span_attributes(span) + _assert_span_attributes( + span_attrs, + { + GenAI.GEN_AI_OPERATION_NAME: "chat", + GenAI.GEN_AI_PROVIDER_NAME: "test-provider", + GenAI.GEN_AI_REQUEST_TEMPERATURE: 0.5, + GenAI.GEN_AI_REQUEST_TOP_P: 0.9, + GenAI.GEN_AI_REQUEST_STOP_SEQUENCES: ("stop",), + GenAI.GEN_AI_RESPONSE_FINISH_REASONS: ("stop",), + GenAI.GEN_AI_RESPONSE_MODEL: "test-response-model", + GenAI.GEN_AI_RESPONSE_ID: "response-id", + GenAI.GEN_AI_USAGE_INPUT_TOKENS: 321, + GenAI.GEN_AI_USAGE_OUTPUT_TOKENS: 654, + "extra": "info", + "custom_attr": "value", + }, ) - # Check that extra attributes are added to the span - assert span_attrs.get("extra") == "info" - assert span_attrs.get("custom_attr") == "value" + input_message = _get_single_message( + span_attrs, "gen_ai.input.messages" + ) + output_message = _get_single_message( + span_attrs, "gen_ai.output.messages" + ) + _assert_text_message(input_message, "Human", "hello world") + _assert_text_message(output_message, "AI", "hello back", "stop") + self.assertEqual(invocation.attributes.get("custom_attr"), "value") + self.assertEqual(invocation.attributes.get("extra"), "info") @patch_env_vars( stability_mode="gen_ai_latest_experimental", content_capturing="SPAN_ONLY", ) def test_llm_manual_start_and_stop_creates_span(self): - message = InputMessage(role="Human", parts=[Text(content="hi")]) - chat_generation = OutputMessage( - role="AI", parts=[Text(content="ok")], finish_reason="stop" - ) + message = _create_input_message("hi") + chat_generation = _create_output_message("ok") invocation = LLMInvocation( request_model="manual-model", @@ -203,40 +277,132 @@ def test_llm_manual_start_and_stop_creates_span(self): invocation.attributes.update({"extra_manual": "yes"}) self.telemetry_handler.stop_llm(invocation) - spans = self.span_exporter.get_finished_spans() - assert len(spans) == 1 - span = spans[0] + span = _get_single_span(self.span_exporter) assert span.name == "chat manual-model" assert span.kind == trace.SpanKind.CLIENT - assert span.start_time is not None - assert span.end_time is not None - assert span.end_time >= span.start_time + _assert_span_time_order(span) + + attrs = _get_span_attributes(span) + _assert_span_attributes( + attrs, + { + "manual": True, + "extra_manual": "yes", + }, + ) + + def test_llm_span_finish_reasons_without_output_messages(self): + invocation = LLMInvocation( + request_model="model-without-output", + provider="test-provider", + finish_reasons=["length"], + response_model_name="alt-model", + response_id="resp-001", + input_tokens=12, + output_tokens=34, + ) + + self.telemetry_handler.start_llm(invocation) + assert invocation.span is not None + self.telemetry_handler.stop_llm(invocation) - attrs = span.attributes - assert attrs is not None - assert attrs.get("manual") is True - assert attrs.get("extra_manual") == "yes" + span = _get_single_span(self.span_exporter) + _assert_span_time_order(span) + attrs = _get_span_attributes(span) + _assert_span_attributes( + attrs, + { + GenAI.GEN_AI_RESPONSE_FINISH_REASONS: ("length",), + GenAI.GEN_AI_RESPONSE_MODEL: "alt-model", + GenAI.GEN_AI_RESPONSE_ID: "resp-001", + GenAI.GEN_AI_USAGE_INPUT_TOKENS: 12, + GenAI.GEN_AI_USAGE_OUTPUT_TOKENS: 34, + }, + ) + + def test_llm_span_finish_reasons_deduplicated_from_invocation(self): + invocation = LLMInvocation( + request_model="model-dedup", + provider="test-provider", + finish_reasons=["stop", "length", "stop"], + ) + + self.telemetry_handler.start_llm(invocation) + assert invocation.span is not None + self.telemetry_handler.stop_llm(invocation) + + span = _get_single_span(self.span_exporter) + attrs = _get_span_attributes(span) + self.assertEqual( + attrs[GenAI.GEN_AI_RESPONSE_FINISH_REASONS], + ("length", "stop"), + ) + + def test_llm_span_finish_reasons_deduplicated_from_output_messages(self): + invocation = LLMInvocation( + request_model="model-output-dedup", + provider="test-provider", + ) + + self.telemetry_handler.start_llm(invocation) + assert invocation.span is not None + invocation.output_messages = [ + _create_output_message("response-1", finish_reason="stop"), + _create_output_message("response-2", finish_reason="length"), + _create_output_message("response-3", finish_reason="stop"), + ] + self.telemetry_handler.stop_llm(invocation) + + span = _get_single_span(self.span_exporter) + attrs = _get_span_attributes(span) + self.assertEqual( + attrs[GenAI.GEN_AI_RESPONSE_FINISH_REASONS], + ("length", "stop"), + ) + + def test_llm_span_uses_expected_schema_url(self): + invocation = LLMInvocation( + request_model="schema-model", + provider="schema-provider", + ) + + self.telemetry_handler.start_llm(invocation) + assert invocation.span is not None + self.telemetry_handler.stop_llm(invocation) + + span = _get_single_span(self.span_exporter) + instrumentation = getattr(span, "instrumentation_scope", None) + if instrumentation is None: + instrumentation = getattr(span, "instrumentation_info", None) + + assert instrumentation is not None + assert ( + getattr(instrumentation, "schema_url", None) + == Schemas.V1_37_0.value + ) @patch_env_vars( stability_mode="gen_ai_latest_experimental", content_capturing="SPAN_ONLY", ) def test_parent_child_span_relationship(self): - message = InputMessage(role="Human", parts=[Text(content="hi")]) - chat_generation = OutputMessage( - role="AI", parts=[Text(content="ok")], finish_reason="stop" - ) + message = _create_input_message("hi") + chat_generation = _create_output_message("ok") with self.telemetry_handler.llm() as parent_invocation: - parent_invocation.request_model = "parent-model" - parent_invocation.input_messages = [message] - parent_invocation.provider = "test-provider" - # Perform things here, calling a tool, processing, etc. + for attr, value in { + "request_model": "parent-model", + "input_messages": [message], + "provider": "test-provider", + }.items(): + setattr(parent_invocation, attr, value) with self.telemetry_handler.llm() as child_invocation: - child_invocation.request_model = "child-model" - child_invocation.input_messages = [message] - child_invocation.provider = "test-provider" - # Perform things here, calling a tool, processing, etc. + for attr, value in { + "request_model": "child-model", + "input_messages": [message], + "provider": "test-provider", + }.items(): + setattr(child_invocation, attr, value) # Stop child first by exiting inner context child_invocation.output_messages = [chat_generation] # Then stop parent by exiting outer context @@ -263,7 +429,7 @@ def test_llm_context_manager_error_path_records_error_status_and_attrs( class BoomError(RuntimeError): pass - message = InputMessage(role="user", parts=[Text(content="hi")]) + message = _create_input_message("hi", role="user") invocation = LLMInvocation( request_model="test-model", input_messages=[message], @@ -272,18 +438,32 @@ class BoomError(RuntimeError): with self.assertRaises(BoomError): with self.telemetry_handler.llm(invocation): - # Simulate user code that fails inside the invocation + for attr, value in { + "max_tokens": 128, + "seed": 123, + "finish_reasons": ["error"], + "response_model_name": "error-model", + "response_id": "error-response", + "input_tokens": 11, + "output_tokens": 22, + }.items(): + setattr(invocation, attr, value) raise BoomError("boom") - # One span should have been exported and should be in error state - spans = self.span_exporter.get_finished_spans() - assert len(spans) == 1 - span = spans[0] + span = _get_single_span(self.span_exporter) assert span.status.status_code == StatusCode.ERROR - assert ( - span.attributes.get(ErrorAttributes.ERROR_TYPE) - == BoomError.__qualname__ + _assert_span_time_order(span) + span_attrs = _get_span_attributes(span) + _assert_span_attributes( + span_attrs, + { + ErrorAttributes.ERROR_TYPE: BoomError.__qualname__, + GenAI.GEN_AI_REQUEST_MAX_TOKENS: 128, + GenAI.GEN_AI_REQUEST_SEED: 123, + GenAI.GEN_AI_RESPONSE_FINISH_REASONS: ("error",), + GenAI.GEN_AI_RESPONSE_MODEL: "error-model", + GenAI.GEN_AI_RESPONSE_ID: "error-response", + GenAI.GEN_AI_USAGE_INPUT_TOKENS: 11, + GenAI.GEN_AI_USAGE_OUTPUT_TOKENS: 22, + }, ) - assert span.start_time is not None - assert span.end_time is not None - assert span.end_time >= span.start_time diff --git a/util/opentelemetry-util-http/src/opentelemetry/util/http/__init__.py b/util/opentelemetry-util-http/src/opentelemetry/util/http/__init__.py index 6c1403fc4..e23e03ded 100644 --- a/util/opentelemetry-util-http/src/opentelemetry/util/http/__init__.py +++ b/util/opentelemetry-util-http/src/opentelemetry/util/http/__init__.py @@ -19,7 +19,7 @@ from re import IGNORECASE as RE_IGNORECASE from re import compile as re_compile from re import search -from typing import Callable, Iterable, overload +from typing import Callable, Iterable, Optional, overload from urllib.parse import parse_qs, urlencode, urlparse, urlunparse from opentelemetry.semconv._incubating.attributes.http_attributes import ( @@ -34,6 +34,10 @@ NET_HOST_NAME, NET_HOST_PORT, ) +from opentelemetry.semconv._incubating.attributes.user_agent_attributes import ( + UserAgentSyntheticTypeValues, +) +from opentelemetry.util.http.constants import BOT_PATTERNS, TEST_PATTERNS OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SANITIZE_FIELDS = ( "OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SANITIZE_FIELDS" @@ -301,3 +305,30 @@ def redact_url(url: str) -> str: url = remove_url_credentials(url) url = redact_query_parameters(url) return url + + +def detect_synthetic_user_agent(user_agent: Optional[str]) -> Optional[str]: + """ + Detect synthetic user agent type based on user agent string contents. + + Args: + user_agent: The user agent string to analyze + + Returns: + UserAgentSyntheticTypeValues.TEST if user agent contains any pattern from TEST_PATTERNS + UserAgentSyntheticTypeValues.BOT if user agent contains any pattern from BOT_PATTERNS + None otherwise + + Note: Test patterns take priority over bot patterns. + """ + if not user_agent: + return None + + user_agent_lower = user_agent.lower() + + if any(test_pattern in user_agent_lower for test_pattern in TEST_PATTERNS): + return UserAgentSyntheticTypeValues.TEST.value + if any(bot_pattern in user_agent_lower for bot_pattern in BOT_PATTERNS): + return UserAgentSyntheticTypeValues.BOT.value + + return None diff --git a/util/opentelemetry-util-http/src/opentelemetry/util/http/constants.py b/util/opentelemetry-util-http/src/opentelemetry/util/http/constants.py new file mode 100644 index 000000000..e49799dac --- /dev/null +++ b/util/opentelemetry-util-http/src/opentelemetry/util/http/constants.py @@ -0,0 +1,34 @@ +# 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. + +""" +Constants for OpenTelemetry HTTP utilities. + +This module contains configuration constants and pattern definitions used +by HTTP instrumentation utilities for various features like synthetic user +agent detection. +""" + +# Test patterns to detect in user agent strings (case-insensitive) +# These patterns indicate synthetic test traffic +TEST_PATTERNS = [ + "alwayson", +] + +# Bot patterns to detect in user agent strings (case-insensitive) +# These patterns indicate automated bot traffic +BOT_PATTERNS = [ + "googlebot", + "bingbot", +] diff --git a/util/opentelemetry-util-http/tests/test_detect_synthetic_user_agent.py b/util/opentelemetry-util-http/tests/test_detect_synthetic_user_agent.py new file mode 100644 index 000000000..2d9d3e991 --- /dev/null +++ b/util/opentelemetry-util-http/tests/test_detect_synthetic_user_agent.py @@ -0,0 +1,88 @@ +# 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. + +import unittest + +from opentelemetry.semconv._incubating.attributes.user_agent_attributes import ( + UserAgentSyntheticTypeValues, +) +from opentelemetry.util.http import detect_synthetic_user_agent + + +class TestDetectSyntheticUserAgent(unittest.TestCase): + def test_detect_bot_googlebot(self): + """Test detection of googlebot user agent.""" + user_agent = "Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)" + result = detect_synthetic_user_agent(user_agent) + self.assertEqual(result, UserAgentSyntheticTypeValues.BOT.value) + + def test_detect_bot_bingbot(self): + """Test detection of bingbot user agent.""" + user_agent = "Mozilla/5.0 (compatible; bingbot/2.0; +http://www.bing.com/bingbot.htm)" + result = detect_synthetic_user_agent(user_agent) + self.assertEqual(result, UserAgentSyntheticTypeValues.BOT.value) + + def test_detect_test_alwayson(self): + """Test detection of alwayson test user agent.""" + user_agent = "AlwaysOn-Monitor/1.0" + result = detect_synthetic_user_agent(user_agent) + self.assertEqual(result, UserAgentSyntheticTypeValues.TEST.value) + + def test_case_insensitive_detection(self): + """Test that detection is case insensitive.""" + # Test uppercase patterns + user_agent_bot = "GOOGLEBOT/2.1" + result = detect_synthetic_user_agent(user_agent_bot) + self.assertEqual(result, UserAgentSyntheticTypeValues.BOT.value) + + user_agent_test = "ALWAYSON-Monitor/1.0" + result = detect_synthetic_user_agent(user_agent_test) + self.assertEqual(result, UserAgentSyntheticTypeValues.TEST.value) + + def test_normal_user_agent_not_detected(self): + """Test that normal browser user agents are not detected as synthetic.""" + user_agent = ( + "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36" + ) + result = detect_synthetic_user_agent(user_agent) + self.assertIsNone(result) + + def test_none_user_agent(self): + """Test that None user agent returns None.""" + result = detect_synthetic_user_agent(None) + self.assertIsNone(result) + + def test_empty_user_agent(self): + """Test that empty user agent returns None.""" + result = detect_synthetic_user_agent("") + self.assertIsNone(result) + + def test_substring_match(self): + """Test that substrings are detected correctly.""" + # Test googlebot in middle of string + user_agent = "MyApp/1.0 googlebot crawler" + result = detect_synthetic_user_agent(user_agent) + self.assertEqual(result, UserAgentSyntheticTypeValues.BOT.value) + + # Test alwayson in middle of string + user_agent = "TestFramework/1.0 alwayson monitoring" + result = detect_synthetic_user_agent(user_agent) + self.assertEqual(result, UserAgentSyntheticTypeValues.TEST.value) + + def test_priority_test_over_bot(self): + """Test that test patterns take priority over bot patterns.""" + user_agent = "alwayson-googlebot/1.0" + result = detect_synthetic_user_agent(user_agent) + # alwayson should be checked first and return 'test' + self.assertEqual(result, UserAgentSyntheticTypeValues.TEST.value)