diff --git a/docs/platforms/python/tracing/instrumentation/custom-instrumentation/index.mdx b/docs/platforms/python/tracing/instrumentation/custom-instrumentation/index.mdx index 509d7af43d2ec..0bccf781a755e 100644 --- a/docs/platforms/python/tracing/instrumentation/custom-instrumentation/index.mdx +++ b/docs/platforms/python/tracing/instrumentation/custom-instrumentation/index.mdx @@ -5,19 +5,31 @@ description: "Learn how to capture performance data on any action in your app." The Sentry SDK for Python does a very good job of auto instrumenting your application. If you use one of the popular frameworks, we've got you covered because well-known operations like HTTP calls and database queries will be instrumented out of the box. The Sentry SDK will also check your installed Python packages and auto-enable the matching SDK integrations. If you want to enable tracing in a piece of code that performs some other operations, add the `@sentry_sdk.trace` decorator. -## Add a Transaction + + +This page covers both transaction mode (default) and stream mode. See New Spans to learn more. + + + + + +The parameter `name` in `start_span()` used to be called `description`. In version 2.15.0 `description` was deprecated and from 2.15.0 on, only `name` should be used. `description` will be removed in `3.0.0`. + + -Adding transactions will allow you to instrument and capture certain regions of your code. +## Add a Transaction/Service Span + +Adding transactions or service spans (in stream mode) will allow you to instrument and capture certain regions of your code. -If you're using one of Sentry's SDK integrations, transactions will be created for you automatically. +If you're using one of Sentry's SDK integrations, transactions/service spans will be created for you automatically. -The following example creates a transaction for an expensive operation (in this case, `eat_pizza`), and then sends the result to Sentry: +The following example creates a transaction/service span for an expensive operation (in this case, `eat_pizza`), and then sends the result to Sentry: -```python +```python {tabTitle:Transaction Mode (Default)} import sentry_sdk def eat_slice(slice): @@ -29,27 +41,44 @@ def eat_pizza(pizza): eat_slice(pizza.slices.pop()) ``` -The [API reference](https://getsentry.github.io/sentry-python/api.html#sentry_sdk.api.start_transaction) documents `start_transaction` and all its parameters. +```python {tabTitle:Stream Mode} +import sentry_sdk + +def eat_slice(slice): + ... + +def eat_pizza(pizza): + with sentry_sdk.traces.start_span( + name="Eat Pizza", + attributes={"sentry.op": "task"}, + # remove the parent span to create service span + parent_span=None, + ): + while pizza.slices > 0: + eat_slice(pizza.slices.pop()) +``` + +The [API reference](https://getsentry.github.io/sentry-python/api.html#performance-monitoring) documents `start_transaction` and `start_span`, along with their parameters. -Note that `sentry_sdk.start_transaction()` is meant be used as a context manager. This ensures that the transaction will be properly set as active and any spans created within will be attached to it. +Note that `sentry_sdk.start_transaction()` (transaction mode) and `sentry_sdk.traces.start_span()` (stream mode) are meant be used as context managers. This ensures that the transaction/service span will be properly set as active and any spans created within will be attached to it. -## Add Spans to a Transaction +## Add Spans to a Transaction/Service Span -If you want to have more fine-grained performance monitoring, you can add child spans to your transaction, which can be done by either: +If you want to have more fine-grained performance monitoring, you can add child spans to your transaction/service span, which can be done by either: - Using a context manager - Using a decorator (this works on sync and async functions) - Manually starting and finishing a span -Calling `sentry_sdk.start_span()` will find the current active transaction and attach the span to it. +Calling `sentry_sdk.start_span()` in transaction mode or `sentry_sdk.traces.start_span()` in stream mode will find the current active transaction/span and attach the new span to it. ### Using a Context Manager -```python +```python {tabTitle:Transaction Mode (Default)} import sentry_sdk def eat_slice(slice): @@ -62,15 +91,26 @@ def eat_pizza(pizza): eat_slice(pizza.slices.pop()) ``` - +```python {tabTitle:Stream Mode} +import sentry_sdk -The parameter `name` in `start_span()` used to be called `description`. In version 2.15.0 `description` was deprecated and from 2.15.0 on, only `name` should be used. `description` will be removed in `3.0.0`. +def eat_slice(slice): + ... - +def eat_pizza(pizza): + with sentry_sdk.traces.start_span( + name="Eat Pizza", + attributes={"sentry.op": "task"}, + parent_span=None, + ): + while pizza.slices > 0: + with sentry_sdk.traces.start_span(name="Eat Slice"): + eat_slice(pizza.slices.pop()) +``` ### Using a Decorator -```python +```python {tabTitle:Transaction Mode (Default)} import sentry_sdk @sentry_sdk.trace @@ -83,17 +123,34 @@ def eat_pizza(pizza): eat_slice(pizza.slices.pop()) ``` +```python {tabTitle:Stream Mode} +import sentry_sdk + +@sentry_sdk.traces.trace +def eat_slice(slice): + ... + +def eat_pizza(pizza): + with sentry_sdk.traces.start_span( + name="Eat Pizza", + attributes={"sentry.op": "task"}, + parent_span=None, + ): + while pizza.slices > 0: + eat_slice(pizza.slices.pop()) +``` + See the [@sentry_sdk.trace decoration section](#sentry_sdktrace-decorator) below for more details. - + -When tracing a static or class method, you **must** add the `@sentry_sdk.trace` decorator **after** the `@staticmethod` or `@classmethod` decorator (i.e., **closer** to the function definition). Otherwise, your function will break! +When tracing a static or class method, you **must** add the `@sentry_sdk.trace` decorator **after** the `@staticmethod` or `@classmethod` decorator (i.e., **closer** to the function definition). Otherwise, your function will break! This applies in both transaction mode and stream mode. ### Manually Starting and Finishing a Span -```python +```python {tabTitle:Transaction Mode (Default)} import sentry_sdk def eat_slice(slice): @@ -107,11 +164,23 @@ def eat_pizza(pizza): span.finish() ``` - +```python {tabTitle:Stream Mode} +import sentry_sdk -The parameter `name` in `start_span()` used to be called `description`. In version 2.15.0 `description` was deprecated and from 2.15.0 on, only `name` should be used. `description` will be removed in `3.0.0`. +def eat_slice(slice): + ... - +def eat_pizza(pizza): + with sentry_sdk.traces.start_span( + name="Eat Pizza", + attributes={"sentry.op": "task"}, + parent_span=None, + ): + while pizza.slices > 0: + span = sentry_sdk.traces.start_span(name="Eat Slice") + eat_slice(pizza.slices.pop()) + span.finish() +``` When you create your span manually, make sure to call `span.finish()` after the block of code you want to wrap in a span to finish the span. If you do not finish the span it will not be sent to Sentry. @@ -121,7 +190,7 @@ Spans can be nested to form a span tree. If you'd like to learn more, read our [ ### Using a Context Manager -```python +```python {tabTitle:Transaction Mode (Default)} import sentry_sdk def chew(): @@ -133,15 +202,21 @@ def eat_slice(slice): chew() ``` - +```python {tabTitle:Stream Mode} +import sentry_sdk -The parameter `name` in `start_span()` used to be called `description`. In version 2.15.0 `description` was deprecated and from 2.15.0 on, only `name` should be used. `description` will be removed in `3.0.0`. +def chew(): + ... - +def eat_slice(slice): + with sentry_sdk.traces.start_span(name="Eat Slice"): + with sentry_sdk.traces.start_span(name="Chew"): + chew() +``` ### Using a Decorator -```python +```python {tabTitle:Transaction Mode (Default)} import sentry_sdk @sentry_sdk.trace @@ -153,11 +228,23 @@ def eat_slice(slice): chew() ``` +```python {tabTitle:Stream Mode} +import sentry_sdk + +@sentry_sdk.traces.trace +def chew(): + ... + +@sentry_sdk.traces.trace +def eat_slice(slice): + chew() +``` + See the [@sentry_sdk.trace decoration section](#sentry_sdktrace-decorator) below for more details. -### Manually +### Manually Starting and Finishing -```python +```python {tabTitle:Transaction Mode (Default)} import sentry_sdk def chew(): @@ -173,32 +260,72 @@ def eat_slice(slice): parent_span.finish() ``` - +```python {tabTitle:Stream Mode} +import sentry_sdk -The parameter `name` in `start_span()` used to be called `description`. In version 2.15.0 `description` was deprecated and from 2.15.0 on, only `name` should be used. `description` will be removed in `3.0.0`. +def chew(): + ... - +def eat_slice(slice): + parent_span = sentry_sdk.traces.start_span(name="Eat Slice") + + # pass the parent span via `parent_span` to create a child span + child_span = sentry_sdk.traces.start_span(name="Chew", parent_span=parent_span) + chew() + child_span.finish() + + parent_span.finish() +``` -The parameters of `start_span()` and `start_child()` are the same. See the [API reference](https://getsentry.github.io/sentry-python/api.html#sentry_sdk.api.start_span) for more details. +In transaction mode, the parameters of `start_span()` and `start_child()` are the same. See the [API reference](https://getsentry.github.io/sentry-python/api.html#sentry_sdk.api.start_span) for more details. +In stream mode, however, there's no separate `start_child()` method. Instead, pass the parent span explicitly via `parent_span` when starting the child (as shown in the example above). When you create your span manually, make sure to call `span.finish()` after the block of code you want to wrap in a span to finish the span. If you do not finish the span it will not be sent to Sentry. +### Sibling Spans + + + +Only available in stream mode. + + + +In stream mode, a span normally attaches to whatever span is currently active. To control parentage explicitly, for example to make a span a sibling instead of a child, pass `parent_span` directly: + +```python +import sentry_sdk + +def eat_slice(slice): + ... + +def chew(): + ... + +def eat_pizza(pizza): + with sentry_sdk.traces.start_span(name="Eat Pizza") as pizza_span: + with sentry_sdk.traces.start_span(name="Eat Slice"): + with sentry_sdk.traces.start_span(name="Chew", parent_span=pizza_span): + # "Chew" is a sibling of "Eat Slice", not its child + chew() + eat_slice(pizza.slices.pop()) +``` + ## @sentry_sdk.trace decorator -You can set `op`, `name` and `attributes` parameters in the `@sentry_sdk.trace` decorator to customize your spans. Attribute values can only be primitive types (like `int`, `float`, `bool`, `str`) or a list of those types without mixing types. +In transaction mode, you can set `op`, `name` and `attributes` parameters in the `@sentry_sdk.trace` decorator to customize your spans. Attribute values can only be primitive types (like `int`, `float`, `bool`, `str`) or a list of those types without mixing types. -When tracing a static or class method, you **must** add the `@sentry_sdk.trace` decorator **after** the `@staticmethod` or `@classmethod` decorator (i.e., **closer** to the function definition). Otherwise, your function will break. +In stream mode, the decorator is `@sentry_sdk.traces.trace` and accepts `name`, `attributes`, and `active`. Note that there's no `op` parameter; set the operation as the `sentry.op` key in `attributes` instead. Attribute values can only be primitive types (like `int`, `float`, `bool`, `str`) or a list of those types without mixing types. - + -The parameters `op`, `name` and `attributes` were added to the `@sentry_sdk.trace` decorator in version 2.35.0. +When tracing a static or class method, you **must** add the decorator **after** the `@staticmethod` or `@classmethod` decorator (i.e., **closer** to the function definition). Otherwise, your function will break. -```python {diff} +```python {tabTitle:Transaction Mode (Default)} import sentry_sdk -+@sentry_sdk.trace(op="my_op", name="Paul", attributes={"x": True}) + @sentry_sdk.trace(op="my_op", name="Paul", attributes={"x": True}) def my_function(i): ... @@ -210,6 +337,21 @@ The parameters `op`, `name` and `attributes` were added to the `@sentry_sdk.trac root_function() ``` +```python {tabTitle:Stream Mode} +import sentry_sdk + + @sentry_sdk.traces.trace(name="Paul", attributes={"sentry.op": "my_op", "x": True}) + def my_function(i): + ... + + @sentry_sdk.traces.trace + def root_function(): + for i in range(3): + my_function(i) + + root_function() +``` + The code above will customize the `my_function` spans like this: ```mermaid @@ -225,6 +367,12 @@ gantt ### Span Templates + + +The `template` parameter is only available in transaction mode. + + + In the `@sentry_sdk.trace` decorator you can also specify a `template`. This helps create spans that follow a certain template. Currently this is only available for spans that are created for the [AI Agents instrumentation](/platforms/python/tracing/instrumentation/custom-instrumentation/ai-agents-module/#spans) of Sentry. Available templates are `AI_AGENT`, `AI_TOOL`, and `AI_CHAT`. @@ -297,9 +445,15 @@ To enable performance monitoring for the functions specified in `functions_to_tr ## Accessing the Current Transaction + + +Only available in transaction mode. + + + The `sentry_sdk.get_current_scope().transaction` property returns the active transaction or `None` if no transaction is active. You can use this property to modify data on the transaction. -```python +```python {tabTitle:Transaction Mode (Default)} import sentry_sdk def eat_pizza(pizza): @@ -314,11 +468,11 @@ def eat_pizza(pizza): ## Accessing the Current Span -To change data in the current span, use ` sentry_sdk.get_current_span()`. This function will return a span if there's one running, otherwise it will return `None`. +To change data in the current span, use ` sentry_sdk.get_current_span()` (transaction mode) or `sentry_sdk.traces.get_current_span()` (stream mode). This function will return a span if there's one running, otherwise it will return `None`. -In this example, we'll set a tag in the span created by the `@sentry_sdk.trace` decorator. +In this example, we'll set custom data in the span created by the `@sentry_sdk.trace` decorator. -```python +```python {tabTitle:Transaction Mode (Default)} import sentry_sdk @sentry_sdk.trace @@ -329,11 +483,22 @@ def eat_slice(slice): span.set_tag("slice_id", slice.id) ``` +```python {tabTitle:Stream Mode} +import sentry_sdk + +@sentry_sdk.traces.trace +def eat_slice(slice): + span = sentry_sdk.traces.get_current_span() + + if span is not None: + span.set_attribute("slice_id", slice.id) +``` + ## Improving Data on Transactions and Spans -You can add data attributes to your transactions. This data is visible in the trace explorer in Sentry. Data attributes can be of type `string`, `number` or `boolean`, as well as (non-mixed) arrays of these types: +In transaction mode, you can add data attributes to your transactions. This data is visible in the trace explorer in Sentry. Data attributes can be of type `string`, `number` or `boolean`, as well as (non-mixed) arrays of these types: -```python +```python {tabTitle:Transaction Mode (Default)} with sentry_sdk.start_transaction(name="my-transaction") as transaction: transaction.set_data("my-data-attribute-1", "value1") transaction.set_data("my-data-attribute-2", 42) @@ -344,9 +509,9 @@ with sentry_sdk.start_transaction(name="my-transaction") as transaction: transaction.set_data("my-data-attribute-6", [True, False, True]) ``` -You can add data attributes to your spans the same way, with the same type restrictions as described above. +You can add data attributes to any span the same way, with the same type restrictions as described above. -```python +```python {tabTitle:Transaction Mode (Default)} with sentry_sdk.start_span(name="my-span") as span: span.set_data("my-data-attribute-1", "value1") span.set_data("my-data-attribute-2", 42) @@ -357,32 +522,41 @@ with sentry_sdk.start_span(name="my-span") as span: span.set_data("my-data-attribute-6", [True, False, True]) ``` -To attach data attributes to the transaction and all its spans, you can use `before_send_transaction`: +```python {tabTitle:Stream Mode} +with sentry_sdk.traces.start_span(name="my-span") as span: + span.set_attribute("my-data-attribute-1", "value1") + span.set_attribute("my-data-attribute-2", 42) + span.set_attribute("my-data-attribute-3", True) + + # or use set_attributes to set multiple attributes at once + span.set_attributes({ + "my-data-attribute-4": ["value1", "value2", "value3"], + "my-data-attribute-5": [42, 43, 44], + "my-data-attribute-6": [True, False, True] + }) +``` -```python -import sentry_sdk -from sentry_sdk.types import Event, Hint + -def before_send_transaction(event: Event, hint: Hint) -> Event | None: - # Set the data attribute "foo" to "bar" on every span belonging to this - # transaction event - for span in event["spans"]: - span["data"]["foo"] = "bar" + - # Set the data on the transaction itself, too - event["contexts"]["trace"]["data"]["foo"] = "bar" +`before_send_span` can only modify span data and you cannot use it to drop spans. See [Dropping Spans](#dropping-spans) below. - return event + -sentry_sdk.init( - traces_sample_rate=1.0, - before_send_transaction=before_send_transaction, -) -``` +## Dropping Spans + + ## Update Current Span Shortcut -You can update the data of the currently running span using the `sentry_sdk.update_current_span()` function. You can set `op`, `name` and `attributes` to update your span. Attribute values can only be primitive types (like `int`, `float`, `bool`, `str`) or a list of those types without mixing types. + + +`sentry_sdk.update_current_span()` isn't available in stream mode. Retrieve the current span with `sentry_sdk.traces.get_current_span()` and update its attributes using `set_attribute()` or `set_attributes()`. + + + +In transaction mode, you can update the data of the currently running span using the `sentry_sdk.update_current_span()` function. You can set `op`, `name` and `attributes` to update your span. Attribute values can only be primitive types (like `int`, `float`, `bool`, `str`) or a list of those types without mixing types. ```python {diff} import sentry_sdk diff --git a/docs/platforms/python/tracing/span-lifecycle/index.mdx b/docs/platforms/python/tracing/span-lifecycle/index.mdx index 571bf58725f73..957b53e0c019d 100644 --- a/docs/platforms/python/tracing/span-lifecycle/index.mdx +++ b/docs/platforms/python/tracing/span-lifecycle/index.mdx @@ -12,13 +12,19 @@ To capture transactions and spans customized to your organization's needs, you m To add custom performance data to your application, you need to add custom instrumentation in the form of [spans](/concepts/key-terms/tracing/distributed-tracing/#traces-transactions-and-spans). Spans are a way to measure the time it takes for a specific action to occur. For example, you can create a span to measure the time it takes for a function to execute. + + +This page covers both transaction mode (default) and stream mode. See New Spans to learn more. + + + ## Span Lifecycle In Python, spans are typically created using a context manager, which automatically manages the span's lifecycle. When you create a span using a context manager, the span automatically starts when entering the context and ends when exiting it. This is the recommended approach for most scenarios. -```python +```python {tabTitle:Transaction Mode (Default)} import sentry_sdk # Start a span for a task @@ -30,13 +36,28 @@ with sentry_sdk.start_span(op="task", name="Create User"): # The span automatically ends here when the 'with' block exits ``` +```python {tabTitle:Stream Mode} +import sentry_sdk + +# Start a span for a task +with sentry_sdk.traces.start_span( + name="Create User", + attributes={"sentry.op": "task"}, +): + # Your code here + # The span will automatically end when exiting this block + user = create_user(email="user@example.com") + send_welcome_email(user) + # The span automatically ends here when the 'with' block exits +``` + You can call the context manager's `__enter__` and `__exit__` methods to more explicitly control the span's lifecycle. ## Span Context and Nesting When you create a span, it becomes the child of the current active span. This allows you to build a hierarchy of spans that represent the execution path of your application: -```python +```python {tabTitle:Transaction Mode (Default)} import sentry_sdk with sentry_sdk.start_span(op="process", name="Process Data"): @@ -51,21 +72,61 @@ with sentry_sdk.start_span(op="process", name="Process Data"): transform_data() ``` +```python {tabTitle:Stream Mode} +import sentry_sdk + +with sentry_sdk.traces.start_span( + name="Process Data", + attributes={"sentry.op": "process"}, +): + # This code is tracked in the "Process Data" span + + with sentry_sdk.traces.start_span( + name="Validate Input", + attributes={"sentry.op": "task"}, + ): + # This is now a child span of "Process Data" + validate_data() + + with sentry_sdk.traces.start_span( + name="Transform Data", + attributes={"sentry.op": "task"}, + ): + # Another child span + transform_data() +``` + ## Span Starting Options The following options can be used when creating spans: -| Option | Type | Description | -| ------------- | --------------- | ----------------------------------------------- | -| `op` | `string` | The operation of the span. | -| `name` | `string` | The name of the span. | -| `start_timestamp` | `datetime/float`| The start time of the span. | +### Transaction Mode (Default) + +| Option | Type | Description | +| ----------------- | ---------------- | --------------------------- | +| `op` | `string` | The operation of the span. | +| `name` | `string` | The name of the span. | +| `start_timestamp` | `datetime/float` | The start time of the span. | + +### Stream mode + +| Option | Type | Description | +| ----------------- | ---------------- | -------------------------------------------------------------------------- | +| `name` | `string` | The name of the span. | +| `attributes` | `dict` | Structured metadata for the span, including `sentry.op` for the operation. | +| `start_timestamp` | `datetime/float` | The start time of the span. | +| `parent_span` | `Span/None` | The parent to attach to. Pass None to force a service span. | + + + `op` is not a dedicated argument in stream mode. Set it as the `sentry.op` + attribute instead. + ## Using the Context Manager -For most scenarios, we recommend using the context manager approach with `sentry_sdk.start_span()`. This creates a new span that automatically starts when entering the context and ends when exiting it. +For most scenarios, we recommend using the context manager approach. This creates a new span that automatically starts when entering the context and ends when exiting it. -```python +```python {tabTitle:Transaction Mode (Default)} import sentry_sdk with sentry_sdk.start_span(op="db", name="Query Users") as span: @@ -76,9 +137,23 @@ with sentry_sdk.start_span(op="db", name="Query Users") as span: span.set_data("user_count", len(users)) ``` +```python {tabTitle:Stream Mode} +import sentry_sdk + +with sentry_sdk.traces.start_span( + name="Query Users", + attributes={"sentry.op": "db"}, +) as span: + # Perform a database query + users = db.query("SELECT * FROM users") + + # You can set attributes on the span + span.set_attribute("user_count", len(users)) +``` + The context manager also correctly handles exceptions, marking the span as failed if an exception occurs: -```python +```python {tabTitle:Transaction Mode (Default)} import sentry_sdk try: @@ -91,11 +166,27 @@ except Exception: pass ``` +```python {tabTitle:Stream Mode} +import sentry_sdk + +try: + with sentry_sdk.traces.start_span( + name="Call External API", + attributes={"sentry.op": "http"}, + ): + # If this raises an exception, the span will be marked as failed + response = requests.get("https://api.example.com/data") + response.raise_for_status() +except Exception: + # The span is already marked as failed and has ended + pass +``` + ## Getting the Current Span -You can access the currently active span using `sentry_sdk.get_current_span()`: +You can access the currently active span: -```python +```python {tabTitle:Transaction Mode (Default)} import sentry_sdk # Get the current active span @@ -104,8 +195,19 @@ if current_span: current_span.set_data("key", "value") ``` +```python {tabTitle:Stream Mode} +import sentry_sdk + +# Get the current active span +current_span = sentry_sdk.traces.get_current_span() +if current_span: + current_span.set_attribute("key", "value") +``` + ## Working with Transactions +`start_transaction` is only available in transaction mode. + [Transactions](/product/dashboards/sentry-dashboards/transaction-summary/#what-is-a-transaction) are a special type of span that represent a complete operation in your application, such as a web request. You can create transactions explicitly: ```python @@ -120,13 +222,38 @@ with sentry_sdk.start_transaction(name="Background Task", op="task") as transact pass ``` +## Working with Service Spans + +Service spans are only available in stream mode. + +In stream mode, a service span (the equivalent of a transaction) is just a span started with no parent. Pass `parent_span=None` to force this, even if a span is currently active: + +```python +import sentry_sdk + +with sentry_sdk.traces.start_span( + name="Background Task", + attributes={"sentry.op": "task"}, + parent_span=None, +) as service_span: + # Your code here + + # You can add child spans to the service span + with sentry_sdk.traces.start_span( + name="Data Processing", + attributes={"sentry.op": "subtask"}, + ): + # Process data + pass +``` + ## Improving Span Data ### Adding Span Attributes Span attributes customize information you can get through tracing. This information can be found in the traces views in Sentry, once you drill into a span. You can capture additional context with span attributes. These can be key-value pairs of various Python types. -```python +```python {tabTitle:Transaction Mode (Default)} import sentry_sdk with sentry_sdk.start_span(op="db", name="Query Users") as span: @@ -137,9 +264,23 @@ with sentry_sdk.start_span(op="db", name="Query Users") as span: span.set_data("result_count", len(users)) ``` +```python {tabTitle:Stream Mode} +import sentry_sdk + +with sentry_sdk.traces.start_span( + name="Query Users", + attributes={"sentry.op": "db"}, +) as span: + # Execute the query + users = db.query("SELECT * FROM users WHERE active = true") + + # You can add more attributes during execution + span.set_attribute("result_count", len(users)) +``` + You can also add attributes to an existing span: -```python +```python {tabTitle:Transaction Mode (Default)} import sentry_sdk # Get the current span @@ -150,42 +291,22 @@ if span: span.set_data("request_size", len(request.body)) ``` -### Adding Attributes to All Spans - -To add attributes to all spans, use the `before_send_transaction` callback: - -```python +```python {tabTitle:Stream Mode} import sentry_sdk -from sentry_sdk.types import Event, Hint - -def before_send_transaction(event: Event, hint: Hint) -> Event | None: - # Add attributes to the root span (transaction) - if "trace" in event.get("contexts", {}): - if "data" not in event["contexts"]["trace"]: - event["contexts"]["trace"]["data"] = {} - event["contexts"]["trace"]["data"].update({ - "app_version": "1.2.3", - "environment_region": "us-west-2" - }) - - # Add attributes to all child spans - for span in event.get("spans", []): - if "data" not in span: - span["data"] = {} +# Get the current span +span = sentry_sdk.traces.get_current_span() +if span: + # Set individual attributes + span.set_attribute("user_id", user.id) + span.set_attribute("request_size", len(request.body)) +``` - span["data"].update({ - "component_version": "2.0.0", - "deployment_stage": "production" - }) +### Adding Attributes to All Spans - return event + -sentry_sdk.init( - # ... - before_send_transaction=before_send_transaction -) -``` +Note that `before_send_span` can only modify span data and not drop spans – use [`ignore_spans`](#dropping-spans) instead. ### Adding Span Operations ("op") @@ -193,7 +314,7 @@ Spans can have an operation associated with them, which helps Sentry understand Sentry maintains a [list of well-known span operations](https://develop.sentry.dev/sdk/performance/span-operations/#list-of-operations) that you should use when applicable: -```python +```python {tabTitle:Transaction Mode (Default)} import sentry_sdk # HTTP client operation @@ -213,11 +334,41 @@ with sentry_sdk.start_span(op="file.read", name="Read Config"): config = json.load(f) ``` +```python {tabTitle:Stream Mode} +import sentry_sdk + +# HTTP client operation +with sentry_sdk.traces.start_span( + name="Fetch User Data", + attributes={"sentry.op": "http.client"}, +): + response = requests.get("https://api.example.com/users") + +# Database operation +with sentry_sdk.traces.start_span( + name="Save User", + attributes={"sentry.op": "db"}, +): + db.execute( + "INSERT INTO users (name, email) VALUES (%s, %s)", + (user.name, user.email), + ) + +# File I/O operation +with sentry_sdk.traces.start_span( + name="Read Config", + attributes={"sentry.op": "file.read"}, +): + with open("config.json", "r") as f: + config = json.load(f) +``` + ### Updating the Span Status -You can update the status of a span to indicate whether it succeeded or failed: +You can update the status of a span to indicate whether it succeeded or failed. +In stream mode, status can only be `ok` (default) or `error`. -```python +```python {tabTitle:Transaction Mode (Default)} import sentry_sdk with sentry_sdk.start_span(op="task", name="Process Payment") as span: @@ -234,3 +385,28 @@ with sentry_sdk.start_span(op="task", name="Process Payment") as span: # Span will automatically be marked as failed when an exception occurs raise ``` + +```python {tabTitle:Stream Mode} +import sentry_sdk + +with sentry_sdk.traces.start_span( + name="Process Payment", + attributes={"sentry.op": "task"}, +) as span: + try: + result = process_payment(payment_id) + if result.success: + # Mark the span as successful (this is also the default) + span.status = "ok" + else: + # Mark the span as failed + span.status = "error" + span.set_attribute("error_reason", result.error) + except Exception: + # Span will automatically be marked as failed when an exception occurs + raise +``` + +## Dropping Spans + + diff --git a/docs/platforms/python/tracing/span-metrics/index.mdx b/docs/platforms/python/tracing/span-metrics/index.mdx index 4483c5c3be834..a280d813c6c60 100644 --- a/docs/platforms/python/tracing/span-metrics/index.mdx +++ b/docs/platforms/python/tracing/span-metrics/index.mdx @@ -17,11 +17,17 @@ Span metrics allow you to extend the default metrics that are collected by traci 1. [Adding metrics to existing spans](#adding-metrics-to-existing-spans) 2. [Creating dedicated spans with custom metrics](#creating-dedicated-metric-spans) + + +This page covers both transaction mode (default) and stream mode. See New Spans to learn more. + + + ## Adding Metrics to Existing Spans You can enhance existing spans with custom metrics by adding data. This is useful when you want to augment automatic instrumentation or add contextual data to spans you've already created. -```python +```python {tabTitle:Transaction Mode (Default)} span = sentry_sdk.get_current_span() if span: # Add individual metrics @@ -32,6 +38,17 @@ if span: span.set_data("processing.duration_ms", 127) ``` +```python {tabTitle:Stream Mode} +span = sentry_sdk.traces.get_current_span() +if span: + # Add individual metrics + span.set_attribute("database.rows_affected", 42) + span.set_attribute("cache.hit_rate", 0.85) + span.set_attribute("memory.heap_used", 1024000) + span.set_attribute("queue.length", 15) + span.set_attribute("processing.duration_ms", 127) +``` + ### Best Practices for Span Data When adding metrics as span data: @@ -44,7 +61,7 @@ When adding metrics as span data: For more detailed operations, tasks, or process tracking, you can create custom dedicated spans that focus on specific metrics or attributes that you want to track. This approach provides better discoverability and more precise span configurations, however it can also create more noise in your trace waterfall. -```python +```python {tabTitle:Transaction Mode (Default)} with sentry_sdk.start_span( op="db.metrics", name="Database Query Metrics" @@ -59,43 +76,27 @@ with sentry_sdk.start_span( pass ``` +```python {tabTitle:Stream Mode} +with sentry_sdk.traces.start_span( + name="Database Query Metrics", + attributes={"sentry.op": "db.metrics"}, +) as span: + # Set metrics after creating the span + span.set_attributes({ + "db.query_type": "SELECT", + "db.table": "users", + "db.execution_time_ms": 45, + "db.rows_returned": 100, + "db.connection_pool_size": 5, + }) + # Your database operation here + pass +``` + For detailed examples of how to implement span metrics in common scenarios, see our Span Metrics Examples guide. ## Adding Metrics to All Spans -To consistently add metrics across all spans in your application, you can use the `before_send_transaction` callback: - -```python -import sentry_sdk -from sentry_sdk.types import Event, Hint - -def before_send_transaction(event: Event, hint: Hint) -> Event | None: - # Add metrics to the root span - if "trace" in event.get("contexts", {}): - if "data" not in event["contexts"]["trace"]: - event["contexts"]["trace"]["data"] = {} - - event["contexts"]["trace"]["data"].update({ - "app.version": "1.2.3", - "environment.region": "us-west-2" - }) - - # Add metrics to all child spans - for span in event.get("spans", []): - if "data" not in span: - span["data"] = {} - - span["data"].update({ - "app.component_version": "2.0.0", - "app.deployment_stage": "production" - }) - - return event - -sentry_sdk.init( - # ... - before_send_transaction=before_send_transaction -) -``` + For detailed examples of how to implement span metrics in common scenarios, see our Span Metrics Examples guide. diff --git a/platform-includes/performance/dropping-spans/python.mdx b/platform-includes/performance/dropping-spans/python.mdx new file mode 100644 index 0000000000000..71dea6d207cdc --- /dev/null +++ b/platform-includes/performance/dropping-spans/python.mdx @@ -0,0 +1,37 @@ +`ignore_span` is only available in stream mode. + +In stream mode, you can prevent specific spans from being created using `ignore_spans`, set under `_experiments`. It evaluates rules at span start, which can be strings, compiled regexes, or dictionaries with `name` and/or `attributes` conditions. If the dropped span is a service span, its children are dropped too. If it's a child span, only that span is dropped and its children are reparented to the nearest ancestor. + +```python +import re +import sentry_sdk + +sentry_sdk.init( + _experiments={ + "trace_lifecycle": "stream", + "ignore_spans": [ + # String match against span name + "/health", + + # Regex match against span name + re.compile(r"/flow/.*"), + + # Match by attributes (all must match) + { + "attributes": { + "service.id": "15def9a", + "flow.pipeline": "legacy", + } + }, + + # Match by name and attributes + { + "name": re.compile(r"/flow/.*"), + "attributes": { + "service.id": re.compile(r".*\.facade"), + }, + }, + ], + }, +) +``` diff --git a/platform-includes/tracing/span-metrics/add-all-spans/python.mdx b/platform-includes/tracing/span-metrics/add-all-spans/python.mdx new file mode 100644 index 0000000000000..bcf9676d3e4d7 --- /dev/null +++ b/platform-includes/tracing/span-metrics/add-all-spans/python.mdx @@ -0,0 +1,58 @@ +How you add data to every span depends on your tracing mode: + +- Transaction mode: `before_send_transaction` runs after the entire transaction finishes and has access to all data set during the span's lifetime. +- Stream mode: `before_send_span` (set under `_experiments`) can only work with attributes set at span start time. Attributes added later during the span's lifetime are **not available**. + +```python {tabTitle:Transaction Mode (Default)} +import sentry_sdk +from sentry_sdk.types import Event, Hint + +def before_send_transaction(event: Event, hint: Hint) -> Event | None: + # Add attributes to the root span (transaction) + if "trace" in event.get("contexts", {}): + if "data" not in event["contexts"]["trace"]: + event["contexts"]["trace"]["data"] = {} + + event["contexts"]["trace"]["data"].update({ + "app_version": "1.2.3", + "environment_region": "us-west-2" + }) + + # Add attributes to all child spans + for span in event.get("spans", []): + if "data" not in span: + span["data"] = {} + + span["data"].update({ + "component_version": "2.0.0", + "deployment_stage": "production" + }) + + return event + +sentry_sdk.init( + # ... + before_send_transaction=before_send_transaction +) +``` + +```python {tabTitle:Stream Mode} +import sentry_sdk + +def before_send_span(span, hint): + # Runs once per span, as it's flushed. + # Only attributes set at span-creation time are available here. + span["attributes"].update({ + "component_version": "2.0.0", + "deployment_stage": "production", + }) + return span + +sentry_sdk.init( + # ... + _experiments={ + "trace_lifecycle": "stream", + "before_send_span": before_send_span, + }, +) +```