Skip to content

Commit 0afbfa4

Browse files
committed
Merge branch 'main' into rich-console/suppress-resource
2 parents f1b4671 + d9b40be commit 0afbfa4

File tree

26 files changed

+848
-347
lines changed

26 files changed

+848
-347
lines changed

CHANGELOG.md

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,35 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1111
1212
## Unreleased
1313

14-
### Added
14+
### Added
15+
16+
- `opentelemetry-instrumentation-aiohttp-client`: add support for url exclusions via `OTEL_PYTHON_EXCLUDED_URLS` / `OTEL_PYTHON_AIOHTTP_CLIENT_EXCLUDED_URLS`
17+
([#3850](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3850))
18+
- `opentelemetry-instrumentation-httpx`: add support for url exclusions via `OTEL_PYTHON_EXCLUDED_URLS` / `OTEL_PYTHON_HTTPX_EXCLUDED_URLS`
19+
([#3837](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3837))
20+
- `opentelemetry-instrumentation-flask`: improve readthedocs for sqlcommenter configuration.
21+
([#3883](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3883))
22+
- `opentelemetry-instrumentation-sqlalchemy`: improve readthedocs for sqlcommenter configuration.
23+
([#3886](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3886))
24+
- `opentelemetry-instrumentation-mysql`, `opentelemetry-instrumentation-mysqlclient`, `opentelemetry-instrumentation-pymysql`: improve readthedocs for sqlcommenter configuration.
25+
([#3885](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3885))
26+
- `opentelemetry-instrumentation-django`: improve readthedocs for sqlcommenter configuration.
27+
([#3884](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3884))
28+
- `opentelemetry-exporter-richconsole`: Add support for suppressing resource information
29+
([#3898](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3898))
30+
31+
### Fixed
1532

16-
- `opentelemetry-instrumentation-richconsole`: Add support for suppressing resource information
33+
- `opentelemetry-instrumentation-botocore`: Handle dict input in _decode_tool_use for Bedrock streaming
34+
([#3875](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3875))
35+
- `opentelemetry-instrumentation-aiohttp-client`, `opentelemetry-instrumentation-aiohttp-server`: Fix readme links and text
36+
([#3902](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3902))
37+
- `opentelemetry-instrumentation-aws-lambda`: Fix ImportError with slash-delimited handler paths
38+
([#3894](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3894))
39+
- `opentelemetry-exporter-richconsole`: Prevent deadlock when parent span is not part of the batch
40+
([#3900](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3900))
41+
- `opentelemetry-instrumentation-aiohttp-server`: delay initialization of tracer, meter and excluded urls to instrumentation for testability
42+
([#3836](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3836))
1743

1844
## Version 1.38.0/0.59b0 (2025-10-16)
1945

exporter/opentelemetry-exporter-richconsole/src/opentelemetry/exporter/richconsole/__init__.py

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -76,12 +76,16 @@ def _ns_to_time(nanoseconds):
7676
return ts.strftime("%H:%M:%S.%f")
7777

7878

79-
def _child_to_tree(child: Tree, span: ReadableSpan, *, suppress_resource: bool):
79+
def _child_to_tree(
80+
child: Tree, span: ReadableSpan, *, suppress_resource: bool
81+
):
8082
child.add(
8183
Text.from_markup(f"[bold cyan]Kind :[/bold cyan] {span.kind.name}")
8284
)
8385
_add_status(child, span)
84-
_child_add_optional_attributes(child, span, suppress_resource=suppress_resource)
86+
_child_add_optional_attributes(
87+
child, span, suppress_resource=suppress_resource
88+
)
8589

8690

8791
def _add_status(child: Tree, span: ReadableSpan):
@@ -106,7 +110,9 @@ def _add_status(child: Tree, span: ReadableSpan):
106110
)
107111

108112

109-
def _child_add_optional_attributes(child: Tree, span: ReadableSpan, *, suppress_resource: bool):
113+
def _child_add_optional_attributes(
114+
child: Tree, span: ReadableSpan, *, suppress_resource: bool
115+
):
110116
if span.events:
111117
events = child.add(
112118
label=Text.from_markup("[bold cyan]Events :[/bold cyan] ")
@@ -165,30 +171,41 @@ def export(self, spans: typing.Sequence[ReadableSpan]) -> SpanExportResult:
165171
if not spans:
166172
return SpanExportResult.SUCCESS
167173

168-
for tree in self.spans_to_tree(spans, suppress_resource=self.suppress_resource).values():
174+
for tree in self.spans_to_tree(
175+
spans, suppress_resource=self.suppress_resource
176+
).values():
169177
self.console.print(tree)
170178

171179
return SpanExportResult.SUCCESS
172180

173181
@staticmethod
174-
def spans_to_tree(spans: typing.Sequence[ReadableSpan], *, suppress_resource: bool = False) -> Dict[str, Tree]:
182+
def spans_to_tree(
183+
spans: typing.Sequence[ReadableSpan],
184+
*,
185+
suppress_resource: bool = False,
186+
) -> Dict[str, Tree]:
175187
trees = {}
176188
parents = {}
177189
spans = list(spans)
190+
span_ids = {s.context.span_id for s in spans}
178191
while spans:
179192
for span in spans:
180-
if not span.parent:
193+
if not span.parent or span.parent.span_id not in span_ids:
181194
trace_id = opentelemetry.trace.format_trace_id(
182195
span.context.trace_id
183196
)
184-
trees[trace_id] = Tree(label=f"Trace {trace_id}")
185-
child = trees[trace_id].add(
197+
tree = trees.setdefault(
198+
trace_id, Tree(label=f"Trace {trace_id}")
199+
)
200+
child = tree.add(
186201
label=Text.from_markup(
187202
f"[blue][{_ns_to_time(span.start_time)}][/blue] [bold]{span.name}[/bold], span {opentelemetry.trace.format_span_id(span.context.span_id)}"
188203
)
189204
)
190205
parents[span.context.span_id] = child
191-
_child_to_tree(child, span, suppress_resource=suppress_resource)
206+
_child_to_tree(
207+
child, span, suppress_resource=suppress_resource
208+
)
192209
spans.remove(span)
193210
elif span.parent and span.parent.span_id in parents:
194211
child = parents[span.parent.span_id].add(
@@ -197,6 +214,8 @@ def spans_to_tree(spans: typing.Sequence[ReadableSpan], *, suppress_resource: bo
197214
)
198215
)
199216
parents[span.context.span_id] = child
200-
_child_to_tree(child, span, suppress_resource=suppress_resource)
217+
_child_to_tree(
218+
child, span, suppress_resource=suppress_resource
219+
)
201220
spans.remove(span)
202221
return trees

exporter/opentelemetry-exporter-richconsole/test-requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ pluggy==1.5.0
99
py-cpuinfo==9.0.0
1010
Pygments==2.17.2
1111
pytest==7.4.4
12+
pytest-timeout==2.3.1
1213
rich==13.7.1
1314
tomli==2.0.1
1415
typing_extensions==4.12.2

exporter/opentelemetry-exporter-richconsole/tests/test_rich_exporter.py

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@
1919
import opentelemetry.trace
2020
from opentelemetry.exporter.richconsole import RichConsoleSpanExporter
2121
from opentelemetry.sdk import trace
22-
from opentelemetry.sdk.trace.export import BatchSpanProcessor
2322
from opentelemetry.sdk.resources import Resource
23+
from opentelemetry.sdk.trace.export import BatchSpanProcessor
2424

2525

2626
@pytest.fixture(name="span_processor")
@@ -99,6 +99,19 @@ def test_multiple_traces(tracer_provider):
9999
for child in trees[traceid_1].children[0].children
100100
)
101101

102+
103+
@pytest.mark.timeout(30)
104+
def test_no_deadlock(tracer_provider):
105+
# non-regression test for https://github.com/open-telemetry/opentelemetry-python-contrib/issues/3254
106+
107+
tracer = tracer_provider.get_tracer(__name__)
108+
with tracer.start_as_current_span("parent"):
109+
with tracer.start_as_current_span("child") as child:
110+
pass
111+
112+
RichConsoleSpanExporter.spans_to_tree((child,))
113+
114+
102115
def test_suppress_resource(span_processor):
103116
attributes = {"resource.key": "resource.value"}
104117
resource = Resource(attributes)
@@ -110,7 +123,9 @@ def test_suppress_resource(span_processor):
110123
with tracer.start_as_current_span("child") as child:
111124
pass
112125

113-
trees = RichConsoleSpanExporter.spans_to_tree((parent, child), suppress_resource=True)
126+
trees = RichConsoleSpanExporter.spans_to_tree(
127+
(parent, child), suppress_resource=True
128+
)
114129
assert len(trees) == 1
115130

116131
nodes = [next(t for t in trees.values())]

instrumentation-genai/opentelemetry-instrumentation-google-genai/CHANGELOG.md

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

88
## Unreleased
99

10+
- Minor change to check LRU cache in Completion Hook before acquiring semaphore/thread ([#3907](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3907)).
11+
1012
## Version 0.4b0 (2025-10-16)
1113

1214
- Implement the new semantic convention changes made in https://github.com/open-telemetry/semantic-conventions/pull/2179.

instrumentation-genai/opentelemetry-instrumentation-langchain/examples/manual/README.rst

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
OpenTelemetry Langcahin Instrumentation Example
1+
OpenTelemetry Langchain Instrumentation Example
22
===============================================
33

44
This is an example of how to instrument Langchain when configuring OpenTelemetry SDK and instrumentations manually.
@@ -8,14 +8,14 @@ Traces include details such as the span name and other attributes.
88

99
Note: `.env <.env>`_ file configures additional environment variables:
1010
- :code:`OTEL_LOGS_EXPORTER=otlp` to specify exporter type.
11-
- :code:`OPENAI_API_KEY` open AI key for accessing the OpenAI API.
11+
- :code:`OPENAI_API_KEY` key for accessing the OpenAI API.
1212
- :code:`OTEL_EXPORTER_OTLP_ENDPOINT` to specify the endpoint for exporting traces (default is http://localhost:4317).
1313

1414
Setup
1515
-----
1616

1717
Minimally, update the `.env <.env>`_ file with your :code:`OPENAI_API_KEY`.
18-
An OTLP compatible endpoint should be listening for traces http://localhost:4317.
18+
An OTLP compatible endpoint should be listening for traces at http://localhost:4317.
1919
If not, update :code:`OTEL_EXPORTER_OTLP_ENDPOINT` as well.
2020

2121
Next, set up a virtual environment like this:

instrumentation/opentelemetry-instrumentation-aiohttp-client/README.rst

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ Installation
1919
References
2020
----------
2121

22+
* `OpenTelemetry aiohttp client Instrumentation <https://opentelemetry-python-contrib.readthedocs.io/en/latest/instrumentation/aiohttp_client/aiohttp_client.html>`_
23+
* `aiohttp Tracing Reference <https://docs.aiohttp.org/en/stable/tracing_reference.html>`_
2224
* `OpenTelemetry Project <https://opentelemetry.io/>`_
23-
* `aiohttp client Tracing <https://docs.aiohttp.org/en/stable/tracing_reference.html>`_
2425
* `OpenTelemetry Python Examples <https://github.com/open-telemetry/opentelemetry-python/tree/main/docs/examples>`_

instrumentation/opentelemetry-instrumentation-aiohttp-client/src/opentelemetry/instrumentation/aiohttp_client/__init__.py

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,20 @@ def response_hook(span: Span, params: typing.Union[
8484
8585
AioHttpClientInstrumentor().instrument(request_hook=request_hook, response_hook=response_hook)
8686
87+
Exclude lists
88+
*************
89+
To exclude certain URLs from tracking, set the environment variable ``OTEL_PYTHON_AIOHTTP_CLIENT_EXCLUDED_URLS``
90+
(or ``OTEL_PYTHON_EXCLUDED_URLS`` to cover all instrumentations) to a string of comma delimited regexes that match the
91+
URLs.
92+
93+
For example,
94+
95+
::
96+
97+
export OTEL_PYTHON_AIOHTTP_CLIENT_EXCLUDED_URLS="client/.*/info,healthcheck"
98+
99+
will exclude requests such as ``https://site/client/123/info`` and ``https://site/xyz/healthcheck``.
100+
87101
API
88102
---
89103
"""
@@ -135,7 +149,11 @@ def response_hook(span: Span, params: typing.Union[
135149
)
136150
from opentelemetry.trace import Span, SpanKind, TracerProvider, get_tracer
137151
from opentelemetry.trace.status import Status, StatusCode
138-
from opentelemetry.util.http import redact_url, sanitize_method
152+
from opentelemetry.util.http import (
153+
get_excluded_urls,
154+
redact_url,
155+
sanitize_method,
156+
)
139157

140158
_UrlFilterT = typing.Optional[typing.Callable[[yarl.URL], str]]
141159
_RequestHookT = typing.Optional[
@@ -271,6 +289,8 @@ def create_trace_config(
271289

272290
metric_attributes = {}
273291

292+
excluded_urls = get_excluded_urls("AIOHTTP_CLIENT")
293+
274294
def _end_trace(trace_config_ctx: types.SimpleNamespace):
275295
elapsed_time = max(default_timer() - trace_config_ctx.start_time, 0)
276296
if trace_config_ctx.token:
@@ -304,7 +324,10 @@ async def on_request_start(
304324
trace_config_ctx: types.SimpleNamespace,
305325
params: aiohttp.TraceRequestStartParams,
306326
):
307-
if not is_instrumentation_enabled():
327+
if (
328+
not is_instrumentation_enabled()
329+
or trace_config_ctx.excluded_urls.url_disabled(str(params.url))
330+
):
308331
trace_config_ctx.span = None
309332
return
310333

@@ -426,6 +449,7 @@ def _trace_config_ctx_factory(**kwargs):
426449
start_time=start_time,
427450
duration_histogram_old=duration_histogram_old,
428451
duration_histogram_new=duration_histogram_new,
452+
excluded_urls=excluded_urls,
429453
**kwargs,
430454
)
431455

instrumentation/opentelemetry-instrumentation-aiohttp-client/tests/test_aiohttp_client_integration.py

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
import asyncio
1818
import contextlib
19+
import os
1920
import typing
2021
import unittest
2122
import urllib.parse
@@ -87,6 +88,7 @@ async def do_request():
8788
return loop.run_until_complete(do_request())
8889

8990

91+
# pylint: disable=too-many-public-methods
9092
class TestAioHttpIntegration(TestBase):
9193
_test_status_codes = (
9294
(HTTPStatus.OK, StatusCode.UNSET),
@@ -332,7 +334,7 @@ def test_schema_url(self):
332334

333335
span = self.memory_exporter.get_finished_spans()[0]
334336
self.assertEqual(
335-
span.instrumentation_info.schema_url,
337+
span.instrumentation_scope.schema_url,
336338
"https://opentelemetry.io/schemas/1.11.0",
337339
)
338340
self.memory_exporter.clear()
@@ -349,7 +351,7 @@ def test_schema_url_new_semconv(self):
349351

350352
span = self.memory_exporter.get_finished_spans()[0]
351353
self.assertEqual(
352-
span.instrumentation_info.schema_url,
354+
span.instrumentation_scope.schema_url,
353355
"https://opentelemetry.io/schemas/1.21.0",
354356
)
355357
self.memory_exporter.clear()
@@ -366,7 +368,7 @@ def test_schema_url_both_semconv(self):
366368

367369
span = self.memory_exporter.get_finished_spans()[0]
368370
self.assertEqual(
369-
span.instrumentation_info.schema_url,
371+
span.instrumentation_scope.schema_url,
370372
"https://opentelemetry.io/schemas/1.21.0",
371373
)
372374
self.memory_exporter.clear()
@@ -803,6 +805,29 @@ async def do_request(url):
803805
)
804806
self.memory_exporter.clear()
805807

808+
def test_ignores_excluded_urls(self):
809+
async def request_handler(request):
810+
assert "traceparent" not in request.headers
811+
return aiohttp.web.Response(status=HTTPStatus.OK)
812+
813+
for env_var in (
814+
"OTEL_PYTHON_AIOHTTP_CLIENT_EXCLUDED_URLS",
815+
"OTEL_PYTHON_EXCLUDED_URLS",
816+
):
817+
with self.subTest(env_var=env_var):
818+
with mock.patch.dict(
819+
os.environ, {env_var: "/some/path"}, clear=True
820+
):
821+
self._http_request(
822+
trace_config=aiohttp_client.create_trace_config(),
823+
request_handler=request_handler,
824+
url="/some/path?query=param&other=param2",
825+
status_code=HTTPStatus.OK,
826+
)
827+
828+
self._assert_spans([], 0)
829+
self._assert_metrics(0)
830+
806831

807832
class TestAioHttpClientInstrumentor(TestBase):
808833
URL = "/test-path"
@@ -1115,6 +1140,21 @@ def response_hook(
11151140
self.assertIn("response_hook_attr", span.attributes)
11161141
self.assertEqual(span.attributes["response_hook_attr"], "value")
11171142

1143+
@mock.patch.dict(
1144+
os.environ, {"OTEL_PYTHON_AIOHTTP_CLIENT_EXCLUDED_URLS": "/test-path"}
1145+
)
1146+
def test_ignores_excluded_urls(self):
1147+
# need the env var set at instrument time
1148+
AioHttpClientInstrumentor().uninstrument()
1149+
AioHttpClientInstrumentor().instrument()
1150+
1151+
url = "/test-path?query=params"
1152+
run_with_test_server(
1153+
self.get_default_request(url), url, self.default_handler
1154+
)
1155+
self._assert_spans(0)
1156+
self._assert_metrics(0)
1157+
11181158

11191159
class TestLoadingAioHttpInstrumentor(unittest.TestCase):
11201160
def test_loading_instrumentor(self):

instrumentation/opentelemetry-instrumentation-aiohttp-server/README.rst

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ OpenTelemetry aiohttp server Integration
33

44
|pypi|
55

6-
.. |pypi| image:: https://badge.fury.io/py/opentelemetry-instrumentation-aiohttp-client.svg
7-
:target: https://pypi.org/project/opentelemetry-instrumentation-aiohttp-client/
6+
.. |pypi| image:: https://badge.fury.io/py/opentelemetry-instrumentation-aiohttp-server.svg
7+
:target: https://pypi.org/project/opentelemetry-instrumentation-aiohttp-server/
88

99
This library allows tracing HTTP requests made by the
1010
`aiohttp server <https://docs.aiohttp.org/en/stable/server.html>`_ library.
@@ -19,6 +19,7 @@ Installation
1919
References
2020
----------
2121

22+
* `OpenTelemetry aiohttp server Instrumentation <https://opentelemetry-python-contrib.readthedocs.io/en/latest/instrumentation/aiohttp_server/aiohttp_server.html>`_
23+
* `aiohttp Tracing Reference <https://docs.aiohttp.org/en/stable/tracing_reference.html>`_
2224
* `OpenTelemetry Project <https://opentelemetry.io/>`_
23-
* `aiohttp client Tracing <https://docs.aiohttp.org/en/stable/tracing_reference.html>`_
2425
* `OpenTelemetry Python Examples <https://github.com/open-telemetry/opentelemetry-python/tree/main/docs/examples>`_

0 commit comments

Comments
 (0)