Skip to content

Commit 894fdfb

Browse files
committed
Merge branch 'main' into 2761-tornado-websocket
2 parents 11460d6 + 333fc5d commit 894fdfb

File tree

28 files changed

+625
-104
lines changed

28 files changed

+625
-104
lines changed

CHANGELOG.md

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

14+
15+
### Added
16+
17+
- `opentelemetry-instrumentation-pika` Added instrumentation for All `SelectConnection` adapters
18+
([#3584](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3584))
19+
- `opentelemetry-instrumentation-tornado` Add support for `WebSocketHandler` instrumentation
20+
([#3498](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3498))
21+
1422
### Fixed
1523

24+
- `opentelemetry-instrumentation-asgi`: fix excluded_urls in instrumentation-asgi
25+
([#3567](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3567))
1626
- `opentelemetry-resource-detector-containerid`: make it more quiet on platforms without cgroups
1727
([#3579](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3579))
1828

1929
### Added
2030

21-
- `opentelemetry-instrumentation-tornado` Add support for `WebSocketHandler` instrumentation
22-
([#3498](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3498))
31+
- `opentelemetry-util-http` Added support for redacting specific url query string values and url credentials in instrumentations
32+
([#3508](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3508))
33+
- `opentelemetry-instrumentation-pymongo` `aggregate` and `getMore` capture statements support
34+
([#3601](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3601))
2335

2436
## Version 1.34.0/0.55b0 (2025-06-04)
2537

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+
## Version 0.3b0 (2025-07-08)
11+
1012
- Add automatic instrumentation to tool call functions ([#3446](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3446))
1113

1214
## Version 0.2b0 (2025-04-28)

instrumentation-genai/opentelemetry-instrumentation-google-genai/src/opentelemetry/instrumentation/google_genai/version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,4 @@
1717
# This version should stay below "1.0" until the fundamentals
1818
# in "TODOS.md" have been addressed. Please revisit the TODOs
1919
# listed there before bumping to a stable version.
20-
__version__ = "0.3b0.dev"
20+
__version__ = "0.4b0.dev"

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ def response_hook(span: Span, params: typing.Union[
135135
)
136136
from opentelemetry.trace import Span, SpanKind, TracerProvider, get_tracer
137137
from opentelemetry.trace.status import Status, StatusCode
138-
from opentelemetry.util.http import remove_url_credentials, sanitize_method
138+
from opentelemetry.util.http import redact_url, sanitize_method
139139

140140
_UrlFilterT = typing.Optional[typing.Callable[[yarl.URL], str]]
141141
_RequestHookT = typing.Optional[
@@ -311,9 +311,9 @@ async def on_request_start(
311311
method = params.method
312312
request_span_name = _get_span_name(method)
313313
request_url = (
314-
remove_url_credentials(trace_config_ctx.url_filter(params.url))
314+
redact_url(trace_config_ctx.url_filter(params.url))
315315
if callable(trace_config_ctx.url_filter)
316-
else remove_url_credentials(str(params.url))
316+
else redact_url(str(params.url))
317317
)
318318

319319
span_attributes = {}

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

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -762,16 +762,16 @@ async def do_request(url):
762762
)
763763
self.memory_exporter.clear()
764764

765-
def test_credential_removal(self):
765+
def test_remove_sensitive_params(self):
766766
trace_configs = [aiohttp_client.create_trace_config()]
767767

768-
app = HttpServerMock("test_credential_removal")
768+
app = HttpServerMock("test_remove_sensitive_params")
769769

770770
@app.route("/status/200")
771771
def index():
772772
return "hello"
773773

774-
url = "http://username:password@localhost:5000/status/200"
774+
url = "http://username:password@localhost:5000/status/200?Signature=secret"
775775

776776
with app.run("localhost", 5000):
777777
with self.subTest(url=url):
@@ -793,7 +793,9 @@ async def do_request(url):
793793
(StatusCode.UNSET, None),
794794
{
795795
HTTP_METHOD: "GET",
796-
HTTP_URL: ("http://localhost:5000/status/200"),
796+
HTTP_URL: (
797+
"http://REDACTED:REDACTED@localhost:5000/status/200?Signature=REDACTED"
798+
),
797799
HTTP_STATUS_CODE: int(HTTPStatus.OK),
798800
},
799801
)

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ async def hello(request):
7272
)
7373
from opentelemetry.semconv.metrics import MetricInstruments
7474
from opentelemetry.trace.status import Status, StatusCode
75-
from opentelemetry.util.http import get_excluded_urls, remove_url_credentials
75+
from opentelemetry.util.http import get_excluded_urls, redact_url
7676

7777
_duration_attrs = [
7878
HTTP_METHOD,
@@ -148,6 +148,7 @@ def collect_request_attributes(request: web.Request) -> Dict:
148148
request.url.port,
149149
str(request.url),
150150
)
151+
151152
query_string = request.query_string
152153
if query_string and http_url:
153154
if isinstance(query_string, bytes):
@@ -161,7 +162,7 @@ def collect_request_attributes(request: web.Request) -> Dict:
161162
HTTP_ROUTE: _get_view_func(request),
162163
HTTP_FLAVOR: f"{request.version.major}.{request.version.minor}",
163164
HTTP_TARGET: request.path,
164-
HTTP_URL: remove_url_credentials(http_url),
165+
HTTP_URL: redact_url(http_url),
165166
}
166167

167168
http_method = request.method

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

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,3 +152,46 @@ async def test_suppress_instrumentation(
152152
await client.get("/test-path")
153153

154154
assert len(memory_exporter.get_finished_spans()) == 0
155+
156+
157+
@pytest.mark.asyncio
158+
async def test_remove_sensitive_params(tracer, aiohttp_server):
159+
"""Test that sensitive information in URLs is properly redacted."""
160+
_, memory_exporter = tracer
161+
162+
# Set up instrumentation
163+
AioHttpServerInstrumentor().instrument()
164+
165+
# Create app with test route
166+
app = aiohttp.web.Application()
167+
168+
async def handler(request):
169+
return aiohttp.web.Response(text="hello")
170+
171+
app.router.add_get("/status/200", handler)
172+
173+
# Start the server
174+
server = await aiohttp_server(app)
175+
176+
# Make request with sensitive data in URL
177+
url = f"http://username:password@{server.host}:{server.port}/status/200?Signature=secret"
178+
async with aiohttp.ClientSession() as session:
179+
async with session.get(url) as response:
180+
assert response.status == 200
181+
assert await response.text() == "hello"
182+
183+
# Verify redaction in span attributes
184+
spans = memory_exporter.get_finished_spans()
185+
assert len(spans) == 1
186+
187+
span = spans[0]
188+
assert span.attributes[HTTP_METHOD] == "GET"
189+
assert span.attributes[HTTP_STATUS_CODE] == 200
190+
assert (
191+
span.attributes[HTTP_URL]
192+
== f"http://{server.host}:{server.port}/status/200?Signature=REDACTED"
193+
)
194+
195+
# Clean up
196+
AioHttpServerInstrumentor().uninstrument()
197+
memory_exporter.clear()

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

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -273,12 +273,14 @@ def client_response_hook(span: Span, scope: Scope, message: dict[str, Any]):
273273
OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SANITIZE_FIELDS,
274274
OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_REQUEST,
275275
OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_RESPONSE,
276+
ExcludeList,
276277
SanitizeValue,
277278
_parse_url_query,
278279
get_custom_headers,
279280
normalise_request_header_name,
280281
normalise_response_header_name,
281-
remove_url_credentials,
282+
parse_excluded_urls,
283+
redact_url,
282284
sanitize_method,
283285
)
284286

@@ -375,7 +377,7 @@ def collect_request_attributes(
375377
if _report_old(sem_conv_opt_in_mode):
376378
_set_http_url(
377379
result,
378-
remove_url_credentials(http_url),
380+
redact_url(http_url),
379381
_StabilityMode.DEFAULT,
380382
)
381383
http_method = scope.get("method", "")
@@ -556,7 +558,7 @@ class OpenTelemetryMiddleware:
556558
def __init__(
557559
self,
558560
app,
559-
excluded_urls=None,
561+
excluded_urls: ExcludeList | str | None = None,
560562
default_span_details=None,
561563
server_request_hook: ServerRequestHook = None,
562564
client_request_hook: ClientRequestHook = None,
@@ -638,6 +640,8 @@ def __init__(
638640
self.active_requests_counter = create_http_server_active_requests(
639641
self.meter
640642
)
643+
if isinstance(excluded_urls, str):
644+
excluded_urls = parse_excluded_urls(excluded_urls)
641645
self.excluded_urls = excluded_urls
642646
self.default_span_details = (
643647
default_span_details or get_default_span_details

instrumentation/opentelemetry-instrumentation-asgi/tests/test_asgi_middleware.py

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1633,6 +1633,29 @@ async def test_no_metric_for_websockets(self):
16331633
await self.get_all_output()
16341634
self.assertIsNone(self.memory_metrics_reader.get_metrics_data())
16351635

1636+
async def test_excluded_urls(self):
1637+
self.scope["path"] = "/test_excluded_urls"
1638+
app = otel_asgi.OpenTelemetryMiddleware(
1639+
simple_asgi, excluded_urls="test_excluded_urls"
1640+
)
1641+
self.seed_app(app)
1642+
await self.send_default_request()
1643+
await self.get_all_output()
1644+
spans = self.memory_exporter.get_finished_spans()
1645+
self.assertEqual(len(spans), 0)
1646+
1647+
async def test_no_excluded_urls(self):
1648+
self.scope["path"] = "/test_excluded_urls"
1649+
app = otel_asgi.OpenTelemetryMiddleware(
1650+
simple_asgi, excluded_urls="test_excluded_urls"
1651+
)
1652+
self.seed_app(app)
1653+
self.scope["path"] = "/test_no_excluded_urls"
1654+
await self.send_default_request()
1655+
await self.get_all_output()
1656+
spans = self.memory_exporter.get_finished_spans()
1657+
self.assertGreater(len(spans), 0)
1658+
16361659

16371660
class TestAsgiAttributes(unittest.TestCase):
16381661
def setUp(self):
@@ -1809,12 +1832,14 @@ def test_response_attributes_invalid_status_code(self):
18091832
otel_asgi.set_status_code(self.span, "Invalid Status Code")
18101833
self.assertEqual(self.span.set_status.call_count, 1)
18111834

1812-
def test_credential_removal(self):
1835+
def test_remove_sensitive_params(self):
18131836
self.scope["server"] = ("username:password@mock", 80)
18141837
self.scope["path"] = "/status/200"
1838+
self.scope["query_string"] = b"X-Goog-Signature=1234567890"
18151839
attrs = otel_asgi.collect_request_attributes(self.scope)
18161840
self.assertEqual(
1817-
attrs[SpanAttributes.HTTP_URL], "http://mock/status/200"
1841+
attrs[SpanAttributes.HTTP_URL],
1842+
"http://REDACTED:REDACTED@mock/status/200?X-Goog-Signature=REDACTED",
18181843
)
18191844

18201845
def test_collect_target_attribute_missing(self):

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -259,7 +259,7 @@ async def async_response_hook(span, request, response):
259259
from opentelemetry.trace import SpanKind, Tracer, TracerProvider, get_tracer
260260
from opentelemetry.trace.span import Span
261261
from opentelemetry.trace.status import StatusCode
262-
from opentelemetry.util.http import remove_url_credentials, sanitize_method
262+
from opentelemetry.util.http import redact_url, sanitize_method
263263

264264
_logger = logging.getLogger(__name__)
265265

@@ -313,7 +313,7 @@ def _extract_parameters(
313313
# In httpx >= 0.20.0, handle_request receives a Request object
314314
request: httpx.Request = args[0]
315315
method = request.method.encode()
316-
url = httpx.URL(remove_url_credentials(str(request.url)))
316+
url = httpx.URL(str(request.url))
317317
headers = request.headers
318318
stream = request.stream
319319
extensions = request.extensions
@@ -382,7 +382,7 @@ def _apply_request_client_attributes_to_span(
382382
)
383383

384384
# http semconv transition: http.url -> url.full
385-
_set_http_url(span_attributes, str(url), semconv)
385+
_set_http_url(span_attributes, redact_url(str(url)), semconv)
386386

387387
# Set HTTP method in metric labels
388388
_set_http_method(

0 commit comments

Comments
 (0)