use sentry_tracing::{EventMapping, SentryLayer};
use std::error::Error;
use std::fmt::Debug;
use std::str::FromStr;
use strut_core::ALERT_FIELD_NAME;
use tracing::field::Field;
use tracing::{Event, Subscriber};
use tracing_subscriber::field::Visit;
use tracing_subscriber::layer::Context;
use tracing_subscriber::registry::LookupSpan;
pub fn make_layer<S>() -> SentryLayer<S>
where
S: Subscriber + for<'a> LookupSpan<'a>,
{
sentry_tracing::layer().event_mapper(field_walker)
}
fn field_walker<S>(event: &Event, ctx: Context<'_, S>) -> EventMapping
where
S: Subscriber + for<'a> LookupSpan<'a>,
{
let mut sentry_behavior: SentryBehaviorVisitor = SentryBehaviorVisitor { event: None };
event.record(&mut sentry_behavior);
match sentry_behavior.event {
None => EventMapping::Ignore,
Some(SentryEventKind::Event) => {
EventMapping::Event(sentry_tracing::event_from_event(event, &ctx))
}
Some(SentryEventKind::Breadcrumb) => {
EventMapping::Breadcrumb(sentry_tracing::breadcrumb_from_event(event, &ctx))
}
}
}
struct SentryBehaviorVisitor {
event: Option<SentryEventKind>,
}
impl Visit for SentryBehaviorVisitor {
fn record_f64(&mut self, field: &Field, _value: f64) {
self.match_field(field);
}
fn record_i64(&mut self, field: &Field, _value: i64) {
self.match_field(field);
}
fn record_u64(&mut self, field: &Field, _value: u64) {
self.match_field(field);
}
fn record_i128(&mut self, field: &Field, _value: i128) {
self.match_field(field);
}
fn record_u128(&mut self, field: &Field, _value: u128) {
self.match_field(field);
}
fn record_bool(&mut self, field: &Field, _value: bool) {
self.match_field(field);
}
fn record_str(&mut self, field: &Field, value: &str) {
if field.name() == ALERT_FIELD_NAME {
self.event = SentryEventKind::from_str(value).ok();
}
}
fn record_bytes(&mut self, field: &Field, _value: &[u8]) {
self.match_field(field);
}
fn record_error(&mut self, field: &Field, _value: &(dyn Error + 'static)) {
self.match_field(field);
}
fn record_debug(&mut self, field: &Field, _value: &dyn Debug) {
self.match_field(field);
}
}
impl SentryBehaviorVisitor {
#[inline(always)]
fn match_field(&mut self, field: &Field) {
if field.name() == ALERT_FIELD_NAME {
self.event = Some(SentryEventKind::Event);
}
}
}
enum SentryEventKind {
Event,
Breadcrumb,
}
impl FromStr for SentryEventKind {
type Err = ();
fn from_str(value: &str) -> Result<Self, Self::Err> {
match value {
"breadcrumb" | "crumb" => Ok(SentryEventKind::Breadcrumb),
_ => Ok(SentryEventKind::Event),
}
}
}