Skip to content

feat(boto3): trace logical AWS calls with common attributes - #7481

Draft
pabloDeputter wants to merge 12 commits into
masterfrom
pablo/improve-boto3-call-lifecycle
Draft

feat(boto3): trace logical AWS calls with common attributes#7481
pabloDeputter wants to merge 12 commits into
masterfrom
pablo/improve-boto3-call-lifecycle

Conversation

@pabloDeputter

@pabloDeputter pabloDeputter commented Sep 11, 2026

Copy link
Copy Markdown
Member

Description

Implements #7474 and #7475 by moving boto3 instrumentation from individual HTTP request attempts to botocore client-call lifecycle and adding common OTel AWS attributes.

...

  • wrapper owns a single botocore client-call lifecycle.
  • introduce service-extension architecture.
  • add generic response, retry, and error attributes.
  • add common attributes that are the same across services.

Issues

Resolves #7474 & #7475

- wrapper owns a single botocore client-call lifecycle.
- introduce service-extension architecture.
- add generic response, retry, and error attributes.
- add common attributes that are the same across services.
…an naming and `rpc.service` and `parse_url` patch target
@github-actions

github-actions Bot commented Sep 11, 2026

Copy link
Copy Markdown
Contributor

Codecov Results 📊

126821 passed | ⏭️ 7148 skipped | Total: 133969 | Pass Rate: 94.66% | Execution Time: 432m 12s

📊 Comparison with Base Branch

Metric Change
Total Tests 📈 +238
Passed Tests 📈 +221
Failed Tests
Skipped Tests 📈 +17

All tests are passing successfully.

✅ Patch coverage is 95.74%. Project has 2537 uncovered lines.
✅ Project coverage is 90.2%. Comparing base (4b98e26) to head (e384637).

Files with missing lines (2)
File Patch % Lines
sentry_sdk/integrations/boto3/_instrumentation.py 93.55% ⚠️ 10 Missing and 13 partials
sentry_sdk/integrations/boto3/_client.py 100.00% ⚠️ 4 partials
Coverage diff
@@            Coverage Diff             @@
##        master       #PR       +/-##
==========================================
+ Coverage    90.18%    90.20%    +0.02%
==========================================
  Files          193       196        +3
  Lines        25790     25893      +103
  Branches      9532      9562       +30
==========================================
+ Hits         23258     23356       +98
- Misses        2532      2537        +5
- Partials      1432      1447       +15

Generated by Codecov Action

@pabloDeputter pabloDeputter changed the title feat(boto3): improve boto3 integration feat(boto3): trace logical AWS calls with common attributes Sep 11, 2026
Comment on lines +147 to +154
streaming_span = sentry_sdk.traces.start_span(
name=span.name,
parent_span=span,
attributes={
SPANDATA.SENTRY_OP: OP.HTTP_CLIENT_STREAM,
SPANDATA.SENTRY_ORIGIN: Boto3Integration.origin,
},
)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Streaming body span remains active after the client call returns

The streaming span is created with the default active=True, so it replaces the current streamed span after the client span has ended and remains active until the response body is fully read or closed. Instrumented work performed in the same scope while the StreamingBody remains open can therefore be incorrectly parented under http.client.stream. Create this span with active=False unless that parenting is intentional.

Evidence
  • _finish_client_span() ends the client span before creating the streaming span with parent_span=span.
  • traces.start_span() defaults active=True, and StreamedSpan._start() stores the new span in scope.streamed_span.
  • The streaming span is only removed from the scope by the wrapped body read() reaching EOF, an exception, or close().
  • The legacy path uses span.start_child() without activating the child, so this scope replacement is specific to span-streaming mode.

Identified by Warden · code-review · ERE-FHG

Comment on lines +176 to +181
except Exception:
if isinstance(streaming_span, StreamedSpan):
streaming_span.end()
else:
streaming_span.finish()
raise

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Streaming-trace body read failures finish the stream span as successful

When span streaming is enabled, a body read exception ends the StreamedSpan through end(), leaving its default ok status. Invoke __exit__(type(exc), exc, exc.__traceback__) or set an error status before ending the span so failed body reads are recorded as errors.

Evidence
  • _finish_client_span() wraps StreamingBody.read and catches body-read exceptions in sentry_streaming_body_read().
  • On the exception path, StreamedSpan.end() is called without the exception; StreamedSpan initializes its status to ok, while only __exit__ changes it to error.
  • _finish_client_span_with_error() demonstrates the existing error-aware pattern by calling span.__exit__(type(exception), exception, exception.__traceback__).
  • test_streaming_body_read_failure_finishes_stream_span verifies completion but not the stream span status.
Also found at 1 additional location
  • tests/integrations/boto3/test_client.py:360-400

Identified by Warden · code-review · ZSA-X6F

Comment thread sentry_sdk/consts.py
Comment on lines +998 to +1003
RPC_SYSTEM_NAME = "rpc.system.name"
"""
A string identifying the remoting system.
Example: "aws-api"
"""

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

RPC_SYSTEM_NAME uses non-standard attribute key rpc.system.name

Use rpc.system (OTEL AWS SDK / RPC semconv), not rpc.system.name; otherwise AWS spans emit a non-standard attribute backends won't recognize.

Evidence
  • Hunk defines RPC_SYSTEM_NAME = "rpc.system.name".
  • _get_client_attributes() in sentry_sdk/integrations/boto3/_instrumentation.py sets SPANDATA.RPC_SYSTEM_NAME to "aws-api" on every client span.
  • OTEL AWS SDK spans require rpc.system=aws-api (same docs linked in that helper); RPC conventions use rpc.system, unlike DB's db.system.name.
  • Tests assert the constant value only, so the wrong key is not caught.

Identified by Warden · find-bugs · VD7-M84

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ref(boto3): Own one logical botocore client-call lifecycle

1 participant