sentry_tracing/layer/error.rs
1use sentry_core::protocol::Event as SentryEvent;
2use tracing_core::{Event, Subscriber};
3use tracing_subscriber::layer::{Context, Layer};
4use tracing_subscriber::registry::LookupSpan;
5
6use crate::converters::event_from_event_impl;
7
8/// Captures tracing events as Sentry error events.
9///
10/// Using this layer alongside the legacy [`SentryLayer`](super::SentryLayer) is unsupported.
11/// Use either the legacy layer or the individual layers, not both.
12#[must_use]
13#[derive(Default)]
14pub struct ErrorLayer {
15 with_span_attributes: bool,
16}
17
18impl ErrorLayer {
19 /// Creates a layer that captures tracing events as Sentry error events.
20 ///
21 /// Most applications should start with [`LogLayer`](super::LogLayer), and add this layer if
22 /// `ERROR` events should also be captured as Sentry error events. Combine it with
23 /// [`BreadcrumbLayer`](super::BreadcrumbLayer) to attach the events leading up to each error.
24 ///
25 /// # Filtering
26 ///
27 /// Configure a level filter to limit error-event volume. We recommend capturing only `ERROR`
28 /// events, as shown below.
29 ///
30 /// <div class="warning">
31 ///
32 /// **Do not use this layer without a filter.** An unfiltered layer captures every tracing
33 /// event, including `DEBUG` and `TRACE`, as a Sentry error event. This can quickly exhaust
34 /// your Sentry error quota.
35 ///
36 /// </div>
37 ///
38 /// ```rust
39 /// # use tracing_subscriber::prelude::*;
40 /// use sentry::integrations::tracing::ErrorLayer;
41 /// use tracing_subscriber::filter::LevelFilter;
42 ///
43 /// tracing_subscriber::registry()
44 /// .with(ErrorLayer::new().with_filter(LevelFilter::ERROR))
45 /// .init();
46 ///
47 /// // This will be captured ...
48 /// tracing::error!("ERROR event");
49 ///
50 /// // ... but this will not be, due to LevelFilter::ERROR being set.
51 /// tracing::warn!("WARN event");
52 /// ```
53 ///
54 /// # Event conversion
55 ///
56 /// With the default converter:
57 ///
58 /// - String, numeric, and boolean fields prefixed with `tags.` become Sentry tags, with the
59 /// prefix removed.
60 /// - Other data fields are stored in the `Rust Tracing Fields` context on the Sentry error event.
61 /// - Record an error as `&dyn std::error::Error` to capture its exception type and source chain.
62 pub fn new() -> Self {
63 Self::default()
64 }
65
66 /// Include attributes of parent spans in error events.
67 ///
68 /// We can only include spans captured by a [`super::SpanLayer`] on the same subscriber. So,
69 /// this option only has any effect when the `SpanLayer` is installed.
70 ///
71 /// # Filtering
72 ///
73 /// Parent-span attributes are included only when the span is accepted by both `SpanLayer`
74 /// and `ErrorLayer`. A level filter such as `LevelFilter::ERROR` therefore omits attributes
75 /// from less severe spans.
76 ///
77 /// To include attributes from all spans captured by `SpanLayer`, we recommend accepting all
78 /// spans in this layer's filter, while filtering events to the desired level, like so:
79 ///
80 /// ```rust
81 /// # use tracing_subscriber::prelude::*;
82 /// use sentry::integrations::tracing::{ErrorLayer, SpanLayer};
83 /// use tracing_subscriber::filter::{filter_fn, LevelFilter, FilterExt as _};
84 ///
85 /// tracing_subscriber::registry()
86 /// .with(SpanLayer::new().with_filter(LevelFilter::INFO))
87 /// .with(
88 /// ErrorLayer::new()
89 /// .enable_span_attributes()
90 /// .with_filter(LevelFilter::ERROR.or(filter_fn(|m| m.is_span()))),
91 /// )
92 /// .init();
93 /// ```
94 ///
95 /// Including all spans in the error layer filter is safe, because this layer does not send
96 /// any spans to Sentry.
97 pub fn enable_span_attributes(mut self) -> Self {
98 self.with_span_attributes = true;
99 self
100 }
101
102 /// Capture an error event on the current `Hub`.
103 pub(super) fn capture(&self, event: SentryEvent<'static>) {
104 sentry_core::capture_event(event);
105 }
106
107 /// Capture a tracing event as a Sentry error event.
108 pub(super) fn capture_tracing_event<S>(&self, event: &Event, ctx: Context<'_, S>)
109 where
110 S: Subscriber + for<'a> LookupSpan<'a>,
111 {
112 let sentry_event = event_from_event_impl(event, self.with_span_attributes.then_some(&ctx));
113 self.capture(sentry_event);
114 }
115}
116
117impl<S> Layer<S> for ErrorLayer
118where
119 S: Subscriber + for<'a> LookupSpan<'a>,
120{
121 fn on_event(&self, event: &Event, ctx: Context<'_, S>) {
122 self.capture_tracing_event(event, ctx);
123 }
124}