Skip to content

chore(boto3): Remove transaction-based tracing - #7304

Closed
alexander-alderman-webb wants to merge 1 commit into
masterfrom
webb/boto3/remove-transactions
Closed

chore(boto3): Remove transaction-based tracing#7304
alexander-alderman-webb wants to merge 1 commit into
masterfrom
webb/boto3/remove-transactions

Conversation

@alexander-alderman-webb

Copy link
Copy Markdown
Contributor

Description

Issues

Closes #7079

Reminders

@github-actions

Copy link
Copy Markdown
Contributor

Codecov Results 📊

118702 passed | ❌ 13 failed | ⏭️ 6755 skipped | Total: 125470 | Pass Rate: 94.61% | Execution Time: 408m 25s

📊 Comparison with Base Branch

Metric Change
Total Tests 📉 -352
Passed Tests 📉 -358
Failed Tests 📈 +13
Skipped Tests 📉 -7

➕ New Tests (13)

View new tests
  • test_breadcrumb
    • File: tests.integrations.boto3.test_s3
    • Status: ❌ Failing
  • test_breadcrumb
    • File: tests.integrations.boto3.test_s3
    • Status: ❌ Failing
  • test_breadcrumb
    • File: tests.integrations.boto3.test_s3
    • Status: ❌ Failing
  • test_breadcrumb
    • File: tests.integrations.boto3.test_s3
    • Status: ❌ Failing
  • test_breadcrumb
    • File: tests.integrations.boto3.test_s3
    • Status: ❌ Failing
  • test_breadcrumb
    • File: tests.integrations.boto3.test_s3
    • Status: ❌ Failing
  • test_breadcrumb
    • File: tests.integrations.boto3.test_s3
    • Status: ❌ Failing
  • test_breadcrumb
    • File: tests.integrations.boto3.test_s3
    • Status: ❌ Failing
  • test_breadcrumb
    • File: tests.integrations.boto3.test_s3
    • Status: ❌ Failing
  • test_breadcrumb
    • File: tests.integrations.boto3.test_s3
    • Status: ❌ Failing
  • test_breadcrumb
    • File: tests.integrations.boto3.test_s3
    • Status: ❌ Failing
  • test_breadcrumb
    • File: tests.integrations.boto3.test_s3
    • Status: ❌ Failing
  • test_breadcrumb
    • File: tests.integrations.boto3.test_s3
    • Status: ❌ Failing

❌ Failed Tests

test_breadcrumb

File: tests.integrations.boto3.test_s3
Suite: py3.10-boto3-v1.24.96
Error: AssertionError: assert {'http.request.method': 'GET'} == {'aws.request.url': , 'http.method': 'GET', 'http.query': , 'http.fragment': ''} Left contains 1 more item: {'http.request.method': 'GET'} Right contains 4 more items: {'aws.request.url': , 'http.fragment': '', 'http.method': 'GET', 'http.query': } Full diff: { - 'aws.request.url': , - 'http.method': 'GET', + 'http.request.method': 'GET', ? ++++++++ - 'http.query': , - 'http.fragment': '', }

