Skip to content

docs(python): Update stream mode in tracing files (tracing part 3/3)#18533

Open
inventarSarah wants to merge 3 commits into
masterfrom
smi/span-first/python-tracing-3
Open

docs(python): Update stream mode in tracing files (tracing part 3/3)#18533
inventarSarah wants to merge 3 commits into
masterfrom
smi/span-first/python-tracing-3

Conversation

@inventarSarah

Copy link
Copy Markdown
Collaborator

DESCRIBE YOUR PR

This PR updates pages under Tracing with information about the new stream mode and the new Span APIs.

Part 3 out of 3
(result of splitting up PR #18511 into smaller parts)

Important

Broken links: I added links to the New Spans guide on these pages, but since this guide does not exist in this branch, we get a linting error.
Should only be merged after #18456

IS YOUR CHANGE URGENT?

Help us prioritize incoming PRs by letting us know when the change needs to go live.

  • Urgent deadline (GA date, etc.):
  • Other deadline:
  • None: Not urgent, can wait up to 1 week+

SLA

  • Teamwork makes the dream work, so please add a reviewer to your PRs.
  • Please give the docs team up to 1 week to review your PR unless you've added an urgent due date to it.
    Thanks in advance for your help!

PRE-MERGE CHECKLIST

Make sure you've checked the following before merging your changes:

  • Checked Vercel preview for correctness, including links
  • PR was reviewed and approved by any necessary SMEs (subject matter experts)
  • PR was reviewed and approved by a member of the Sentry docs team

LEGAL BOILERPLATE

Look, I get it. The entity doing business as "Sentry" was incorporated in the State of Delaware in 2015 as Functional Software, Inc. and is gonna need some rights from me in order to utilize my contributions in this here PR. So here's the deal: I retain all rights, title and interest in and to my contributions, and by keeping this boilerplate intact I confirm that Sentry can use, modify, copy, and redistribute my contributions, under Sentry's choice of terms.

EXTRA RESOURCES

@vercel

vercel Bot commented Jun 24, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
sentry-docs Ready Ready Preview, Comment Jun 24, 2026 1:09pm
1 Skipped Deployment
Project Deployment Actions Updated (UTC)
develop-docs Ignored Ignored Preview Jun 24, 2026 1:09pm

Request Review

@inventarSarah inventarSarah force-pushed the smi/span-first/python-tracing-3 branch from a1d549e to 9da9987 Compare June 24, 2026 11:50
@inventarSarah inventarSarah marked this pull request as ready for review June 24, 2026 11:50
Comment thread docs/platforms/python/tracing/configure-sampling/index.mdx
) as span:
try:
# Check inventory availability
inventory_start = time.time()

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.

Bug: Code examples in the span-metrics documentation use functions from the time, psutil, and os modules without importing them, which will cause a NameError at runtime.
Severity: HIGH

Suggested Fix

Add import time to the E-Commerce examples. Add import psutil and import os to the Data Processing Pipeline examples to make the code snippets self-contained and runnable.

Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent. Verify if this is a real issue. If it is, propose a fix; if not, explain why it's
not valid.

Location: docs/platforms/python/tracing/span-metrics/examples.mdx#L675

Potential issue: In the E-Commerce "Order processing service" examples for both
Transaction and Stream modes, the code uses `time.time()` to measure execution time but
fails to import the `time` module. Similarly, the Data Processing Pipeline examples use
`psutil.cpu_percent()`, `psutil.Process().memory_info()`, and `os.path.getsize()`
without importing the `psutil` or `os` modules. Users copying these examples will
encounter `NameError` exceptions at runtime when these functions are called.

Also affects:

  • docs/platforms/python/tracing/span-metrics/examples.mdx:614
  • docs/platforms/python/tracing/span-metrics/examples.mdx:801
  • docs/platforms/python/tracing/span-metrics/examples.mdx:809
  • docs/platforms/python/tracing/span-metrics/examples.mdx:887
  • docs/platforms/python/tracing/span-metrics/examples.mdx:895

Did we get this right? 👍 / 👎 to inform future reviews.

@inventarSarah inventarSarah Jun 24, 2026

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

added the missing imports

Comment thread docs/platforms/python/tracing/configure-sampling/index.mdx
Comment thread docs/platforms/python/tracing/configure-sampling/index.mdx
<Alert>
In **transaction mode**, `sentry_sdk.continue_trace()` returns a transaction, but does not start it. To start the transaction, use `start_transaction()`.

