sentry_tracing/
lib.rs

1//! Support for automatic breadcrumb, event, and trace capturing from `tracing` events and spans.
2//!
3//! The `tracing` crate is supported in four ways:
4//! - `tracing` events can be captured as Sentry events. These are grouped and show up in the Sentry
5//!   [issues](https://docs.sentry.io/product/issues/) page, representing high severity issues to be
6//!   acted upon.
7//! - `tracing` events can be captured as [breadcrumbs](https://docs.sentry.io/product/issues/issue-details/breadcrumbs/).
8//!   Breadcrumbs create a trail of what happened prior to an event, and are therefore sent only when
9//!   an event is captured, either manually through e.g. `sentry::capture_message` or through integrations
10//!   (e.g. the panic integration is enabled (default) and a panic happens).
11//! - `tracing` events can be captured as traditional [structured logs](https://docs.sentry.io/product/explore/logs/).
12//!   The `tracing` fields are captured as attributes on the logs, which can be queried in the Logs
13//!   explorer. (Available on crate feature `logs`)
14//! - `tracing` spans can be captured as Sentry spans. These can be used to provide more contextual
15//!   information for errors, diagnose [performance
16//!   issues](https://docs.sentry.io/product/insights/overview/), and capture additional attributes to
17//!   aggregate and compute [metrics](https://docs.sentry.io/product/explore/trace-explorer/).
18//!
19//! By default, events above `Info` are recorded as breadcrumbs, events above `Error` are captured
20//! as error events, and spans above `Info` are recorded as spans.
21//!
22//! # Configuration
23//!
24//! To fully enable the tracing integration, set the traces sample rate and add a layer to the
25//! tracing subscriber:
26//!
27//! ```
28//! use tracing_subscriber::prelude::*;
29//!
30//! let _guard = sentry::init(sentry::ClientOptions {
31//!     // Enable capturing of traces; set this a to lower value in production:
32//!     traces_sample_rate: 1.0,
33//!     ..sentry::ClientOptions::default()
34//! });
35//!
36//! // Register the Sentry tracing layer to capture breadcrumbs, events, and spans:
37//! tracing_subscriber::registry()
38//!     .with(tracing_subscriber::fmt::layer())
39//!     .with(sentry::integrations::tracing::layer())
40//!     .init();
41//! ```
42//!
43//! You can customize the behavior of the layer by providing an explicit event filter, to customize which events
44//! are captured by Sentry and the data type they are mapped to.
45//! Similarly, you can provide a span filter to customize which spans are captured by Sentry.
46//!
47//! ```
48//! use sentry::integrations::tracing::EventFilter;
49//! use tracing_subscriber::prelude::*;
50//!
51//! let sentry_layer = sentry::integrations::tracing::layer()
52//!     .event_filter(|md| match *md.level() {
53//!         tracing::Level::ERROR => EventFilter::Event,
54//!         _ => EventFilter::Ignore,
55//!     })
56//!     .span_filter(|md| matches!(*md.level(), tracing::Level::ERROR | tracing::Level::WARN));
57//!
58//! tracing_subscriber::registry()
59//!     .with(tracing_subscriber::fmt::layer())
60//!     .with(sentry_layer)
61//!     .init();
62//! ```
63//!
64//! In addition, a custom event mapper can be provided, to fully customize if and how `tracing` events are converted to Sentry data.
65//!
66//! Note that if both an event mapper and event filter are set, the mapper takes precedence, thus the
67//! filter has no effect.
68//!
69//! # Capturing breadcrumbs
70//!
71//! Tracing events automatically create breadcrumbs that are attached to the current scope in
72//! Sentry. They show up on errors and transactions captured within this scope as shown in the
73//! examples below.
74//!
75//! Fields passed to the event macro are automatically tracked as structured data in Sentry. For
76//! breadcrumbs, they are shown directly with the breadcrumb message. For other types of data, read
77//! below.
78//!
79//! ```
80//! for i in 0..10 {
81//!     tracing::debug!(number = i, "Generates a breadcrumb");
82//! }
83//! ```
84//!
85//! # Capturing logs
86//!
87//! Tracing events can be captured as traditional structured logs in Sentry.
88//! This is gated by the `logs` feature flag and requires setting up a custom Event filter/mapper
89//! to capture logs. You also need to pass `enable_logs: true` in your `sentry::init` call.
90//!
91//! ```
92//! // assuming `tracing::Level::INFO => EventFilter::Log` in your `event_filter`
93//! for i in 0..10 {
94//!     tracing::info!(number = i, my.key = "val", my.num = 42, "This is a log");
95//! }
96//! ```
97//!
98//! The fields of a `tracing` event are captured as attributes of the log.
99//! Logs can be viewed and queried in the Logs explorer based on message and attributes.
100//! Fields containing dots will be displayed as nested under their common prefix in the UI.
101//!
102//! # Tracking Errors
103//!
104//! The easiest way to emit errors is by logging an event with `ERROR` level. This will create a
105//! grouped issue in Sentry. To add custom information, prepend the message with fields. It is also
106//! possible to add Sentry tags if a field is prefixed with `"tags."`
107//!
108//! ```
109//! tracing::error!(
110//!     field = "value",                  // will become a context field
111//!     tags.custom = "value",            // will become a tag in Sentry
112//!     "this is an error with a custom tag",
113//! );
114//! ```
115//!
116//! To track [error structs](std::error::Error), assign a reference to error trait object as field
117//! in one of the logging macros. By convention, it is recommended to use the `ERROR` level and
118//! assign it to a field called `error`, although the integration will also work with all other
119//! levels and field names.
120//!
121//! All other fields passed to the macro are captured in a custom "Tracing Fields" context in
122//! Sentry.
123//!
124//! ```
125//! use std::error::Error;
126//! use std::io;
127//!
128//! let custom_error = io::Error::new(io::ErrorKind::Other, "oh no");
129//! tracing::error!(error = &custom_error as &dyn Error);
130//! ```
131//!
132//! It is also possible to combine error messages with error structs. In Sentry, this creates issues
133//! grouped by the message and location of the error log, and adds the passed error as nested
134//! source.
135//!
136//! ```
137//! use std::error::Error;
138//! use std::io;
139//!
140//! let custom_error = io::Error::new(io::ErrorKind::Other, "oh no");
141//! tracing::error!(error = &custom_error as &dyn Error, "my operation failed");
142//! ```
143//!
144//! # Sending multiple items to Sentry
145//!
146//! To map a `tracing` event to multiple items in Sentry, you can combine multiple event filters
147//! using the bitwise or operator:
148//!
149//! ```
150//! use sentry::integrations::tracing::EventFilter;
151//! use tracing_subscriber::prelude::*;
152//!
153//! let sentry_layer = sentry::integrations::tracing::layer()
154//!     .event_filter(|md| match *md.level() {
155//!         tracing::Level::ERROR => EventFilter::Event | EventFilter::Log,
156//!         tracing::Level::TRACE => EventFilter::Ignore,
157//!         _ => EventFilter::Log,
158//!     })
159//!     .span_filter(|md| matches!(*md.level(), tracing::Level::ERROR | tracing::Level::WARN));
160//!
161//! tracing_subscriber::registry()
162//!     .with(tracing_subscriber::fmt::layer())
163//!     .with(sentry_layer)
164//!     .init();
165//! ```
166//!
167//! If you're using a custom event mapper instead of an event filter, use `EventMapping::Combined`.
168//!
169//! # Tracing Spans
170//!
171//! The integration automatically tracks `tracing` spans as spans in Sentry. A convenient way to do
172//! this is with the `#[instrument]` attribute macro, which creates a span/transaction for the function
173//! in Sentry.
174//!
175//! Function arguments are added as context fields automatically, which can be configured through
176//! attribute arguments. Refer to documentation of the macro for more information.
177//!
178//! ```
179//! use std::time::Duration;
180//!
181//! use tracing_subscriber::prelude::*;
182//!
183//! // Functions instrumented by tracing automatically
184//! // create spans/transactions around their execution.
185//! #[tracing::instrument]
186//! async fn outer() {
187//!     for i in 0..10 {
188//!         inner(i).await;
189//!     }
190//! }
191//!
192//! // This creates spans inside the outer transaction, unless called directly.
193//! #[tracing::instrument]
194//! async fn inner(i: u32) {
195//!     // Also works, since log events are ingested by the tracing system
196//!     tracing::debug!(number = i, "Generates a breadcrumb");
197//!
198//!     tokio::time::sleep(Duration::from_millis(100)).await;
199//! }
200//! ```
201//!
202//! By default, the name of the span sent to Sentry matches the name of the `tracing` span, which
203//! is the name of the function when using `tracing::instrument`, or the name passed to the
204//! `tracing::<level>_span` macros.
205//!
206//! By default, the `op` of the span sent to Sentry is `default`.
207//!
208//! ## Special Span Fields
209//!
210//! Some fields on spans are treated specially by the Sentry tracing integration:
211//! - `sentry.name`: overrides the span name sent to Sentry.
212//!   This is useful to customize the span name when using `#[tracing::instrument]`, or to update
213//!   it retroactively (using `span.record`) after the span has been created.
214//! - `sentry.op`: overrides the span `op` sent to Sentry.
215//! - `sentry.trace`: in Sentry, the `sentry-trace` header is sent with HTTP requests to achieve distributed tracing.
216//!   If the value of this field is set to the value of a valid `sentry-trace` header, which
217//!   other Sentry SDKs send automatically with outgoing requests, then the SDK will continue the trace using the given distributed tracing information.
218//!   This is useful to achieve distributed tracing at service boundaries by using only the
219//!   `tracing` API.
220//!   Note that `sentry.trace` will only be effective on span creation (it cannot be applied retroactively)
221//!   and requires the span it's applied to to be a root span, i.e. no span should active upon its
222//!   creation.
223//!
224//!
225//! Example:
226//!
227//! ```
228//! #[tracing::instrument(skip_all, fields(
229//!     sentry.name = "GET /payments",
230//!     sentry.op = "http.server",
231//!     sentry.trace = headers.get("sentry-trace").unwrap_or(&"".to_owned()),
232//! ))]
233//! async fn handle_request(headers: std::collections::HashMap<String, String>) {
234//!     // ...
235//! }
236//! ```
237
238#![doc(html_favicon_url = "https://sentry-brand.storage.googleapis.com/favicon.ico")]
239#![doc(html_logo_url = "https://sentry-brand.storage.googleapis.com/sentry-glyph-black.png")]
240#![warn(missing_docs)]
241
242mod converters;
243mod layer;
244
245pub use converters::*;
246pub use layer::*;
247
248const TAGS_PREFIX: &str = "tags.";
249const SENTRY_OP_FIELD: &str = "sentry.op";
250const SENTRY_NAME_FIELD: &str = "sentry.name";
251const SENTRY_TRACE_FIELD: &str = "sentry.trace";