Stack Trace
tests/integrations/boto3/test_s3.py:252: in test_breadcrumb
    assert crumb["data"] == ApproxDict(
E   AssertionError: assert {'http.request.method': 'GET'} == {'aws.request.url': <ANY>, 'http.method': 'GET', 'http.query': <ANY>, 'http.fragment': ''}
E     
E     Left contains 1 more item:
E     {'http.request.method': 'GET'}
E     Right contains 4 more items:
E     {'aws.request.url': <ANY>,
E      'http.fragment': '',
E      'http.method': 'GET',
E      'http.query': <ANY>}
E     
E     Full diff:
E       {
E     -     'aws.request.url': <ANY>,
E     -     'http.method': 'GET',
E     +     'http.request.method': 'GET',
E     ?          ++++++++
E     -     'http.query': <ANY>,
E     -     'http.fragment': '',
E       }

test_breadcrumb

File: tests.integrations.boto3.test_s3
Suite: py3.10-boto3-v1.43.78
Error: AssertionError: assert {'http.request.method': 'GET'} == {'aws.request.url': , 'http.method': 'GET', 'http.query': , 'http.fragment': ''} Left contains 1 more item: {'http.request.method': 'GET'} Right contains 4 more items: {'aws.request.url': , 'http.fragment': '', 'http.method': 'GET', 'http.query': } Full diff: { - 'aws.request.url': , - 'http.method': 'GET', + 'http.request.method': 'GET', ? ++++++++ - 'http.query': , - 'http.fragment': '', }

Stack Trace
tests/integrations/boto3/test_s3.py:252: in test_breadcrumb
    assert crumb["data"] == ApproxDict(
E   AssertionError: assert {'http.request.method': 'GET'} == {'aws.request.url': <ANY>, 'http.method': 'GET', 'http.query': <ANY>, 'http.fragment': ''}
E     
E     Left contains 1 more item:
E     {'http.request.method': 'GET'}
E     Right contains 4 more items:
E     {'aws.request.url': <ANY>,
E      'http.fragment': '',
E      'http.method': 'GET',
E      'http.query': <ANY>}
E     
E     Full diff:
E       {
E     -     'aws.request.url': <ANY>,
E     -     'http.method': 'GET',
E     +     'http.request.method': 'GET',
E     ?          ++++++++
E     -     'http.query': <ANY>,
E     -     'http.fragment': '',
E       }

test_breadcrumb

File: tests.integrations.boto3.test_s3
Suite: py3.11-boto3-v1.24.96
Error: AssertionError: assert {'http.request.method': 'GET'} == {'aws.request.url': , 'http.method': 'GET', 'http.query': , 'http.fragment': ''} Left contains 1 more item: {'http.request.method': 'GET'} Right contains 4 more items: {'aws.request.url': , 'http.fragment': '', 'http.method': 'GET', 'http.query': } Full diff: { - 'aws.request.url': , - 'http.method': 'GET', + 'http.request.method': 'GET', ? ++++++++ - 'http.query': , - 'http.fragment': '', }

Stack Trace
tests/integrations/boto3/test_s3.py:252: in test_breadcrumb
    assert crumb["data"] == ApproxDict(
E   AssertionError: assert {'http.request.method': 'GET'} == {'aws.request.url': <ANY>, 'http.method': 'GET', 'http.query': <ANY>, 'http.fragment': ''}
E     
E     Left contains 1 more item:
E     {'http.request.method': 'GET'}
E     Right contains 4 more items:
E     {'aws.request.url': <ANY>,
E      'http.fragment': '',
E      'http.method': 'GET',
E      'http.query': <ANY>}
E     
E     Full diff:
E       {
E     -     'aws.request.url': <ANY>,
E     -     'http.method': 'GET',
E     +     'http.request.method': 'GET',
E     ?          ++++++++
E     -     'http.query': <ANY>,
E     -     'http.fragment': '',
E       }

test_breadcrumb

File: tests.integrations.boto3.test_s3
Suite: py3.12-boto3-v1.35.99
Error: AssertionError: assert {'http.request.method': 'GET'} == {'aws.request.url': , 'http.method': 'GET', 'http.query': , 'http.fragment': ''} Left contains 1 more item: {'http.request.method': 'GET'} Right contains 4 more items: {'aws.request.url': , 'http.fragment': '', 'http.method': 'GET', 'http.query': } Full diff: { - 'aws.request.url': , - 'http.method': 'GET', + 'http.request.method': 'GET', ? ++++++++ - 'http.query': , - 'http.fragment': '', }

Stack Trace
tests/integrations/boto3/test_s3.py:252: in test_breadcrumb
    assert crumb["data"] == ApproxDict(
E   AssertionError: assert {'http.request.method': 'GET'} == {'aws.request.url': <ANY>, 'http.method': 'GET', 'http.query': <ANY>, 'http.fragment': ''}
E     
E     Left contains 1 more item:
E     {'http.request.method': 'GET'}
E     Right contains 4 more items:
E     {'aws.request.url': <ANY>,
E      'http.fragment': '',
E      'http.method': 'GET',
E      'http.query': <ANY>}
E     
E     Full diff:
E       {
E     -     'aws.request.url': <ANY>,
E     -     'http.method': 'GET',
E     +     'http.request.method': 'GET',
E     ?          ++++++++
E     -     'http.query': <ANY>,
E     -     'http.fragment': '',
E       }

test_breadcrumb

File: tests.integrations.boto3.test_s3
Suite: py3.13-boto3-v1.35.99
Error: AssertionError: assert {'http.request.method': 'GET'} == {'aws.request.url': , 'http.method': 'GET', 'http.query': , 'http.fragment': ''} Left contains 1 more item: {'http.request.method': 'GET'} Right contains 4 more items: {'aws.request.url': , 'http.fragment': '', 'http.method': 'GET', 'http.query': } Full diff: { - 'aws.request.url': , - 'http.method': 'GET', + 'http.request.method': 'GET', ? ++++++++ - 'http.query': , - 'http.fragment': '', }

Stack Trace
tests/integrations/boto3/test_s3.py:252: in test_breadcrumb
    assert crumb["data"] == ApproxDict(
E   AssertionError: assert {'http.request.method': 'GET'} == {'aws.request.url': <ANY>, 'http.method': 'GET', 'http.query': <ANY>, 'http.fragment': ''}
E     
E     Left contains 1 more item:
E     {'http.request.method': 'GET'}
E     Right contains 4 more items:
E     {'aws.request.url': <ANY>,
E      'http.fragment': '',
E      'http.method': 'GET',
E      'http.query': <ANY>}
E     
E     Full diff:
E       {
E     -     'aws.request.url': <ANY>,
E     -     'http.method': 'GET',
E     +     'http.request.method': 'GET',
E     ?          ++++++++
E     -     'http.query': <ANY>,
E     -     'http.fragment': '',
E       }

test_breadcrumb

File: tests.integrations.boto3.test_s3
Suite: py3.13-boto3-v1.43.78
Error: AssertionError: assert {'http.request.method': 'GET'} == {'aws.request.url': , 'http.method': 'GET', 'http.query': , 'http.fragment': ''} Left contains 1 more item: {'http.request.method': 'GET'} Right contains 4 more items: {'aws.request.url': , 'http.fragment': '', 'http.method': 'GET', 'http.query': } Full diff: { - 'aws.request.url': , - 'http.method': 'GET', + 'http.request.method': 'GET', ? ++++++++ - 'http.query': , - 'http.fragment': '', }

Stack Trace
tests/integrations/boto3/test_s3.py:252: in test_breadcrumb
    assert crumb["data"] == ApproxDict(
E   AssertionError: assert {'http.request.method': 'GET'} == {'aws.request.url': <ANY>, 'http.method': 'GET', 'http.query': <ANY>, 'http.fragment': ''}
E     
E     Left contains 1 more item:
E     {'http.request.method': 'GET'}
E     Right contains 4 more items:
E     {'aws.request.url': <ANY>,
E      'http.fragment': '',
E      'http.method': 'GET',
E      'http.query': <ANY>}
E     
E     Full diff:
E       {
E     -     'aws.request.url': <ANY>,
E     -     'http.method': 'GET',
E     +     'http.request.method': 'GET',
E     ?          ++++++++
E     -     'http.query': <ANY>,
E     -     'http.fragment': '',
E       }

test_breadcrumb

File: tests.integrations.boto3.test_s3
Suite: py3.14-boto3-v1.43.78
Error: AssertionError: assert {'http.request.method': 'GET'} == {'aws.request.url': , 'http.method': 'GET', 'http.query': , 'http.fragment': ''} Left contains 1 more item: {'http.request.method': 'GET'} Right contains 4 more items: {'aws.request.url': , 'http.fragment': '', 'http.method': 'GET', 'http.query': } Full diff: { - 'aws.request.url': , - 'http.method': 'GET', + 'http.request.method': 'GET', ? ++++++++ - 'http.query': , - 'http.fragment': '', }

Stack Trace
tests/integrations/boto3/test_s3.py:252: in test_breadcrumb
    assert crumb["data"] == ApproxDict(
E   AssertionError: assert {'http.request.method': 'GET'} == {'aws.request.url': <ANY>, 'http.method': 'GET', 'http.query': <ANY>, 'http.fragment': ''}
E     
E     Left contains 1 more item:
E     {'http.request.method': 'GET'}
E     Right contains 4 more items:
E     {'aws.request.url': <ANY>,
E      'http.fragment': '',
E      'http.method': 'GET',
E      'http.query': <ANY>}
E     
E     Full diff:
E       {
E     -     'aws.request.url': <ANY>,
E     -     'http.method': 'GET',
E     +     'http.request.method': 'GET',
E     ?          ++++++++
E     -     'http.query': <ANY>,
E     -     'http.fragment': '',
E       }

test_breadcrumb

File: tests.integrations.boto3.test_s3
Suite: py3.14t-boto3-v1.43.78
Error: AssertionError: assert {'http.request.method': 'GET'} == {'aws.request.url': , 'http.method': 'GET', 'http.query': , 'http.fragment': ''} Left contains 1 more item: {'http.request.method': 'GET'} Right contains 4 more items: {'aws.request.url': , 'http.fragment': '', 'http.method': 'GET', 'http.query': } Full diff: { - 'aws.request.url': , - 'http.method': 'GET', + 'http.request.method': 'GET', ? ++++++++ - 'http.query': , - 'http.fragment': '', }

Stack Trace
tests/integrations/boto3/test_s3.py:252: in test_breadcrumb
    assert crumb["data"] == ApproxDict(
E   AssertionError: assert {'http.request.method': 'GET'} == {'aws.request.url': <ANY>, 'http.method': 'GET', 'http.query': <ANY>, 'http.fragment': ''}
E     
E     Left contains 1 more item:
E     {'http.request.method': 'GET'}
E     Right contains 4 more items:
E     {'aws.request.url': <ANY>,
E      'http.fragment': '',
E      'http.method': 'GET',
E      'http.query': <ANY>}
E     
E     Full diff:
E       {
E     -     'aws.request.url': <ANY>,
E     -     'http.method': 'GET',
E     +     'http.request.method': 'GET',
E     ?          ++++++++
E     -     'http.query': <ANY>,
E     -     'http.fragment': '',
E       }

test_breadcrumb

File: tests.integrations.boto3.test_s3
Suite: py3.6-boto3-v1.16.63
Error: AssertionError: assert {'http.request.method': 'GET'} == {'aws.request.url': ,\n 'http.fragment': '',\n 'http.method': 'GET',\n 'http.query': } Left contains 1 more item: {'http.request.method': 'GET'} Right contains 4 more items: {'aws.request.url': , 'http.fragment': '', 'http.method': 'GET', 'http.query': } Full diff: { - 'aws.request.url': , - 'http.fragment': '', - 'http.method': 'GET', + 'http.request.method': 'GET', ? ++++++++ - 'http.query': , }

Stack Trace
tests/integrations/boto3/test_s3.py:252: in test_breadcrumb
    assert crumb["data"] == ApproxDict(
E   AssertionError: assert {'http.request.method': 'GET'} == {'aws.request.url': <ANY>,\n 'http.fragment': '',\n 'http.method': 'GET',\n 'http.query': <ANY>}
E     Left contains 1 more item:
E     {'http.request.method': 'GET'}
E     Right contains 4 more items:
E     {'aws.request.url': <ANY>,
E      'http.fragment': '',
E      'http.method': 'GET',
E      'http.query': <ANY>}
E     Full diff:
E       {
E     -  'aws.request.url': <ANY>,
E     -  'http.fragment': '',
E     -  'http.method': 'GET',
E     +  'http.request.method': 'GET',
E     ?       ++++++++
E     -  'http.query': <ANY>,
E       }

test_breadcrumb

File: tests.integrations.boto3.test_s3
Suite: py3.7-boto3-v1.16.63
Error: AssertionError: assert {'http.request.method': 'GET'} == {'aws.request.url': , 'http.method': 'GET', 'http.query': , 'http.fragment': ''} Left contains 1 more item: {'http.request.method': 'GET'} Right contains 4 more items: {'aws.request.url': , 'http.fragment': '', 'http.method': 'GET', 'http.query': } Full diff: { - 'aws.request.url': , - 'http.fragment': '', - 'http.method': 'GET', + 'http.request.method': 'GET', ? ++++++++ - 'http.query': , }

Stack Trace
tests/integrations/boto3/test_s3.py:252: in test_breadcrumb
    assert crumb["data"] == ApproxDict(
E   AssertionError: assert {'http.request.method': 'GET'} == {'aws.request.url': <ANY>, 'http.method': 'GET', 'http.query': <ANY>, 'http.fragment': ''}
E     Left contains 1 more item:
E     {'http.request.method': 'GET'}
E     Right contains 4 more items:
E     {'aws.request.url': <ANY>,
E      'http.fragment': '',
E      'http.method': 'GET',
E      'http.query': <ANY>}
E     Full diff:
E       {
E     -  'aws.request.url': <ANY>,
E     -  'http.fragment': '',
E     -  'http.method': 'GET',
E     +  'http.request.method': 'GET',
E     ?       ++++++++
E     -  'http.query': <ANY>,
E       }

test_breadcrumb

File: tests.integrations.boto3.test_s3
Suite: py3.7-boto3-v1.24.96
Error: AssertionError: assert {'http.request.method': 'GET'} == {'aws.request.url': , 'http.method': 'GET', 'http.query': , 'http.fragment': ''} Left contains 1 more item: {'http.request.method': 'GET'} Right contains 4 more items: {'aws.request.url': , 'http.fragment': '', 'http.method': 'GET', 'http.query': } Full diff: { - 'aws.request.url': , - 'http.fragment': '', - 'http.method': 'GET', + 'http.request.method': 'GET', ? ++++++++ - 'http.query': , }

Stack Trace
tests/integrations/boto3/test_s3.py:252: in test_breadcrumb
    assert crumb["data"] == ApproxDict(
E   AssertionError: assert {'http.request.method': 'GET'} == {'aws.request.url': <ANY>, 'http.method': 'GET', 'http.query': <ANY>, 'http.fragment': ''}
E     Left contains 1 more item:
E     {'http.request.method': 'GET'}
E     Right contains 4 more items:
E     {'aws.request.url': <ANY>,
E      'http.fragment': '',
E      'http.method': 'GET',
E      'http.query': <ANY>}
E     Full diff:
E       {
E     -  'aws.request.url': <ANY>,
E     -  'http.fragment': '',
E     -  'http.method': 'GET',
E     +  'http.request.method': 'GET',
E     ?       ++++++++
E     -  'http.query': <ANY>,
E       }

test_breadcrumb

File: tests.integrations.boto3.test_s3
Suite: py3.8-boto3-v1.16.63
Error: AssertionError: assert {'http.request.method': 'GET'} == {'aws.request.url': , 'http.method': 'GET', 'http.query': , 'http.fragment': ''} Left contains 1 more item: {'http.request.method': 'GET'} Right contains 4 more items: {'aws.request.url': , 'http.fragment': '', 'http.method': 'GET', 'http.query': } Full diff: { - 'aws.request.url': , - 'http.fragment': '', - 'http.method': 'GET', + 'http.request.method': 'GET', ? ++++++++ - 'http.query': , }

Stack Trace
tests/integrations/boto3/test_s3.py:252: in test_breadcrumb
    assert crumb["data"] == ApproxDict(
E   AssertionError: assert {'http.request.method': 'GET'} == {'aws.request.url': <ANY>, 'http.method': 'GET', 'http.query': <ANY>, 'http.fragment': ''}
E     
E     Left contains 1 more item:
E     {'http.request.method': 'GET'}
E     Right contains 4 more items:
E     {'aws.request.url': <ANY>,
E      'http.fragment': '',
E      'http.method': 'GET',
E      'http.query': <ANY>}
E     
E     Full diff:
E       {
E     -     'aws.request.url': <ANY>,
E     -     'http.fragment': '',
E     -     'http.method': 'GET',
E     +     'http.request.method': 'GET',
E     ?          ++++++++
E     -     'http.query': <ANY>,
E       }

test_breadcrumb

File: tests.integrations.boto3.test_s3
Suite: py3.8-boto3-v1.35.99
Error: AssertionError: assert {'http.request.method': 'GET'} == {'aws.request.url': , 'http.method': 'GET', 'http.query': , 'http.fragment': ''} Left contains 1 more item: {'http.request.method': 'GET'} Right contains 4 more items: {'aws.request.url': , 'http.fragment': '', 'http.method': 'GET', 'http.query': } Full diff: { - 'aws.request.url': , - 'http.fragment': '', - 'http.method': 'GET', + 'http.request.method': 'GET', ? ++++++++ - 'http.query': , }

Stack Trace
tests/integrations/boto3/test_s3.py:252: in test_breadcrumb
    assert crumb["data"] == ApproxDict(
E   AssertionError: assert {'http.request.method': 'GET'} == {'aws.request.url': <ANY>, 'http.method': 'GET', 'http.query': <ANY>, 'http.fragment': ''}
E     
E     Left contains 1 more item:
E     {'http.request.method': 'GET'}
E     Right contains 4 more items:
E     {'aws.request.url': <ANY>,
E      'http.fragment': '',
E      'http.method': 'GET',
E      'http.query': <ANY>}
E     
E     Full diff:
E       {
E     -     'aws.request.url': <ANY>,
E     -     'http.fragment': '',
E     -     'http.method': 'GET',
E     +     'http.request.method': 'GET',
E     ?          ++++++++
E     -     'http.query': <ANY>,
E       }

✅ Patch coverage is 87.50%. Project has 2597 uncovered lines.
❌ Project coverage is 89.85%. Comparing base (base) to head (head).

Files with missing lines (1)
File Patch % Lines
sentry_sdk/integrations/boto3.py 87.50% ⚠️ 2 Missing and 1 partials
Coverage diff
@@            Coverage Diff             @@
##          main       #PR       +/-##
==========================================
- Coverage    90.30%    89.85%    -0.45%
==========================================
  Files          193       193         —
  Lines        25601     25579       -22
  Branches      9436      9422       -14
==========================================
+ Hits         23116     22982      -134
- Misses        2485      2597      +112
- Partials      1435      1420       -15

Generated by Codecov Action

Comment on lines +80 to +90
if parsed_url and should_send_default_pii():
breadcrumb.update(
{
SPANDATA.URL_FULL: parsed_url.url,
SPANDATA.URL_QUERY: parsed_url.query,
SPANDATA.URL_FRAGMENT: parsed_url.fragment,
}
)

if request.method is not None:
breadcrumb[SPANDATA.HTTP_REQUEST_METHOD] = request.method

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.

Boto3 breadcrumbs drop URL data and change keys outside stream mode

This path always emits stream-style breadcrumb keys and only includes URL fields when should_send_default_pii() is true, so default non-stream users lose aws.request.url/http.method/http.query and often get method-only crumbs. Keep the legacy breadcrumb shape when span streaming is off, or document/migrate the contract and update test_breadcrumb.

Evidence
  • _sentry_request_created() now always builds crumbs with SPANDATA.HTTP_REQUEST_METHOD and PII-gated URL_FULL/URL_QUERY/URL_FRAGMENT.
  • The removed non-stream branch previously set aws.request.url, SPANDATA.HTTP_METHOD, SPANDATA.HTTP_QUERY, and SPANDATA.HTTP_FRAGMENT whenever parsed_url existed, without a PII gate.
  • tests/integrations/boto3/test_s3.py::test_breadcrumb still expects those legacy keys under default (non-stream) init.
  • Stdlib keeps the dual-path crumb contract (HTTP_METHOD/url vs stream keys), so this is a boto3-only break for existing consumers.

Identified by Warden · code-review · APS-2P9

Comment on lines 109 to +149

with sentry_sdk.traces.start_span(name="custom parent") as span, MockResponse(
s3.meta.client, 200, {}, b"hello"
):
body = obj.get()["Body"]
assert body.read(1) == b"h"
assert body.read(2) == b"el"
assert body.read(3) == b"lo"
assert body.read(1) == b""
span.end()
if not send_default_pii:
assert "url.full" not in span1["attributes"]
assert "url.fragment" not in span1["attributes"]
assert "url.query" not in span1["attributes"]

sentry_sdk.flush()
spans = [item.payload for item in items]
assert len(spans) == 3

span1 = spans[0]
assert span1["attributes"]["sentry.op"] == "http.client"
assert span1["name"] == "aws.s3.GetObject"

expected_attrs = {
"http.request.method": "GET",
"rpc.method": "S3/GetObject",
"sentry.environment": "production",
"sentry.op": "http.client",
"sentry.origin": "auto.http.boto3",
"sentry.release": mock.ANY,
"sentry.sdk.name": "sentry.python",
"sentry.sdk.version": mock.ANY,
"sentry.segment.id": mock.ANY,
"sentry.segment.name": "custom parent",
"server.address": mock.ANY,
"thread.id": mock.ANY,
"thread.name": mock.ANY,
}
if send_default_pii:
expected_attrs["url.full"] = "https://bucket.s3.amazonaws.com/foo.pdf"
expected_attrs["url.fragment"] = ""
expected_attrs["url.query"] = ""
assert span1["attributes"] == ApproxDict(expected_attrs)

if not send_default_pii:
assert "url.full" not in span1["attributes"]
assert "url.fragment" not in span1["attributes"]
assert "url.query" not in span1["attributes"]

span2 = spans[1]
assert span2["attributes"]["sentry.op"] == "http.client.stream"
assert span2["name"] == "aws.s3.GetObject"
assert span2["parent_span_id"] == span1["span_id"]
else:
events = capture_events()

with sentry_sdk.start_transaction() as transaction, MockResponse(
s3.meta.client, 200, {}, b"hello"
):
body = obj.get()["Body"]
assert body.read(1) == b"h"
assert body.read(2) == b"el"
assert body.read(3) == b"lo"
assert body.read(1) == b""
transaction.finish()

(event,) = events
assert event["type"] == "transaction"
assert len(event["spans"]) == 2

span1 = event["spans"][0]
assert span1["op"] == "http.client"
assert span1["description"] == "aws.s3.GetObject"
assert span1["data"] == ApproxDict(
{
"http.method": "GET",
"aws.request.url": "https://bucket.s3.amazonaws.com/foo.pdf",
"http.fragment": "",
"http.query": "",
}
)

span2 = event["spans"][1]
assert span2["op"] == "http.client.stream"
assert span2["description"] == "aws.s3.GetObject"
assert span2["parent_span_id"] == span1["span_id"]
span2 = spans[1]
assert span2["attributes"]["sentry.op"] == "http.client.stream"
assert span2["name"] == "aws.s3.GetObject"
assert span2["parent_span_id"] == span1["span_id"]


@pytest.mark.parametrize("span_streaming", [True, False])
def test_streaming_close(
sentry_init,
capture_events,
capture_items,
span_streaming,
):
sentry_init(
traces_sample_rate=1.0,
integrations=[Boto3Integration()],
trace_lifecycle="stream" if span_streaming else "static",
trace_lifecycle="stream",
)

s3 = session.resource("s3")
obj = s3.Bucket("bucket").Object("foo.pdf")
items = capture_items("span")

if span_streaming:
items = capture_items("span")

with sentry_sdk.traces.start_span(name="custom parent") as span, MockResponse(
s3.meta.client, 200, {}, b"hello"
):
body = obj.get()["Body"]
assert body.read(1) == b"h"
body.close() # close partially-read stream
span.end()

sentry_sdk.flush()
spans = [item.payload for item in items]
assert len(spans) == 3
span1 = spans[0]
assert span1["attributes"]["sentry.op"] == "http.client"
span2 = spans[1]
assert span2["attributes"]["sentry.op"] == "http.client.stream"
else:
events = capture_events()

with sentry_sdk.start_transaction() as transaction, MockResponse(
s3.meta.client, 200, {}, b"hello"
):
body = obj.get()["Body"]
assert body.read(1) == b"h"
body.close() # close partially-read stream
transaction.finish()
with sentry_sdk.traces.start_span(name="custom parent") as span, MockResponse(
s3.meta.client, 200, {}, b"hello"
):
body = obj.get()["Body"]
assert body.read(1) == b"h"
body.close() # close partially-read stream
span.end()

(event,) = events
assert event["type"] == "transaction"
assert len(event["spans"]) == 2
span1 = event["spans"][0]
assert span1["op"] == "http.client"
span2 = event["spans"][1]
assert span2["op"] == "http.client.stream"
sentry_sdk.flush()
spans = [item.payload for item in items]
assert len(spans) == 3
span1 = spans[0]
assert span1["attributes"]["sentry.op"] == "http.client"
span2 = spans[1]
assert span2["attributes"]["sentry.op"] == "http.client.stream"

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.

Boto3 instrumentation drops AWS operation spans in static lifecycle

The Boto3 integration now records its AWS operation span only through the span-streaming API. With the default or trace_lifecycle="static" lifecycle, sentry_sdk.traces.get_current_span() is always None, so _sentrysdk_span is never stored and the integration emits no aws.<service>.<operation> span. If removing static transaction tracing is intentional, please document this compatibility change and the required migration to trace_lifecycle="stream"; otherwise retain a static start_span/start_transaction path.

Evidence
  • _sentry_request_created() only checks sentry_sdk.traces.get_current_span() before calling sentry_sdk.traces.start_span().
  • traces.get_current_span() returns a value only for the stream lifecycle; default/static scopes expose no streamed span.
  • Consequently, static requests never receive _sentrysdk_span in request.context, and _sentry_after_call() / _sentry_after_call_error() return without emitting an AWS operation span.
  • The SDK documents trace_lifecycle=None/"static" as a supported completed-transaction lifecycle, while peer HTTP integrations retain a static fallback.
Also found at 1 additional location
  • tests/integrations/boto3/test_s3.py:161-213

Identified by Warden · code-review · XZY-VT9

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.

Remove transaction-based tracing from boto3

1 participant