Skip to content

Commit 2926372

Browse files
authored
Merge branch 'main' into genai-utils/agent-invocation
2 parents a8f1839 + bca3c85 commit 2926372

21 files changed

Lines changed: 398 additions & 30 deletions

File tree

CHANGELOG.md

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1515

1616
- Bump `pylint` to `4.0.5`
1717
([#4244](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/4244))
18+
- `opentelemetry-instrumentation-sqlite3`: Add uninstrument, error status, suppress, and no-op tests
19+
([#4335](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/4335))
20+
21+
### Fixed
22+
23+
- `opentelemetry-instrumentation-pika` Use `ObjectProxy` instead of `BaseObjectProxy` for `ReadyMessagesDequeProxy` to restore iterability with wrapt 2.x
24+
([#4461](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/4461))
25+
- `opentelemetry-instrumentation-dbapi` Use `ObjectProxy` instead of `BaseObjectProxy` for `TracedCursorProxy` to restore iterability with wrapt 2.x
26+
([#4427](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/4427))
27+
- `opentelemetry-instrumentation-flask`: Clean up environ keys in `_teardown_request` to prevent duplicate execution
28+
([#4341](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/4341))
29+
- `opentelemetry-instrumentation-flask`: Stop reading the deprecated (from 3.1) `flask.__version__` attribute; resolve the Flask version via `importlib.metadata`
30+
([#4422](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/4422))
1831

1932
### Breaking changes
2033

@@ -40,6 +53,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
4053

4154
### Fixed
4255

56+
- `opentelemetry-instrumentation-celery`: Coerce non-string values to strings in `CeleryGetter.get()` to prevent `TypeError` in `TraceState.from_header()` when Celery request attributes contain ints
57+
([#4360](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/4360))
4358
- `opentelemetry-docker-tests`: Replace deprecated `SpanAttributes` from `opentelemetry.semconv.trace` with `opentelemetry.semconv._incubating.attributes`
4459
([#4339](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/4339))
4560
- `opentelemetry-instrumentation-confluent-kafka`: Skip `recv` span creation when `poll()` returns no message or `consume()` returns an empty list, avoiding empty spans on idle polls
@@ -1162,15 +1177,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
11621177

11631178
### Added
11641179

1165-
- Support `aio_pika` 9.x (([#1670](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1670])
1180+
- Support `aio_pika` 9.x ([#1670](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1670))
11661181
- `opentelemetry-instrumentation-redis` Add `sanitize_query` config option to allow query sanitization. ([#1572](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1572))
11671182
- `opentelemetry-instrumentation-elasticsearch` Add optional db.statement query sanitization.
11681183
([#1598](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1598))
11691184
- `opentelemetry-instrumentation-celery` Record exceptions as events on the span.
11701185
([#1573](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1573))
11711186
- Add metric instrumentation for urllib
11721187
([#1553](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1553))
1173-
- `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
1188+
- `opentelemetry/sdk/extension/aws` Implement [`aws.ecs.*`](https://opentelemetry.io/docs/specs/semconv/resource/cloud-provider/aws/ecs/) and [`aws.logs.*`](https://opentelemetry.io/docs/specs/semconv/resource/cloud-provider/aws/logs/) resource attributes in the `AwsEcsResourceDetector` detector when the ECS Metadata v4 is available
11741189
([#1212](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1212))
11751190
- `opentelemetry-instrumentation-aio-pika` Support `aio_pika` 8.x
11761191
([#1481](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1481))
@@ -1366,7 +1381,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
13661381
- Add a test for asgi using NoOpTracerProvider
13671382
([#1367](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1367))
13681383

1369-
## [1.12.0rc2-0.32b0](https://github.com/open-telemetry/opentelemetry-python/releases/tag/v1.12.0rc2-0.32b0) - 2022-07-01
1384+
## [1.12.0rc2-0.32b0](https://github.com/open-telemetry/opentelemetry-python/releases/tag/v1.12.0rc2) - 2022-07-01
13701385

13711386
- Pyramid: Only categorize 500s server exceptions as errors
13721387
([#1037](https://github.com/open-telemetry/opentelemetry-python-contrib/issues/1037))
@@ -2000,7 +2015,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
20002015
- `opentelemetry-instrumentation-grpc` Add tests for grpc span attributes, grpc `abort()` conditions
20012016
([#236](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/236))
20022017
- Add README and example app for Prometheus Remote Write Exporter
2003-
([#227](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/227]))
2018+
([#227](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/227))
20042019
- `opentelemetry-instrumentation-botocore` Adds a field to report the number of retries it take to complete an API call
20052020
([#275](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/275))
20062021
- `opentelemetry-instrumentation-requests` Use instanceof to check if responses are valid Response objects
@@ -2387,8 +2402,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
23872402
- `opentelemetry-ext-http-requests` Updates for core library changes
23882403

23892404
- `Added support for PyPy3` Initial release
2390-
2391-
## [#1033](https://github.com/open-telemetryopentelemetry-python-contrib/issues/1033)
2405+
([#1033](https://github.com/open-telemetry/opentelemetry-python-contrib/issues/1033))
23922406

23932407
## Version 0.1a0 (2019-09-30)
23942408

CONTRIBUTING.md

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ Please also read the [OpenTelemetry Contributor Guide](https://github.com/open-t
4242
- [Guideline for GenAI instrumentations](#guideline-for-genai-instrumentations)
4343
- [Get Involved](#get-involved)
4444
- [Expectations from contributors](#expectations-from-contributors)
45+
- [Guidelines for native OpenTelemetry instrumentation](#guidelines-for-native-opentelemetry-instrumentation)
4546
- [Updating supported Python versions](#updating-supported-python-versions)
4647
- [Bumping the Python baseline](#bumping-the-python-baseline)
4748
- [Adding support for a new Python release](#adding-support-for-a-new-python-release)
@@ -293,7 +294,7 @@ Some of the tox targets install packages from the [OpenTelemetry Python Core Rep
293294
CORE_REPO_SHA=c49ad57bfe35cfc69bfa863d74058ca9bec55fc3 tox
294295
```
295296

296-
The continuous integration overrides that environment variable with as per the configuration [here](https://github.com/open-telemetry/opentelemetry-python-contrib/blob/main/.github/workflows/test_0.yml#L14).
297+
The continuous integration overrides that environment variable with as per the configuration [here](https://github.com/open-telemetry/opentelemetry-python-contrib/blob/main/.github/workflows/test.yml#L17).
297298

298299
## Style Guide
299300

@@ -307,6 +308,9 @@ The continuous integration overrides that environment variable with as per the c
307308

308309
Below is a checklist of things to be mindful of when implementing a new instrumentation or working on a specific instrumentation. It is one of our goals as a community to keep the implementation specific details of instrumentations as similar across the board as possible for ease of testing and feature parity. It is also good to abstract as much common functionality as possible.
309310

311+
- Find or create a new [Issue](https://github.com/open-telemetry/opentelemetry-python-contrib/issues) describing the tool or framework to instrument and its use cases to support with OpenTelemetry.
312+
- Be familiar with the [expectations from contributors](#expectations-from-contributors) that apply.
313+
- If you're a tool or framework maintainer, please consider using the OpenTelemetry API directly to support [native instrumentation](#guidelines-for-native-opentelemetry-instrumentation) instead of adding a new community instrumentation library.
310314
- Follow semantic conventions
311315
- The instrumentation should follow the semantic conventions defined [here](https://github.com/open-telemetry/semantic-conventions/tree/main/docs).
312316
- To ensure consistency, we encourage contributions that align with [STABLE](https://opentelemetry.io/docs/specs/otel/document-status/#lifecycle-status) semantic conventions if available. This approach helps us avoid potential confusion and reduces the need to support multiple outdated versions of semantic conventions. However, we are still open to considering exceptional cases where changes are well justified.
@@ -384,6 +388,16 @@ Instrumentations that relate to [Generative AI](https://opentelemetry.io/docs/sp
384388

385389
OpenTelemetry is an open source community, and as such, greatly encourages contributions from anyone interested in the project. With that being said, there is a certain level of expectation from contributors even after a pull request is merged, specifically pertaining to instrumentations. The OpenTelemetry Python community expects contributors to maintain a level of support and interest in the instrumentations they contribute. This is to ensure that the instrumentation does not become stale and still functions the way the original contributor intended. Some instrumentations also pertain to libraries that the current members of the community are not so familiar with, so it is necessary to rely on the expertise of the original contributing parties.
386390

391+
### Guidelines for native OpenTelemetry instrumentation
392+
393+
The preferred approach to supporting instrumentation of a tool or framework is native OpenTelemetry instrumentation. Compared to adding a new instrumentation library to this community repository, native instrumentation is better for:
394+
395+
* continued support by framework experts long-term
396+
* access to more context for critical paths and correlations
397+
* granularity and performance
398+
399+
To support native instrumentation, the tool or framework should use the [OpenTelemetry API](https://opentelemetry-python.readthedocs.io/en/latest/) directly to emit traces, metrics, and logs.
400+
387401
## Updating supported Python versions
388402

389403
### Bumping the Python baseline

docs-requirements.txt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,11 +44,13 @@ PyMySQL~=1.1.1
4444
pymssql~=2.3.2
4545
pyramid>=1.7
4646
redis>=2.6
47-
remoulade>=0.50
47+
remoulade[limits]>=5.0.0 ; python_version >= "3.12"
48+
remoulade>=0.50.0 ; python_version < "3.12"
4849
sqlalchemy>=1.0
4950
starlette~=0.50
5051
tornado>=5.1.1
5152
tortoise-orm>=0.17.0
53+
wrapt~=2.1
5254

5355
# required by opamp
5456
uuid_utils

docs/nitpick-exceptions.ini

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ py-class=
4444
psycopg.Connection
4545
psycopg.AsyncConnection
4646
ObjectProxy
47+
wrapt.proxies.ObjectProxy
4748
fastapi.applications.FastAPI
4849
starlette.applications.Starlette
4950
_contextvars.Token

instrumentation-genai/opentelemetry-instrumentation-openai-v2/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## Unreleased
99

10+
- Import `OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT` from
11+
`opentelemetry.util.genai.environment_variables` instead of re-defining it locally,
12+
making `opentelemetry-util-genai` the single source of truth for this constant.
13+
([#4455](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/4455))
1014
- Fix compatibility with wrapt 2.x by using positional arguments in `wrap_function_wrapper()` calls
1115
([#4445](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/4445))
1216
- Fix `ChoiceBuffer` crash on streaming tool-call deltas with `arguments=None`

instrumentation-genai/opentelemetry-instrumentation-openai-v2/src/opentelemetry/instrumentation/openai_v2/utils.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@
3636
error_attributes as ErrorAttributes,
3737
)
3838
from opentelemetry.trace.status import Status, StatusCode
39+
from opentelemetry.util.genai.environment_variables import (
40+
OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT,
41+
)
3942
from opentelemetry.util.genai.types import (
4043
InputMessage,
4144
LLMInvocation, # pylint: disable=no-name-in-module # TODO: migrate to InferenceInvocation
@@ -45,10 +48,6 @@
4548
ToolCallResponse,
4649
)
4750

48-
OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT = (
49-
"OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT"
50-
)
51-
5251

5352
def is_content_enabled() -> bool:
5453
capture_content = environ.get(

instrumentation/opentelemetry-instrumentation-celery/src/opentelemetry/instrumentation/celery/__init__.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,8 +104,18 @@ def get(self, carrier, key):
104104
value = getattr(carrier, key, None)
105105
if value is None:
106106
return None
107-
if isinstance(value, str) or not isinstance(value, Iterable):
107+
# Celery's Context copies all message properties as instance
108+
# attributes, including non-string values like timelimit (tuple
109+
# of ints). The TextMapPropagator contract requires string
110+
# values, so coerce anything that isn't already a string.
111+
if isinstance(value, str):
108112
value = (value,)
113+
elif isinstance(value, Iterable):
114+
value = tuple(
115+
str(v) if not isinstance(v, str) else v for v in value
116+
)
117+
else:
118+
value = (str(value),)
109119
return value
110120

111121
def keys(self, carrier):

instrumentation/opentelemetry-instrumentation-celery/tests/test_getter.py

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,40 @@ def test_get_iter(self):
3636
getter = CeleryGetter()
3737
mock_obj.test = ["val"]
3838
val = getter.get(mock_obj, "test")
39-
self.assertEqual(val, ["val"])
39+
self.assertEqual(val, ("val",))
40+
41+
def test_get_int(self):
42+
"""Non-string scalar values should be coerced to strings.
43+
44+
Celery's Context stores some attributes as ints (e.g. priority).
45+
The TextMapPropagator contract requires string values; passing
46+
an int to re.split() in TraceState.from_header() causes a
47+
TypeError.
48+
"""
49+
mock_obj = mock.Mock()
50+
getter = CeleryGetter()
51+
mock_obj.test = 42
52+
val = getter.get(mock_obj, "test")
53+
self.assertEqual(val, ("42",))
54+
55+
def test_get_iter_with_non_string_elements(self):
56+
"""Iterable values containing non-strings should be coerced.
57+
58+
Celery's timelimit attribute is a tuple of ints, e.g. (300, 60).
59+
"""
60+
mock_obj = mock.Mock()
61+
getter = CeleryGetter()
62+
mock_obj.test = (300, 60)
63+
val = getter.get(mock_obj, "test")
64+
self.assertEqual(val, ("300", "60"))
65+
66+
def test_get_iter_with_mixed_types(self):
67+
"""Iterables with a mix of strings and non-strings."""
68+
mock_obj = mock.Mock()
69+
getter = CeleryGetter()
70+
mock_obj.test = ["val", 123]
71+
val = getter.get(mock_obj, "test")
72+
self.assertEqual(val, ("val", "123"))
4073

4174
def test_keys(self):
4275
getter = CeleryGetter()

instrumentation/opentelemetry-instrumentation-dbapi/src/opentelemetry/instrumentation/dbapi/__init__.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -177,8 +177,12 @@
177177

178178
try:
179179
# wrapt 2.0.0+
180-
from wrapt import BaseObjectProxy # pylint: disable=no-name-in-module
180+
from wrapt import ( # pylint: disable=no-name-in-module
181+
BaseObjectProxy,
182+
ObjectProxy,
183+
)
181184
except ImportError:
185+
from wrapt import ObjectProxy
182186
from wrapt import ObjectProxy as BaseObjectProxy
183187

184188
from opentelemetry import trace as trace_api
@@ -805,14 +809,14 @@ async def traced_execution_async(
805809

806810

807811
# pylint: disable=abstract-method,no-member
808-
class TracedCursorProxy(BaseObjectProxy, Generic[CursorT]):
812+
class TracedCursorProxy(ObjectProxy, Generic[CursorT]):
809813
# pylint: disable=unused-argument
810814
def __init__(
811815
self,
812816
cursor: CursorT,
813817
db_api_integration: DatabaseApiIntegration,
814818
):
815-
BaseObjectProxy.__init__(self, cursor)
819+
ObjectProxy.__init__(self, cursor)
816820
self._self_cursor_tracer = CursorTracer[CursorT](db_api_integration)
817821

818822
def execute(self, *args: Any, **kwargs: Any):

instrumentation/opentelemetry-instrumentation-dbapi/tests/test_dbapi_integration.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -306,6 +306,20 @@ def test_executemany(self):
306306
span = spans_list[0]
307307
self.assertEqual(span.attributes[DB_STATEMENT], "Test query")
308308

309+
# pylint: disable=no-self-use
310+
def test_executemany_iterable_cursor(self):
311+
db_integration = dbapi.DatabaseApiIntegration(
312+
"instrumenting_module_test_name", "testcomponent"
313+
)
314+
mock_connection = db_integration.wrapped_connection(
315+
mock_connect, {}, {}
316+
)
317+
cursor = mock_connection.cursor()
318+
cursor.executemany("Test query")
319+
320+
for _row in cursor:
321+
pass
322+
309323
def test_executemany_comment(self):
310324
connect_module = mock.MagicMock()
311325
connect_module.__name__ = "test"
@@ -1296,13 +1310,17 @@ def __init__(self) -> None:
12961310
self._cnx._cmysql.get_client_info = mock.MagicMock(
12971311
return_value="1.2.3"
12981312
)
1313+
self._items = []
12991314

13001315
# pylint: disable=unused-argument, no-self-use
13011316
def execute(self, query, params=None, throw_exception=False):
13021317
if throw_exception:
13031318
# pylint: disable=broad-exception-raised
13041319
raise Exception("Test Exception")
13051320

1321+
def __iter__(self):
1322+
yield from self._items
1323+
13061324
# pylint: disable=unused-argument, no-self-use
13071325
def executemany(self, query, params=None, throw_exception=False):
13081326
if throw_exception:

0 commit comments

Comments
 (0)