use bitflags::bitflags;
use sentry_core::Breadcrumb;
use tracing_core::{span as tracing_span, Event, Level, Metadata, Subscriber};
use tracing_subscriber::layer::{Context, Layer};
use tracing_subscriber::registry::LookupSpan;
mod breadcrumb;
mod error;
#[cfg(feature = "logs")]
mod log;
mod span;
mod span_guard_stack;
pub use breadcrumb::BreadcrumbLayer;
pub use error::ErrorLayer;
#[cfg(feature = "logs")]
pub use log::LogLayer;
pub(super) use span::SentrySpanData;
pub use span::SpanLayer;
#[expect(deprecated)]
mod event_filter {
use super::*;
bitflags! {
#[deprecated = "only used for the legacy combined layer"]
#[derive(Debug, Clone, Copy)]
pub struct EventFilter: u32 {
const Ignore = 0b000;
const Breadcrumb = 0b001;
const Event = 0b010;
const Log = 0b100;
}
}
}
#[expect(deprecated)]
pub use event_filter::EventFilter;
#[deprecated = "only used for the legacy combined layer"]
#[derive(Debug)]
#[non_exhaustive]
pub enum EventMapping {
Ignore,
Breadcrumb(Breadcrumb),
Event(Box<sentry_core::protocol::Event<'static>>),
#[cfg(feature = "logs")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "logs")))]
Log(sentry_core::protocol::Log),
#[expect(deprecated)]
Combined(CombinedEventMapping),
}
#[deprecated = "only used for the legacy combined layer"]
#[derive(Debug)]
pub struct CombinedEventMapping(#[expect(deprecated)] Vec<EventMapping>);
#[expect(deprecated)]
impl From<EventMapping> for CombinedEventMapping {
fn from(value: EventMapping) -> Self {
match value {
EventMapping::Combined(combined) => combined,
_ => CombinedEventMapping(vec![value]),
}
}
}
#[expect(deprecated)]
impl From<Vec<EventMapping>> for CombinedEventMapping {
fn from(value: Vec<EventMapping>) -> Self {
Self(value)
}
}
#[deprecated = "only used for the legacy combined layer"]
#[expect(deprecated)]
pub fn default_event_filter(metadata: &Metadata) -> EventFilter {
match metadata.level() {
#[cfg(feature = "logs")]
&Level::ERROR => EventFilter::Event | EventFilter::Log,
#[cfg(not(feature = "logs"))]
&Level::ERROR => EventFilter::Event,
#[cfg(feature = "logs")]
&Level::WARN | &Level::INFO => EventFilter::Breadcrumb | EventFilter::Log,
#[cfg(not(feature = "logs"))]
&Level::WARN | &Level::INFO => EventFilter::Breadcrumb,
&Level::DEBUG | &Level::TRACE => EventFilter::Ignore,
}
}
#[deprecated = "only used for the legacy combined layer"]
pub fn default_span_filter(metadata: &Metadata) -> bool {
matches!(
metadata.level(),
&Level::ERROR | &Level::WARN | &Level::INFO
)
}
#[expect(deprecated)]
type EventMapper<S> = Box<dyn Fn(&Event, Context<'_, S>) -> EventMapping + Send + Sync>;
#[deprecated(
note = "Prefer SpanLayer::new() and LogLayer::new(); see SentryLayer docs for migration"
)]
pub struct SentryLayer<S> {
#[expect(deprecated)]
event_filter: Box<dyn Fn(&Metadata) -> EventFilter + Send + Sync>,
event_mapper: Option<EventMapper<S>>,
span_filter: Box<dyn Fn(&Metadata) -> bool + Send + Sync>,
span_layer: SpanLayer,
breadcrumb: BreadcrumbLayer,
error: ErrorLayer,
#[cfg(feature = "logs")]
log: LogLayer,
}
#[expect(deprecated, reason = "implementing the legacy layer")]
impl<S> SentryLayer<S> {
#[must_use]
pub fn event_filter<F>(mut self, filter: F) -> Self
where
F: Fn(&Metadata) -> EventFilter + Send + Sync + 'static,
{
self.event_filter = Box::new(filter);
self
}
#[must_use]
pub fn event_mapper<F>(mut self, mapper: F) -> Self
where
F: Fn(&Event, Context<'_, S>) -> EventMapping + Send + Sync + 'static,
{
self.event_mapper = Some(Box::new(mapper));
self
}
#[must_use]
pub fn span_filter<F>(mut self, filter: F) -> Self
where
F: Fn(&Metadata) -> bool + Send + Sync + 'static,
{
self.span_filter = Box::new(filter);
self
}
#[must_use]
pub fn enable_span_attributes(mut self) -> Self {
self.breadcrumb = self.breadcrumb.enable_span_attributes();
self.error = self.error.enable_span_attributes();
#[cfg(feature = "logs")]
{
self.log = self.log.enable_span_attributes();
}
self
}
}
#[expect(deprecated, reason = "implementing the legacy layer")]
impl<S> Default for SentryLayer<S>
where
S: Subscriber + for<'a> LookupSpan<'a>,
{
fn default() -> Self {
Self {
event_filter: Box::new(default_event_filter),
event_mapper: None,
span_filter: Box::new(default_span_filter),
span_layer: SpanLayer::new(),
breadcrumb: BreadcrumbLayer::new(),
error: ErrorLayer::new(),
#[cfg(feature = "logs")]
log: LogLayer::new(),
}
}
}
#[expect(deprecated, reason = "implementing the legacy layer")]
impl<S> Layer<S> for SentryLayer<S>
where
S: Subscriber + for<'a> LookupSpan<'a>,
{
fn on_event(&self, event: &Event, ctx: Context<'_, S>) {
if let Some(mapper) = &self.event_mapper {
let items = CombinedEventMapping::from(mapper(event, ctx));
for item in items.0 {
match item {
EventMapping::Ignore => (),
EventMapping::Breadcrumb(breadcrumb) => self.breadcrumb.capture(breadcrumb),
EventMapping::Event(event) => self.error.capture(*event),
#[cfg(feature = "logs")]
EventMapping::Log(log) => self.log.capture(log),
EventMapping::Combined(_) => sentry_core::sentry_debug!(
"[SentryLayer] found nested CombinedEventMapping, ignoring"
),
}
}
} else {
let filter = (self.event_filter)(event.metadata());
if filter.contains(EventFilter::Breadcrumb) {
self.breadcrumb.capture_tracing_event(event, ctx.clone());
}
if filter.contains(EventFilter::Event) {
self.error.capture_tracing_event(event, ctx.clone());
}
#[cfg(feature = "logs")]
if filter.contains(EventFilter::Log) {
self.log.capture_tracing_event(event, ctx);
}
}
}
fn on_new_span(
&self,
attrs: &tracing_span::Attributes<'_>,
id: &tracing_span::Id,
ctx: Context<'_, S>,
) {
if (self.span_filter)(attrs.metadata()) {
self.span_layer.on_new_span(attrs, id, ctx);
}
}
fn on_enter(&self, id: &tracing_span::Id, ctx: Context<'_, S>) {
self.span_layer.on_enter(id, ctx);
}
fn on_exit(&self, id: &tracing_span::Id, ctx: Context<'_, S>) {
self.span_layer.on_exit(id, ctx);
}
fn on_close(&self, id: tracing_span::Id, ctx: Context<'_, S>) {
self.span_layer.on_close(id, ctx);
}
fn on_record(
&self,
id: &tracing_span::Id,
values: &tracing_span::Record<'_>,
ctx: Context<'_, S>,
) {
self.span_layer.on_record(id, values, ctx);
}
}
#[deprecated(
note = "Prefer SpanLayer::new() and LogLayer::new(); see SentryLayer docs for migration"
)]
#[expect(deprecated, reason = "returning the legacy layer")]
pub fn layer<S>() -> SentryLayer<S>
where
S: Subscriber + for<'a> LookupSpan<'a>,
{
Default::default()
}