use console_subscriber::{ConsoleLayer, Server as ConsoleServer};
use std::{
io::{StderrLock, StdoutLock},
net::IpAddr,
};
use tracing_subscriber::{
filter::LevelFilter,
fmt::{
FormatEvent, FormatFields, Layer, MakeWriter,
format::{FmtSpan, Format},
},
layer::{Layer as _, SubscriberExt},
registry::Registry,
util::SubscriberInitExt,
};
type StdoutLockFn = fn() -> StdoutLock<'static>;
type StderrLockFn = fn() -> StderrLock<'static>;
#[macro_export]
macro_rules! trace {
(level = $level:expr, $($rest:tt)*) => {{
match $level {
::tracing::Level::TRACE => ::tracing::trace!($($rest)*),
::tracing::Level::DEBUG => ::tracing::debug!($($rest)*),
::tracing::Level::INFO => ::tracing::info!($($rest)*),
::tracing::Level::WARN => ::tracing::warn!($($rest)*),
::tracing::Level::ERROR => ::tracing::error!($($rest)*),
}
}};
}
pub struct Tracing<W = StderrLockFn> {
level_filter: LevelFilter,
span_events: FmtSpan,
json_enabled: bool,
tokio_console_enabled: bool,
tokio_console_ip_addr: IpAddr,
tokio_console_port: u16,
writer: W,
}
impl Tracing<StderrLockFn> {
pub const DEFAULT_JSON_ENABLED: bool = false;
pub const DEFAULT_LEVEL_FILTER: LevelFilter = LevelFilter::INFO;
pub const DEFAULT_TOKIO_CONSOLE_ENABLED: bool = false;
pub const DEFAULT_TOKIO_CONSOLE_IP_ADDR: IpAddr = ConsoleServer::DEFAULT_IP;
pub const DEFAULT_TOKIO_CONSOLE_PORT: u16 = ConsoleServer::DEFAULT_PORT;
#[must_use]
pub fn default_span_events() -> FmtSpan {
FmtSpan::NEW | FmtSpan::CLOSE
}
pub fn default_writer() -> StderrLock<'static> {
Self::stderr_lock_writer()
}
}
impl Default for Tracing<StderrLockFn> {
fn default() -> Self {
Self {
level_filter: Self::DEFAULT_LEVEL_FILTER,
json_enabled: Self::DEFAULT_JSON_ENABLED,
span_events: Self::default_span_events(),
tokio_console_enabled: Self::DEFAULT_TOKIO_CONSOLE_ENABLED,
tokio_console_ip_addr: Self::DEFAULT_TOKIO_CONSOLE_IP_ADDR,
tokio_console_port: Self::DEFAULT_TOKIO_CONSOLE_PORT,
writer: Self::default_writer,
}
}
}
impl<W> Tracing<W> {
pub fn stdout_lock_writer() -> StdoutLock<'static> {
std::io::stdout().lock()
}
pub fn stderr_lock_writer() -> StderrLock<'static> {
std::io::stderr().lock()
}
#[must_use]
pub const fn with_level_filter(mut self, level_filter: LevelFilter) -> Self {
self.level_filter = level_filter;
self
}
#[must_use]
pub const fn with_json_enabled(mut self, json_enabled: bool) -> Self {
self.json_enabled = json_enabled;
self
}
#[must_use]
pub const fn with_tokio_console_port(mut self, tokio_console_port: u16) -> Self {
self.tokio_console_port = tokio_console_port;
self
}
#[must_use]
pub const fn with_tokio_console_enabled(mut self, tokio_console_enabled: bool) -> Self {
self.tokio_console_enabled = tokio_console_enabled;
self
}
#[must_use]
pub fn with_writer<W2>(self, writer: W2) -> Tracing<W2> {
Tracing {
level_filter: self.level_filter,
json_enabled: self.json_enabled,
span_events: self.span_events,
tokio_console_enabled: self.tokio_console_enabled,
tokio_console_ip_addr: self.tokio_console_ip_addr,
tokio_console_port: self.tokio_console_port,
writer,
}
}
pub fn with_stdout_lock_writer(self) -> Tracing<StdoutLockFn> {
self.with_writer(Self::stdout_lock_writer)
}
pub fn with_stderr_lock_writer(self) -> Tracing<StderrLockFn> {
self.with_writer(Self::stderr_lock_writer)
}
fn init_helper<N, F, T, W0>(self, layer: Layer<Registry, N, Format<F, T>, W0>)
where
N: 'static + for<'a> FormatFields<'a> + Send + Sync,
Format<F, T>: 'static + FormatEvent<Registry, N> + Send + Sync,
W0: 'static + for<'a> MakeWriter<'a> + Send + Sync,
W: 'static + for<'a> MakeWriter<'a> + Send + Sync,
{
let layer = layer
.with_span_events(self.span_events)
.with_writer(self.writer)
.with_filter(self.level_filter);
let layer = tracing_subscriber::registry().with(layer);
if self.tokio_console_enabled {
let tokio_console_server_addr = (self.tokio_console_ip_addr, self.tokio_console_port);
let console_layer = ConsoleLayer::builder().server_addr(tokio_console_server_addr).spawn();
let layer = layer.with(console_layer);
layer.init();
} else {
layer.init();
}
}
pub fn init(self)
where
W: 'static + for<'a> MakeWriter<'a> + Send + Sync,
{
let layer = tracing_subscriber::fmt::layer();
if self.json_enabled {
self.init_helper(layer.json());
} else {
self.init_helper(layer);
}
}
}