In **stream mode**, `sentry_sdk.traces.continue_trace()` replaces `sentry_sdk.continue_trace()` and is not a context manager. Instead, it sets the propagation context, and the next span created with `sentry_sdk.traces.start_span()` picks it up automatically.

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.

Instead is confusing here, both old and new set the propagation context.

Suggested change
In **stream mode**, `sentry_sdk.traces.continue_trace()` replaces `sentry_sdk.continue_trace()` and is not a context manager. Instead, it sets the propagation context, and the next span created with `sentry_sdk.traces.start_span()` picks it up automatically.
In **stream mode**, `sentry_sdk.traces.continue_trace()` replaces `sentry_sdk.continue_trace()` and is not a context manager. The next span created with `sentry_sdk.traces.start_span()` picks it up automatically.

@sentrivana sentrivana left a comment

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.

Approving to not block, but please see comments 🙏🏻

"parent_sample_rate": Optional[float], # the sample rate used by the parent (SDK-provided)
"attributes": dict[str, Any] # attributes set at span-creation time (SDK- and user-provided)
},
"custom_sampling_context": Optional[dict[str, Any]] # additional custom data for sampling

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.

The SDK will put whatever the user sets as custom sampling context on the top level, next to span_context, so there is no custom_sampling_context key (unless the user explicitly set it)

Suggested change
"custom_sampling_context": Optional[dict[str, Any]] # additional custom data for sampling
... # additional custom key-value pairs for sampling set via custom sampling context

Comment on lines +184 to +194
custom_sampling_ctx = sampling_context.get("custom_sampling_context") or {}
environment = os.environ.get("ENVIRONMENT", "development")

# Sample all spans in development
if environment == "development":
return 1.0

# Sample more spans if there are recent errors
# Note: hasRecentErrors is a custom attribute that needs to be set
if custom_sampling_ctx.get("hasRecentErrors") is True:
return 0.8

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.

There won't be a custom_sampling_context key in stream mode -- the user-set hasRecentErrors will be a top-level key of sampling_context

Suggested change
custom_sampling_ctx = sampling_context.get("custom_sampling_context") or {}
environment = os.environ.get("ENVIRONMENT", "development")
# Sample all spans in development
if environment == "development":
return 1.0
# Sample more spans if there are recent errors
# Note: hasRecentErrors is a custom attribute that needs to be set
if custom_sampling_ctx.get("hasRecentErrors") is True:
return 0.8
environment = os.environ.get("ENVIRONMENT", "development")
# Sample all spans in development
if environment == "development":
return 1.0
# Sample more spans if there are recent errors
# Note: hasRecentErrors is a custom attribute that needs to be set
if sampling_context.get("hasRecentErrors") is True:
return 0.8

Comment on lines +285 to +295
custom_sampling_ctx = sampling_context.get("custom_sampling_context") or {}

# Always sample for premium users
# Note: user.tier is a custom attribute that needs to be set
if custom_sampling_ctx.get("user", {}).get("tier") == "premium":
return 1.0

# Sample more spans for users experiencing errors
# Note: hasRecentErrors is a custom attribute
if custom_sampling_ctx.get("hasRecentErrors") is True:
return 0.8

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.

Suggested change
custom_sampling_ctx = sampling_context.get("custom_sampling_context") or {}
# Always sample for premium users
# Note: user.tier is a custom attribute that needs to be set
if custom_sampling_ctx.get("user", {}).get("tier") == "premium":
return 1.0
# Sample more spans for users experiencing errors
# Note: hasRecentErrors is a custom attribute
if custom_sampling_ctx.get("hasRecentErrors") is True:
return 0.8
# Always sample for premium users
# Note: user.tier is a custom attribute that needs to be set
if sampling_context.get("user", {}).get("tier") == "premium":
return 1.0
# Sample more spans for users experiencing errors
# Note: hasRecentErrors is a custom attribute
if sampling_context.get("hasRecentErrors") is True:
return 0.8

Comment on lines +401 to +419
custom_sampling_context = sampling_context.get("custom_sampling_context") or {}

# Sample based on user segment
# Note: user.segment is a custom attribute
user_segment = custom_sampling_context.get("user", {}).get("segment")
if user_segment == "enterprise":
return 0.8
elif user_segment == "premium":
return 0.5

# Sample based on transaction value
# Note: transaction.value is a custom attribute
transaction_value = custom_sampling_context.get("transaction", {}).get("value")
if transaction_value is not None and transaction_value > 1000: # High-value transactions
return 0.7

