#![cfg_attr(usdt_need_asm, feature(asm))]
#![cfg_attr(all(target_os = "macos", usdt_need_asm_sym), feature(asm_sym))]
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use slog::{Drain, KV};
pub type JsonMap = serde_json::Map<String, serde_json::Value>;
#[usdt::provider(provider = "slog", probe_format = "{probe}_")]
mod probes {
use crate::Message;
fn trace(msg: &Message) {}
fn debug(msg: &Message) {}
fn info(msg: &Message) {}
fn warn(msg: &Message) {}
fn error(msg: &Message) {}
fn critical(msg: &Message) {}
}
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)]
pub struct Location {
pub module: String,
pub file: String,
pub line: u32,
}
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)]
pub struct Message {
pub location: Location,
pub level: String,
pub timestamp: DateTime<Utc>,
pub message: String,
pub kv: JsonMap,
}
#[allow(clippy::large_enum_variant)]
#[derive(Debug)]
pub enum ProbeRegistration {
Success,
Failed(usdt::Error),
}
impl ProbeRegistration {
pub fn is_success(&self) -> bool {
matches!(self, ProbeRegistration::Success)
}
}
#[derive(Debug)]
pub struct Dtrace<D> {
_phantom: std::marker::PhantomData<D>,
}
impl Dtrace<slog::Discard> {
pub fn new() -> (Self, ProbeRegistration) {
let registration = match usdt::register_probes() {
Ok(_) => ProbeRegistration::Success,
Err(e) => ProbeRegistration::Failed(e),
};
(
Self {
_phantom: std::marker::PhantomData,
},
registration,
)
}
}
pub fn with_drain<D>(drain: D) -> (slog::Duplicate<D, Dtrace<slog::Discard>>, ProbeRegistration)
where
D: Drain,
{
let (d, registration) = Dtrace::new();
(slog::Duplicate(drain, d), registration)
}
fn create_dtrace_message(record: &slog::Record, values: &slog::OwnedKVList) -> Message {
let location = Location {
module: record.module().to_string(),
file: record.file().to_string(),
line: record.line(),
};
let mut serializer = Serializer::default();
let kv = match record
.kv()
.serialize(record, &mut serializer)
.and_then(|_| values.serialize(record, &mut serializer))
{
Ok(()) => serializer.map,
Err(e) => {
let mut map = JsonMap::default();
let _ = map.insert(
String::from("err"),
serde_json::Value::from(format!("{}", e)),
);
map
}
};
let msg = Message {
location,
timestamp: Utc::now(),
level: record.level().as_str().to_string(),
message: record.msg().to_string(),
kv,
};
msg
}
impl<D> Drain for Dtrace<D>
where
D: Drain<Ok = (), Err = slog::Never>,
{
type Ok = ();
type Err = slog::Never;
fn log(
&self,
record: &slog::Record<'_>,
values: &slog::OwnedKVList,
) -> Result<Self::Ok, Self::Err> {
match record.level() {
slog::Level::Trace => probes::trace_!(|| create_dtrace_message(record, values)),
slog::Level::Debug => probes::debug_!(|| create_dtrace_message(record, values)),
slog::Level::Info => probes::info_!(|| create_dtrace_message(record, values)),
slog::Level::Warning => probes::warn_!(|| create_dtrace_message(record, values)),
slog::Level::Error => probes::error_!(|| create_dtrace_message(record, values)),
slog::Level::Critical => probes::critical_!(|| create_dtrace_message(record, values)),
}
Ok(())
}
}
#[derive(Debug, Clone, Default)]
struct Serializer {
map: crate::JsonMap,
}
impl Serializer {
fn emit<T>(&mut self, key: slog::Key, value: T) -> slog::Result
where
T: Into<serde_json::Value>,
{
self.map.insert(key.to_string(), value.into());
Ok(())
}
}
macro_rules! impl_emit {
($method:ident, $ty:ty) => {
fn $method(&mut self, key: slog::Key, value: $ty) -> slog::Result {
self.emit(key, value).unwrap();
Ok(())
}
};
}
impl slog::Serializer for Serializer {
fn emit_arguments(&mut self, key: slog::Key, values: &std::fmt::Arguments<'_>) -> slog::Result {
self.map
.insert(key.to_string(), format!("{}", values).into());
Ok(())
}
impl_emit!(emit_u8, u8);
impl_emit!(emit_u16, u16);
impl_emit!(emit_u32, u32);
impl_emit!(emit_u64, u64);
impl_emit!(emit_i8, i8);
impl_emit!(emit_i16, i16);
impl_emit!(emit_i32, i32);
impl_emit!(emit_i64, i64);
impl_emit!(emit_isize, isize);
impl_emit!(emit_usize, usize);
impl_emit!(emit_bool, bool);
impl_emit!(emit_f32, f32);
impl_emit!(emit_f64, f64);
impl_emit!(emit_str, &str);
fn emit_unit(&mut self, key: slog::Key) -> slog::Result {
self.emit(key, ())
}
fn emit_none(&mut self, key: slog::Key) -> slog::Result {
self.map.insert(key.to_string(), serde_json::Value::Null);
Ok(())
}
}