Repository navigation
Conversation
Pull request dashboard statusWaiting on the author · refreshed 2026-11-11 09:32 UTC Respond to 7 review items (e.g. link a commit, explain why not, ask a follow-up): Status above doesn't look right?
|
| ) | ||
|
|
||
|
|
||
| class CapturingStream(wrapt.ObjectProxy): |
There was a problem hiding this comment.
WSGI is very old and I know is used widely with functions not defined in any PEP. I have tried to support common extensions while being as safe as possible here
| end = min(position + len(data), self._max_size) | ||
| self._data[position:end] = data[: end - position] | ||
|
|
||
| def to_attribute_value(self) -> str | bytes | None: |
There was a problem hiding this comment.
I was a bit surprised by the string/bytes polymorphic type for the attribute. I don't know if there is any similar one in semconv? It puts a lot of work in SDKs since it's not that trivial to know if it's text or not, we also have to work around the unicode truncation caused by max size - it feels like SDKs "doing too much". It feels like all of this should be left to backends? In that case, it could also be possible to capture content-encoding bodies since the backend should be able to support decoding them.
@SylvainJuge what do you think? Could it make sense to reconsider this part of semconv?
There was a problem hiding this comment.
I agree with you, here trying to capture it as text in the SDK/instrumentation level adds quite a lot of complexity, this also shows in the WIP java implementation open-telemetry/opentelemetry-java-instrumentation#20620
- requires to implement a heuristic to detect textual content, which may also rely on HTTP headers or not
- requires to deal with unicode truncation detection
- may also imply some overhead as conversion from/to bytes to string is required
The history and discussion of open-telemetry/semantic-conventions#3521 drifted in this direction because:
- using text makes it easier for humans to consume
- using text makes implementation of sanitization easier
As you wrote, using the same semconv attribute for something that is either a binary array or text makes also the consumption harder as the format is not known in advance and depends on the context (it may vary from HTTP request to request).
I will open an issue in semconv so we can further discuss improvements we could make here.
There was a problem hiding this comment.
@anuraaga I've opened open-telemetry/semantic-conventions#4307 to try to clarify/simplify this.
I think doing it in the SDK has some advantages for the end-users, however if this proves too complex to implement we should definitely consider alternatives, feel free to comment on the issue above to continue the discussion.
|
Hi @anuraaga — just a friendly reminder that this pull request is waiting on you. The dashboard status comment has the open items and is kept current.
|
|
Don't think this has anything pending for me - @xrmx can you take a look? We'll probably make changes to the spec to simplify it, but it probably makes sense to follow up after those changes to update this instrumentation |
There was a problem hiding this comment.
🟡 Changes recommended
Uncaught codec errors can disrupt request handling and span cleanup, while the pinned SDK cannot preserve captured binary bodies as byte-valued span attributes.
3 open issues
What changed in this PR
Adds opt-in HTTP request body capture to WSGI instrumentation, with shared helpers intended for reuse by other HTTP instrumentations.
Changes:
- Adds bounded buffering and stream proxying to capture bytes read by the application.
- Integrates capture with WSGI span finalization, disabled by default and in stable-only HTTP mode.
- Adds documentation, tests, a changelog entry, and the
wraptdependency.
| File | Description |
|---|---|
uv.lock |
Records the new utility dependency on wrapt. |
util/opentelemetry-util-http/tests/test_body_capture.py |
Tests buffering, decoding, limits, and stream proxy behavior. |
util/opentelemetry-util-http/src/opentelemetry/util/http/body_capture.py |
Adds shared body buffering and capture helpers. |
util/opentelemetry-util-http/pyproject.toml |
Declares the wrapt dependency. |
instrumentation/opentelemetry-instrumentation-wsgi/tests/test_wsgi_middleware.py |
Tests capture configuration and WSGI integration. |
instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py |
Documents and integrates request body capture. |
.changelog/5272.added |
Announces opt-in request body capture. |
Review effort: Balanced
| return | ||
| body = body_buffer.to_attribute_value() | ||
| if body is not None: | ||
| span.set_attribute(HTTP_REQUEST_BODY_CONTENT, body) |
| except (LookupError, UnicodeDecodeError): | ||
| return data |
There was a problem hiding this comment.
This will require a pylint warning suppression # pylint: disable=broad-exception-caught
| def _self_readinto(self, target: bytearray | memoryview) -> int | None: | ||
| count = self.__wrapped__.readinto(target) | ||
| if count: | ||
| self._self_capture(memoryview(target)[:count]) |
|
|
||
| # TODO: use the semconv constant once the semconv package is generated from a | ||
| # semantic conventions release that includes this attribute (> 1.44.0). | ||
| HTTP_REQUEST_BODY_CONTENT: Final = "http.request.body.content" |
There was a problem hiding this comment.
We now have it in opentelemetry.semconv._incubating.attributes.http_attributes
| ) -> BodyBuffer | None: | ||
| """Returns a buffer for a body, or ``None`` if it should not be captured. | ||
|
|
||
| Bodies with a content coding are not captured. |
There was a problem hiding this comment.
| Bodies with a content coding are not captured. | |
| Bodies with a content encoding are not captured. |
| return | ||
| body = body_buffer.to_attribute_value() | ||
| if body is not None: | ||
| span.set_attribute(HTTP_REQUEST_BODY_CONTENT, body) |
There was a problem hiding this comment.
Should we also try to set the content type and content-encoding here? These are opt-in but otherwise it would be pretty hard for backends to know how to handle the data for non-text data. This would probably require an update on the semconv side as well.


Description
I am helping implement HTTP body capture. This PR defines shared helpers and uses them in WSGI. After this PR, I would send PRs using the shared helpers for ASGI, tornado, aiohttp.
#5215
I have taken some liberties from the proposals in the issue. I think separating request and response body size limits is useful, requests are often untrusted and usually have far different size than response. It matches a similar concept as the separate header allowlists for header capture. And when separating, I don't see much need for the enabled flag at all since it duplicates
=0, a 1KB default seems quite arbitrary and needing to be changed almost all the time, so it' seems reasonable to instead just make the user specify always. So I implemented as a single knobcaptured_request_body_sizedefaulting to0, disabled. Seems to do the job in the easiest way for users/cc @xrmx
Type of change
Please delete options that are not relevant.
Checklist:
See contributing.md for styleguide, changelog guidelines, and more.
tox -e precommittox -e typecheck