Custom Trace Propagation
Distributed tracing works out of the box for supported frameworks and when tracing is enabled. If you're using an unsupported framework or don't want to turn on tracing, you can set up custom instrumentation for distributed tracing.
This page describes how to manually propagate trace information into and out of your Python application. All you have to do is to make sure your application extracts incoming headers and to set those headers again when making an outgoing request within your application.
This page covers both transaction mode (default) and stream mode. See Streamed Spans to learn more.
Incoming tracing information has to be extracted and stored in memory for later use. Sentry provides the continue_trace() function to help you with this. Incoming tracing information can come from different places:
- In a web environment, it's sent with HTTP headers, for example, by another Sentry SDK used in your frontend project.
- In a job queue, like Celery, it can be retrieved from meta or header variables.
- You also can pick up tracing information from environment variables.
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. The next span created with sentry_sdk.traces.start_span() picks it up automatically.
Here's an example of how to extract and store incoming tracing information using continue_trace():
import sentry_sdk
from my_project import get_incoming_headers_as_dict
headers = get_incoming_headers_as_dict()
transaction = sentry_sdk.continue_trace(headers)
with sentry_sdk.start_transaction(transaction):
...
import sentry_sdk
from my_project import get_incoming_headers_as_dict
headers = get_incoming_headers_as_dict()
transaction = sentry_sdk.continue_trace(headers)
with sentry_sdk.start_transaction(transaction):
...
import sentry_sdk
from my_project import get_incoming_headers_as_dict
headers = get_incoming_headers_as_dict()
sentry_sdk.traces.continue_trace(headers)
with sentry_sdk.traces.start_span(name="handle request"):
...
In these examples, get_incoming_headers_as_dict() returns a dictionary that contains tracing information from HTTP headers, environment variables, or any other mechanism your project uses to communicate with the outside world.
This step only applies to stream mode and is not available in transaction mode.
In stream mode, if you need to start a completely new trace unconnected to the current one, use sentry_sdk.traces.new_trace(). This is useful for background jobs or scheduled tasks where you want a clean trace boundary:
import sentry_sdk
with sentry_sdk.traces.start_span(name="span in trace 1"):
...
sentry_sdk.traces.new_trace()
with sentry_sdk.traces.start_span(name="span in trace 2"):
# This span is the root of a new, separate trace
...
import sentry_sdk
with sentry_sdk.traces.start_span(name="span in trace 1"):
...
sentry_sdk.traces.new_trace()
with sentry_sdk.traces.start_span(name="span in trace 2"):
# This span is the root of a new, separate trace
...
For distributed tracing to work, the two headers sentry-trace and baggage, must be added to outgoing requests. If you pregenerate HTML on the server-side, you might want to take a look at Inject Tracing Information into Rendered HTML, which describes how to pass on tracing information through HTML meta tags.
If you are sending outgoing HTTP requests with Requests, AIOHTTP, the low level http.client, or httplib on Python 2, this tracing information is automatically added to outgoing requests.
If you're using none of the above, you can generate this tracing information with the Sentry SDK's get_traceparent() and get_baggage() functions. Here's an example:
import sentry_sdk
from my_project import make_an_outgoing_request
headers = {}
headers["sentry-trace"] = sentry_sdk.get_traceparent()
headers["baggage"] = sentry_sdk.get_baggage()
make_an_outgoing_request(to="https://example.com", headers=headers)
import sentry_sdk
from my_project import make_an_outgoing_request
headers = {}
headers["sentry-trace"] = sentry_sdk.get_traceparent()
headers["baggage"] = sentry_sdk.get_baggage()
make_an_outgoing_request(to="https://example.com", headers=headers)
In this example, tracing information is propagated to the project running at https://example.com. If this project uses the Sentry Python SDK, it will extract and save the tracing information for later use.
The two services are now connected with your custom distributed tracing implementation.
To propagate tracing information into JavaScript running in rendered HTML, you have to inject HTML meta tags for sentry-trace and baggage data into your rendered HTML. Here's an example:
import sentry_sdk
from my_project import render
meta = ""
meta += '<meta name="sentry-trace" content="%s">' % sentry_sdk.get_traceparent()
meta += '<meta name="baggage" content="%s">' % sentry_sdk.get_baggage()
html = """
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
{additional_meta}
</head>
<body>
<p>This is a website.</p>
</body>
</html>
""".format(additional_meta=meta)
render(html)
import sentry_sdk
from my_project import render
meta = ""
meta += '<meta name="sentry-trace" content="%s">' % sentry_sdk.get_traceparent()
meta += '<meta name="baggage" content="%s">' % sentry_sdk.get_baggage()
html = """
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
{additional_meta}
</head>
<body>
<p>This is a website.</p>
</body>
</html>
""".format(additional_meta=meta)
render(html)
If you make outgoing requests from your project to other services, check if the headers sentry-trace and baggage are present in the request. If so, distributed tracing is working.
Our documentation is open source and available on GitHub. Your contributions are welcome, whether fixing a typo (drat!) or suggesting an update ("yeah, this would be better").