pub mod export;
pub mod health;
pub mod log;
pub mod metrics;
pub mod trace;
pub use log::format::{JsonFormatter, PrettyFormatter};
pub use log::subscriber::{
set_global_subscriber, set_min_level, LogFormat, LogSubscriber, Subscriber,
};
pub use log::{Event, Level, Value};
pub use trace::context::SpanContext;
pub use trace::span::{Span, SpanGuard};
pub use trace::{SpanId, TraceId};
pub use metrics::counter::Counter;
pub use metrics::gauge::Gauge;
pub use metrics::histogram::Histogram;
pub use metrics::registry::MetricsRegistry;
pub use health::{AsyncHealthCheck, HealthCheck, HealthRegistry, HealthStatus};
pub use export::prometheus::PrometheusExporter;
pub use export::stdout::StdoutExporter;
pub use export::Exporter;
pub mod prelude {
pub use crate::{
Counter, Event, Gauge, HealthCheck, HealthRegistry, HealthStatus, Histogram, Level,
MetricsRegistry, Span, SpanContext, SpanGuard, SpanId, Subscriber, TraceId, Value,
};
}
pub fn init_logging() {
let sub = LogSubscriber::from_env();
set_min_level(sub.min_level);
let _ = set_global_subscriber(sub);
}
#[macro_export]
macro_rules! log_event {
($level:expr, $msg:expr $(, $key:ident = $val:expr)* $(,)?) => {{
if ($level as u8) >= $crate::log::subscriber::min_level() as u8 {
let event = $crate::Event::now($level, $msg)
$(.field(stringify!($key), $val))*;
$crate::log::subscriber::dispatch(&event);
}
}};
}
#[macro_export]
macro_rules! trace {
($msg:expr $(, $key:ident = $val:expr)* $(,)?) => {
$crate::log_event!($crate::Level::Trace, $msg $(, $key = $val)*)
};
}
#[macro_export]
macro_rules! debug {
($msg:expr $(, $key:ident = $val:expr)* $(,)?) => {
$crate::log_event!($crate::Level::Debug, $msg $(, $key = $val)*)
};
}
#[macro_export]
macro_rules! info {
($msg:expr $(, $key:ident = $val:expr)* $(,)?) => {
$crate::log_event!($crate::Level::Info, $msg $(, $key = $val)*)
};
}
#[macro_export]
macro_rules! warn {
($msg:expr $(, $key:ident = $val:expr)* $(,)?) => {
$crate::log_event!($crate::Level::Warn, $msg $(, $key = $val)*)
};
}
#[macro_export]
macro_rules! error {
($msg:expr $(, $key:ident = $val:expr)* $(,)?) => {
$crate::log_event!($crate::Level::Error, $msg $(, $key = $val)*)
};
}
#[macro_export]
macro_rules! trace_event {
($msg:expr $(, $key:ident = $val:expr)* $(,)?) => {
$crate::log_event!($crate::Level::Trace, $msg $(, $key = $val)*)
};
}
#[cfg(test)]
mod tests {
use crate::log::subscriber::{min_level, set_min_level, LogFormat, LogSubscriber};
use crate::log::{Event, Level};
fn with_level<F: FnOnce()>(level: Level, f: F) {
let prev = min_level();
set_min_level(level);
f();
set_min_level(prev);
}
#[test]
fn log_event_macro_passes_at_or_above_min_level() {
with_level(Level::Debug, || {
crate::log_event!(Level::Debug, "debug msg");
crate::log_event!(Level::Info, "info msg");
});
}
#[test]
fn trace_macro_compiles() {
with_level(Level::Trace, || {
crate::trace!("trace message");
crate::trace!("trace with field", key = "value");
});
}
#[test]
fn debug_macro_compiles() {
with_level(Level::Debug, || {
crate::debug!("debug message");
crate::debug!("debug with field", count = 42_i32);
});
}
#[test]
fn info_macro_compiles() {
with_level(Level::Info, || {
crate::info!("info message");
crate::info!("info with fields", status = 200_i32, path = "/health");
});
}
#[test]
fn warn_macro_compiles() {
with_level(Level::Warn, || {
crate::warn!("warn message");
});
}
#[test]
fn error_macro_compiles() {
with_level(Level::Error, || {
crate::error!("error message");
crate::error!("error with field", code = 500_i32);
});
}
#[test]
fn trace_event_alias_compiles() {
with_level(Level::Trace, || {
crate::trace_event!("trace alias");
});
}
#[test]
fn log_event_macro_filtered_when_below_min() {
with_level(Level::Error, || {
crate::log_event!(Level::Debug, "should be filtered");
crate::log_event!(Level::Info, "should be filtered");
crate::log_event!(Level::Warn, "should be filtered");
crate::log_event!(Level::Error, "should pass filter");
});
}
#[test]
fn log_subscriber_new_fields_accessible() {
let sub = LogSubscriber::new(Level::Debug, LogFormat::Json);
assert_eq!(sub.min_level, Level::Debug);
assert_eq!(sub.format, LogFormat::Json);
}
#[test]
fn init_logging_is_idempotent() {
crate::init_logging();
crate::init_logging();
}
#[test]
fn json_format_output_is_valid_structure() {
let event = Event::now(Level::Info, "test")
.field("key", "val")
.field("n", 42_i32);
let mut buf = Vec::new();
crate::JsonFormatter::format(&event, &mut buf).unwrap();
let s = String::from_utf8(buf).unwrap();
assert!(s.starts_with('{'));
assert!(s.trim_end().ends_with('}'));
assert!(s.contains("\"level\":\"INFO\""));
assert!(s.contains("\"msg\":\"test\""));
assert!(s.contains("\"key\":\"val\""));
assert!(s.contains("\"n\":42"));
}
#[test]
fn set_min_level_affects_macro_gate() {
with_level(Level::Error, || {
assert_eq!(min_level(), Level::Error);
assert!((Level::Trace as u8) < (min_level() as u8));
});
}
}