# Sample based on error rate in the service
# Note: service.error_rate is a custom attribute
error_rate = custom_sampling_context.get("service", {}).get("error_rate")

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.

Suggested change
custom_sampling_context = sampling_context.get("custom_sampling_context") or {}
# Sample based on user segment
# Note: user.segment is a custom attribute
user_segment = custom_sampling_context.get("user", {}).get("segment")
if user_segment == "enterprise":
return 0.8
elif user_segment == "premium":
return 0.5
# Sample based on transaction value
# Note: transaction.value is a custom attribute
transaction_value = custom_sampling_context.get("transaction", {}).get("value")
if transaction_value is not None and transaction_value > 1000: # High-value transactions
return 0.7
# Sample based on error rate in the service
# Note: service.error_rate is a custom attribute
error_rate = custom_sampling_context.get("service", {}).get("error_rate")
# Sample based on user segment
# Note: user.segment is a custom attribute
user_segment = sampling_context.get("user", {}).get("segment")
if user_segment == "enterprise":
return 0.8
elif user_segment == "premium":
return 0.5
# Sample based on transaction value
# Note: transaction.value is a custom attribute
transaction_value = sampling_context.get("transaction", {}).get("value")
if transaction_value is not None and transaction_value > 1000: # High-value transactions
return 0.7
# Sample based on error rate in the service
# Note: service.error_rate is a custom attribute
error_rate = sampling_context.get("service", {}).get("error_rate")

Comment on lines +508 to +524
custom_sampling_ctx = sampling_context.get("custom_sampling_context") or {}

# Sample more spans with high memory usage
# Note: memory_usage_mb is a custom attribute
memory_usage = custom_sampling_ctx.get("memory_usage_mb")
if memory_usage is not None and memory_usage > 500:
return 0.8

# Sample more spans with high CPU usage
# Note: cpu_percent is a custom attribute
cpu_percent = custom_sampling_ctx.get("cpu_percent")
if cpu_percent is not None and cpu_percent > 80:
return 0.8

# Sample more spans with high database load
# Note: db_connections is a custom attribute
db_connections = custom_sampling_ctx.get("db_connections")

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.

Suggested change
custom_sampling_ctx = sampling_context.get("custom_sampling_context") or {}
# Sample more spans with high memory usage
# Note: memory_usage_mb is a custom attribute
memory_usage = custom_sampling_ctx.get("memory_usage_mb")
if memory_usage is not None and memory_usage > 500:
return 0.8
# Sample more spans with high CPU usage
# Note: cpu_percent is a custom attribute
cpu_percent = custom_sampling_ctx.get("cpu_percent")
if cpu_percent is not None and cpu_percent > 80:
return 0.8
# Sample more spans with high database load
# Note: db_connections is a custom attribute
db_connections = custom_sampling_ctx.get("db_connections")
# Sample more spans with high memory usage
# Note: memory_usage_mb is a custom attribute
memory_usage = sampling_context.get("memory_usage_mb")
if memory_usage is not None and memory_usage > 500:
return 0.8
# Sample more spans with high CPU usage
# Note: cpu_percent is a custom attribute
cpu_percent = sampling_context.get("cpu_percent")
if cpu_percent is not None and cpu_percent > 80:
return 0.8
# Sample more spans with high database load
# Note: db_connections is a custom attribute
db_connections = sampling_context.get("db_connections")

This guide provides practical examples of using span attributes and metrics to solve common monitoring and debugging challenges in Python applications. Each example demonstrates how to instrument different components, showing how they work together within a distributed trace to provide end-to-end visibility.

<Alert>
This page covers both transaction mode (default) and stream mode. See{" "}

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.

Suggested change
This page covers both transaction mode (default) and stream mode. See{" "}
This page covers both transaction mode (default) and stream mode. See

Comment on lines +233 to +234
span.set_attribute("storage.actual_upload_time_ms",
(time.time() - upload_start) * 1000)

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.

Suggested change
span.set_attribute("storage.actual_upload_time_ms",
(time.time() - upload_start) * 1000)
span.set_attribute(
"storage.actual_upload_time_ms",
(time.time() - upload_start) * 1000
)

Comment on lines +902 to +903
span.set_attribute("resource.memory_used_mb",
psutil.Process().memory_info().rss / (1024 * 1024))

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.

Suggested change
span.set_attribute("resource.memory_used_mb",
psutil.Process().memory_info().rss / (1024 * 1024))
span.set_attribute(
"resource.memory_used_mb",
psutil.Process().memory_info().rss / (1024 * 1024)
)

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.

4 participants