sentry_tracing/layer/breadcrumb.rs
1use sentry_core::Breadcrumb;
2use tracing_core::{Event, Subscriber};
3use tracing_subscriber::layer::{Context, Layer};
4use tracing_subscriber::registry::LookupSpan;
5
6use crate::converters::breadcrumb_from_event_impl;
7
8/// Adds tracing events as Sentry breadcrumbs.
9///
10/// Without a filter, events at every level become breadcrumbs. Configure a filter to limit
11/// breadcrumb volume; see [`BreadcrumbLayer::new`] for the recommended levels.
12///
13/// Using this layer alongside the legacy [`SentryLayer`](super::SentryLayer) is unsupported.
14/// Use either the legacy layer or the individual layers, not both.
15#[must_use]
16#[derive(Default)]
17pub struct BreadcrumbLayer {
18 with_span_attributes: bool,
19}
20
21impl BreadcrumbLayer {
22 /// Creates a layer that adds tracing events as Sentry breadcrumbs.
23 ///
24 /// For most users, we would recommend using the [`LogLayer`](super::LogLayer) instead of this
25 /// layer, as logs are sent regardless of whether an error occurs.
26 ///
27 /// Breadcrumbs are only sent attached to Sentry error events. Users using this layer may therefore
28 /// also wish to enable the [`ErrorLayer`](super::ErrorLayer).
29 ///
30 /// # Filtering
31 ///
32 /// Without a filter, events at every level become breadcrumbs. Configure a filter to limit
33 /// breadcrumb volume. We recommend capturing `WARN` and `INFO` events. If you also use the error layer, exclude `ERROR`
34 /// events, which would otherwise duplicate the error event as a breadcrumb. If a custom filter
35 /// sends the same event to both layers, install the breadcrumb layer before the error layer so
36 /// that breadcrumb is included in the error.
37 ///
38 /// ```rust
39 /// # use tracing_subscriber::prelude::*;
40 /// use sentry::integrations::tracing::{BreadcrumbLayer, ErrorLayer};
41 /// use tracing::Level;
42 /// use tracing_subscriber::filter::{filter_fn, LevelFilter};
43 ///
44 /// tracing_subscriber::registry()
45 /// .with(
46 /// BreadcrumbLayer::new().with_filter(filter_fn(|metadata| {
47 /// matches!(*metadata.level(), Level::WARN | Level::INFO)
48 /// })),
49 /// )
50 /// .with(ErrorLayer::new().with_filter(LevelFilter::ERROR))
51 /// .init();
52 ///
53 /// // These will be captured as breadcrumbs ...
54 /// tracing::info!("INFO breadcrumb");
55 /// tracing::warn!("WARN breadcrumb");
56 ///
57 /// // ... but this will not be.
58 /// tracing::debug!("DEBUG breadcrumb");
59 ///
60 /// // This will not be captured as a breadcrumb, but as an error event by the error layer. The
61 /// // breadcrumbs above will be contained in that error event.
62 /// tracing::error!("ERROR event");
63 /// ```
64 pub fn new() -> Self {
65 Self::default()
66 }
67
68 /// Include attributes of parent spans in breadcrumbs.
69 ///
70 /// We can only include spans captured by a [`super::SpanLayer`] on the same subscriber. So,
71 /// this option only has any effect when the `SpanLayer` is installed.
72 ///
73 /// # Filtering
74 ///
75 /// Parent-span attributes are included only when the span is accepted by both `SpanLayer`
76 /// and `BreadcrumbLayer`. A filter that only accepts `WARN` and `INFO` metadata therefore
77 /// omits attributes from spans at other levels.
78 ///
79 /// To include attributes from all spans captured by `SpanLayer`, we recommend accepting all
80 /// spans in this layer's filter, while filtering events to the desired levels, like so:
81 ///
82 ///
83 pub fn enable_span_attributes(mut self) -> Self {
84 self.with_span_attributes = true;
85 self
86 }
87
88 /// Add a breadcrumb to the current `Hub`'s scope.
89 pub(super) fn capture(&self, breadcrumb: Breadcrumb) {
90 sentry_core::add_breadcrumb(breadcrumb);
91 }
92}
93
94impl BreadcrumbLayer {
95 /// Capture a tracing event as a breadcrumb.
96 pub(super) fn capture_tracing_event<S>(&self, event: &Event, ctx: Context<'_, S>)
97 where
98 S: Subscriber + for<'a> LookupSpan<'a>,
99 {
100 self.capture(breadcrumb_from_event_impl(
101 event,
102 self.with_span_attributes.then_some(&ctx),
103 ));
104 }
105}
106
107impl<S> Layer<S> for BreadcrumbLayer
108where
109 S: Subscriber + for<'a> LookupSpan<'a>,
110{
111 fn on_event(&self, event: &Event, ctx: Context<'_, S>) {
112 self.capture_tracing_event(event, ctx);
113 }
114}