Performance Monitoring
Sentry allows you to monitor the performance of your application, showing you how latency in one service may affect another service, and letting you pinpoint exactly which parts of a given operation may be responsible. To do this, it captures distributed traces consisting of transactions and spans, which measure individual services and individual operations within those services, respectively. You can learn more about this model in our Distributed Tracing docs. Performance Monitoring is available for the Sentry Python SDK version ≥ 0.11.2.
Enabling Tracing
To get started automatically sending traces you can:
- Set a uniform sample rate for all transactions, by setting the
traces_sample_rate
option in your SDK config to a number between0
and1
. (For example, to send 20% of transactions, settraces_sample_rate
to0.2
.) - Control the sample rate dynamically, based on the transaction itself and the context in which it's captured, by providing a function to the
traces_sampler
config option.
import sentry_sdk
def traces_sampler(sampling_context):
# ...
# return a number between 0 and 1 or a boolean
sentry_sdk.init(
dsn="https://examplePublicKey@o0.ingest.sentry.io/0",
# To set a uniform sample rate
traces_sample_rate=0.2,
# Alternatively, to control sampling dynamically
traces_sampler=traces_sampler
)
If either of these options is set, tracing will be enabled in your app, and transactions will start getting captured automatically. (The two options are meant to be mutually exclusive, but if you do set both, traces_sampler
will take precedence.)
As you're getting tracing set up, we recommend setting traces_sample_rate
to 1
, so all created transactions are sent to Sentry. Once you're done with testing, though, you'll probably want to consider either lowering your traces_sample_rate
value, or switching to traces_sampler
, which will allow you to set the sample rate individually for each transaction. Without sampling, automatically-captured transactions can add up quickly. (The Flask integration, for example, will send a transaction for every request made to the server.) Sampling allows you to send representative data without overwhelming either your system or your Sentry transaction quota.
You can learn more about the traces_sample_rate
and traces_sampler
options in Sampling Transactions.
Manual Instrumentation
To manually instrument certain regions of your code, you can create a transaction to capture them.
The following example creates a transaction for a scope that contains an expensive operation (for example, process_item
), and sends the result to Sentry:
from sentry_sdk import start_transaction
while True:
item = get_from_queue()
with start_transaction(op="task", name=item.get_transaction_name()):
# process_item may create more spans internally (see next examples)
process_item(item)
Adding More Spans to the Transaction
The next example contains the implementation of the hypothetical process_item
function called from the code snippet in the previous section. Our SDK can determine if there is currently an open transaction and add all newly created spans as child operations to that transaction. Keep in mind that each individual span also needs to be manually finished; otherwise, spans will not show up in the transaction. When using spans and transactions as context managers, they are automatically finished at the end of the with
block.
In cases where you want to attach Spans to an already ongoing Transaction you can use Hub.current.scope.transaction
. This property will return a Transaction
in case there is a running Transaction otherwise it returns None
.
Alternatively, instead of adding to the top-level transaction, you can make a child span of the current span, if there is one. Use Hub.current.scope.span
in that case.
You can choose the value of op
and description
.
from sentry_sdk import Hub
def process_item(item):
transaction = Hub.current.scope.transaction
# omitted code...
with transaction.start_child(op="http", description="GET /") as span:
response = my_custom_http_library.request("GET", "/")
span.set_tag("http.status_code", response.status_code)
span.set_data("http.foobarsessionid", get_foobar_sessionid())
The alternative to make a tree of spans:
from sentry_sdk import Hub
def process_item(item):
parent_span = Hub.current.scope.span
# omitted code...
with parent_span.start_child(op="http", description="GET /") as span:
response = my_custom_http_library.request("GET", "/")
span.set_tag("http.status_code", response.status_code)
span.set_data("http.foobarsessionid", get_foobar_sessionid())
Retrieving a Transaction
In cases where you want to attach Spans to an already ongoing Transaction you can use Hub.current.scope.transaction
. This property will return a Transaction
in case there is a running Transaction otherwise it returns None
.
from sentry_sdk import Hub
transaction = Hub.current.scope.transaction
if transaction is None:
with start_transaction(name="task"):
do_task()
else:
transaction.name = "new name"
with transaction.start_child(op="task"):
do_task()
Retrieving the Current Span
Started spans are stored in the scope, and can be fetched off the scope:
from sentry_sdk import Hub
span = Hub.current.scope.span
if span is None:
# no span in progress, create new transaction
with start_transaction(name="task"):
do_task()
else:
# new task span as child of current span
with span.start_child(op="task"):
do_task()
Adding Query Information and Parameters to Spans
Currently, every tag has a maximum character limit of 200 characters. Tags over the 200 character limit will become truncated, losing potentially important information. To retain this data, you can split data over several tags instead.
For example, a 200+ character tagged request:
https://empowerplant.io/api/0/projects/ep/setup_form/?user_id=314159265358979323846264338327&tracking_id=EasyAsABC123OrSimpleAsDoReMi&product_name=PlantToHumanTranslator&product_id=161803398874989484820458683436563811772030917980576
The 200+ character request above will become truncated to:
https://empowerplant.io/api/0/projects/ep/setup_form/?user_id=314159265358979323846264338327&tracking_id=EasyAsABC123OrSimpleAsDoReMi&product_name=PlantToHumanTranslator&product_id=1618033988749894848
Instead, using span.set_tag
and span.set_data
preserves the details of this query using structured metadata. This could be done over base_url
, endpoint
, and parameters
:
import sentry_sdk
# ...
base_url = "https://empowerplant.io"
endpoint = "/api/0/projects/ep/setup_form"
parameters = {
"user_id": 314159265358979323846264338327,
"tracking_id": "EasyAsABC123OrSimpleAsDoReMi",
"product_name": PlantToHumanTranslator,
"product_id": 161803398874989484820458683436563811772030917980576,
}
with sentry_sdk.start_span(op="request", transaction="setup form") as span:
span.set_tag("base_url", base_url)
span.set_tag("endpoint", endpoint)
span.set_data("parameters", parameters)
make_request(
"{base_url}/{endpoint}/".format(
base_url=base_url,
endpoint=endpoint,
),
data=parameters
)
# ...
- Package:
- pypi:sentry-sdk
- Version:
- 0.19.5
- Repository:
- https://github.com/getsentry/sentry-python