use serde_json::{Map, Value};
use tracing::field::{Field, Visit};
use tracing::{Event, Subscriber};
use tracing_subscriber::layer::{Context, Layer};
use crate::event::{Breadcrumb, Event as ErrEvent};
use crate::level::Level;
fn from_tracing_level(level: &tracing::Level) -> Level {
match *level {
tracing::Level::ERROR => Level::Error,
tracing::Level::WARN => Level::Warning,
tracing::Level::INFO => Level::Info,
tracing::Level::DEBUG => Level::Debug,
tracing::Level::TRACE => Level::Debug,
}
}
pub struct ErrsightLayer {
event_level: Level,
breadcrumb_level: Level,
}
impl ErrsightLayer {
pub fn new() -> Self {
ErrsightLayer {
event_level: Level::Error,
breadcrumb_level: Level::Info,
}
}
pub fn event_level(mut self, level: Level) -> Self {
self.event_level = level;
self
}
pub fn breadcrumb_level(mut self, level: Level) -> Self {
self.breadcrumb_level = level;
self
}
}
impl Default for ErrsightLayer {
fn default() -> Self {
Self::new()
}
}
impl<S: Subscriber> Layer<S> for ErrsightLayer {
fn on_event(&self, event: &Event<'_>, _ctx: Context<'_, S>) {
let metadata = event.metadata();
let level = from_tracing_level(metadata.level());
if level < self.breadcrumb_level {
return;
}
let mut visitor = FieldVisitor::default();
event.record(&mut visitor);
let message = visitor
.message
.unwrap_or_else(|| metadata.target().to_string());
if level >= self.event_level {
let mut ev = ErrEvent::new(level, message);
ev.set_metadata("logger.target", metadata.target());
if let (Some(file), Some(line)) = (metadata.file(), metadata.line()) {
ev.set_metadata("logger.location", format!("{file}:{line}"));
}
if !visitor.fields.is_empty() {
ev.set_metadata("fields", Value::Object(visitor.fields));
}
crate::capture_event(ev);
} else {
let mut crumb = Breadcrumb::new(metadata.target(), message).level(level);
for (k, v) in visitor.fields {
crumb = crumb.data(k, v);
}
crate::add_breadcrumb(crumb);
}
}
}
#[derive(Default)]
struct FieldVisitor {
message: Option<String>,
fields: Map<String, Value>,
}
impl FieldVisitor {
fn insert(&mut self, field: &Field, value: Value) {
if field.name() == "message" {
if let Value::String(s) = &value {
self.message = Some(s.clone());
return;
}
}
self.fields.insert(field.name().to_string(), value);
}
}
impl Visit for FieldVisitor {
fn record_str(&mut self, field: &Field, value: &str) {
self.insert(field, Value::String(value.to_string()));
}
fn record_i64(&mut self, field: &Field, value: i64) {
self.insert(field, Value::from(value));
}
fn record_u64(&mut self, field: &Field, value: u64) {
self.insert(field, Value::from(value));
}
fn record_f64(&mut self, field: &Field, value: f64) {
self.insert(field, Value::from(value));
}
fn record_bool(&mut self, field: &Field, value: bool) {
self.insert(field, Value::Bool(value));
}
fn record_error(&mut self, field: &Field, value: &(dyn std::error::Error + 'static)) {
self.insert(field, Value::String(value.to_string()));
}
fn record_debug(&mut self, field: &Field, value: &dyn std::fmt::Debug) {
if field.name() == "message" {
self.message = Some(format!("{value:?}"));
} else {
self.insert(field, Value::String(format!("{value:?}")));
}
}
}