use super::define::*;
use super::record::*;
#[allow(unused_variables)]
pub trait Plugin: AnyPlugin + Send + Sync + 'static {
#[inline]
#[must_use]
fn pre(&self, record: &mut Record) -> bool {
true
}
#[inline]
#[must_use]
fn post(&self, record: &mut Record) -> bool {
true
}
}
pub trait AnyPlugin: Any {
fn as_any(&self) -> &dyn Any;
}
impl<T: Any> AnyPlugin for T {
#[inline]
fn as_any(&self) -> &dyn Any {
self
}
}
pub struct LevelPlugin;
impl Plugin for LevelPlugin {
#[inline]
fn pre(&self, record: &mut Record) -> bool {
match level_to_str(record.level()) {
None => record.append("level", &record.level().to_string()),
Some(level) => record.append("level", &level),
};
true
}
}
pub struct TimePlugin {
pub format: chrono::SecondsFormat,
pub utc: bool,
pub z: bool,
}
impl TimePlugin {
pub fn from_secs() -> Self {
Self { format: chrono::SecondsFormat::Secs, utc: false, z: false }
}
pub fn from_millis() -> Self {
Self { format: chrono::SecondsFormat::Millis, utc: false, z: false }
}
pub fn from_micros() -> Self {
Self { format: chrono::SecondsFormat::Micros, utc: false, z: false }
}
pub fn from_nanos() -> Self {
Self { format: chrono::SecondsFormat::Nanos, utc: false, z: false }
}
pub fn use_utc(mut self) -> Self {
self.utc = true;
self
}
pub fn use_z(mut self) -> Self {
self.z = true;
self
}
}
impl Plugin for TimePlugin {
#[inline]
fn pre(&self, record: &mut Record) -> bool {
let now = match self.utc {
true => chrono::Utc::now().to_rfc3339_opts(self.format, self.z),
false => chrono::Local::now().to_rfc3339_opts(self.format, self.z),
};
record.append("time", &now);
true
}
}
pub struct SourcePlugin {
pub level: Level,
}
impl SourcePlugin {
pub fn new() -> Self {
Self { level: LEVEL_TRACE }
}
pub fn level(mut self, level: Level) -> Self {
self.level = level;
self
}
}
impl Default for SourcePlugin {
fn default() -> Self {
Self::new()
}
}
impl Plugin for SourcePlugin {
#[inline]
fn post(&self, record: &mut Record) -> bool {
if record.level() >= self.level {
record.append("src", &format!("{}:{}", record.source().file, record.source().line));
}
true
}
}
#[derive(Debug, Default, Clone)]
pub struct StackFrame {
pub funcname: String,
pub filename: String,
pub lineno: u32,
}
impl Encode for StackFrame {
#[inline]
fn encode(&self, buf: &mut Vec<u8>) {
buf.push(b'{');
"funcname".encode(buf);
buf.push(b':');
self.funcname.encode(buf);
buf.push(b',');
"filename".encode(buf);
buf.push(b':');
self.filename.encode(buf);
buf.push(b',');
"lineno".encode(buf);
buf.push(b':');
self.lineno.encode(buf);
buf.push(b'}');
}
}
pub struct StackPlugin {
pub level: Level,
}
impl StackPlugin {
pub fn from_level(level: Level) -> Self {
Self {level}
}
}
impl Plugin for StackPlugin {
fn post(&self, record: &mut Record) -> bool {
if record.level() != self.level {
return true;
}
match std::env::var("RUST_BACKTRACE") {
Ok(val) if val != "0" => {}
_ => return true,
}
let mut frames = vec![];
backtrace::trace(|frame| {
backtrace::resolve_frame(frame, |symbol| {
if let (Some(funcname), Some(filename), Some(lineno)) = (symbol.name(), symbol.filename(), symbol.lineno()) {
let funcname = funcname.to_string();
let filename = filename.to_string_lossy().to_string();
if filename.starts_with("/rustc/") ||
funcname.starts_with("backtrace::") ||
funcname.starts_with(concat!(env!("CARGO_PKG_NAME"), "::")) ||
funcname.starts_with(concat!("<", env!("CARGO_PKG_NAME"))) {
return;
}
frames.push(StackFrame {funcname, filename, lineno});
}
});
true
});
record.append("stack", &frames);
true
}
}