Skip to content

Commit 9bb3221

Browse files
Merge branch 'main' into feat/openai-responses-create-instrumentation-first-part
2 parents 0293f21 + 2caa924 commit 9bb3221

8 files changed

Lines changed: 105 additions & 15 deletions

File tree

CHANGELOG.md

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2626
([#4427](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/4427))
2727
- `opentelemetry-instrumentation-flask`: Clean up environ keys in `_teardown_request` to prevent duplicate execution
2828
([#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))
31+
- `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
32+
([#4360](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/4360))
33+
- `opentelemetry-instrumentation-aiohttp-server`: Use `canonical` attribute of the `Resource` as a span name
34+
([#3896](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3896))
2935

3036
### Breaking changes
3137

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

5258
### Fixed
5359

54-
- `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
55-
([#4360](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/4360))
5660
- `opentelemetry-docker-tests`: Replace deprecated `SpanAttributes` from `opentelemetry.semconv.trace` with `opentelemetry.semconv._incubating.attributes`
5761
([#4339](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/4339))
5862
- `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

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: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,8 @@ 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

instrumentation/opentelemetry-instrumentation-aiohttp-server/src/opentelemetry/instrumentation/aiohttp_server/__init__.py

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -252,14 +252,21 @@ def _parse_duration_attrs(
252252

253253

254254
def get_default_span_name(request: web.Request) -> str:
255-
"""Default implementation for get_default_span_details
255+
"""Returns the span name.
256256
Args:
257257
request: the request object itself.
258258
Returns:
259-
The span name.
259+
The span name as "{method} {canonical_name}" of a resource if possible or just "{method}".
260260
"""
261-
span_name = request.path.strip() or f"HTTP {request.method}"
262-
return span_name
261+
try:
262+
resource = request.match_info.route.resource
263+
path = resource.canonical
264+
except AttributeError:
265+
path = ""
266+
267+
if path:
268+
return f"{request.method} {path}"
269+
return f"{request.method}"
263270

264271

265272
def _get_view_func(request: web.Request) -> str:

instrumentation/opentelemetry-instrumentation-aiohttp-server/tests/test_aiohttp_server_integration.py

Lines changed: 63 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,15 @@ async def fixture_server_fixture(tracer, aiohttp_server, suppress):
143143
AioHttpServerInstrumentor().instrument(tracer_provider=tracer_provider)
144144

145145
app = aiohttp.web.Application()
146-
app.add_routes([aiohttp.web.get("/test-path", default_handler)])
146+
app.add_routes(
147+
[
148+
aiohttp.web.get("/test-path", default_handler),
149+
aiohttp.web.get("/test-path/{url_param}", default_handler),
150+
aiohttp.web.get(
151+
"/object/{object_id}/action/{another_param}", default_handler
152+
),
153+
]
154+
)
147155
if suppress:
148156
with suppress_http_instrumentation():
149157
server = await aiohttp_server(app)
@@ -198,6 +206,60 @@ async def test_status_code_instrumentation(
198206
assert url == span.attributes[HTTP_TARGET]
199207

200208

209+
@pytest.mark.asyncio
210+
@pytest.mark.parametrize(
211+
"span_name, example_paths",
212+
[
213+
(
214+
"GET /test-path/{url_param}",
215+
(
216+
"/test-path/foo",
217+
"/test-path/bar",
218+
),
219+
),
220+
(
221+
"GET /object/{object_id}/action/{another_param}",
222+
(
223+
"/object/1/action/bar",
224+
"/object/234/action/baz",
225+
),
226+
),
227+
(
228+
"GET",
229+
(
230+
"/i/dont/exist",
231+
"/me-neither",
232+
),
233+
),
234+
],
235+
)
236+
async def test_url_params_instrumentation(
237+
tracer,
238+
server_fixture,
239+
aiohttp_client,
240+
span_name,
241+
example_paths,
242+
):
243+
_, memory_exporter = tracer
244+
server, _ = server_fixture
245+
246+
assert len(memory_exporter.get_finished_spans()) == 0
247+
248+
client = await aiohttp_client(server)
249+
for path in example_paths:
250+
await client.get(path)
251+
252+
assert len(memory_exporter.get_finished_spans()) == 2
253+
254+
for request_path, span in zip(
255+
example_paths, memory_exporter.get_finished_spans()
256+
):
257+
assert span_name == span.name
258+
assert request_path == span.attributes[HTTP_TARGET]
259+
full_url = f"http://{server.host}:{server.port}{request_path}"
260+
assert full_url == span.attributes[HTTP_URL]
261+
262+
201263
@pytest.mark.asyncio
202264
@pytest.mark.parametrize("suppress", [True])
203265
async def test_suppress_instrumentation(

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

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -301,10 +301,6 @@ def response_hook(span: Span, status: str, response_headers: List):
301301
)
302302

303303
_logger = getLogger(__name__)
304-
# Global constants for Flask 3.1+ streaming context cleanup
305-
_IS_FLASK_31_PLUS = hasattr(flask, "__version__") and package_version.parse(
306-
flask.__version__
307-
) >= package_version.parse("3.1.0")
308304

309305
_ENVIRON_STARTTIME_KEY = "opentelemetry-flask.starttime_key"
310306
_ENVIRON_SPAN_KEY = "opentelemetry-flask.span_key"
@@ -316,6 +312,11 @@ def response_hook(span: Span, status: str, response_headers: List):
316312

317313
flask_version = version("flask")
318314

315+
# Global constant for Flask 3.1+ streaming context cleanup
316+
_IS_FLASK_31_PLUS = package_version.parse(
317+
flask_version
318+
) >= package_version.parse("3.1.0")
319+
319320
if package_version.parse(flask_version) >= package_version.parse("2.2.0"):
320321

321322
def _request_ctx_ref() -> weakref.ReferenceType:

instrumentation/opentelemetry-instrumentation-flask/tests/test_flask_compatibility.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import io
2121
import threading
2222
import time
23+
from importlib.metadata import version
2324
from unittest import mock, skipIf
2425

2526
import flask
@@ -37,7 +38,7 @@
3738
class TestFlaskCompatibility(WsgiTestBase):
3839
def setUp(self):
3940
super().setUp()
40-
self.flask_version = flask.__version__
41+
self.flask_version = version("flask")
4142

4243
def test_streaming_response_context_cleanup(self):
4344
"""Test that streaming responses properly clean up context"""

tests/opentelemetry-docker-tests/tests/test-requirements.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ python-dotenv==0.21.1
5858
pytz==2024.1
5959
PyYAML==5.3.1
6060
redis==5.0.1
61-
requests==2.25.0
61+
requests==2.31.0
6262
six==1.16.0
6363
SQLAlchemy==1.4.52
6464
texttable==1.7.0

0 commit comments

Comments
 (0)