use crate::transport::Context;
use opentelemetry::trace::TraceContextExt;
use opentelemetry::Context as OTELContext;
pub extern crate log as rs_log;
pub trait RequestIdOwner {
fn request_id(&self) -> String;
}
impl RequestIdOwner for OTELContext {
fn request_id(&self) -> String {
self.span().span_context().trace_id().to_string()
}
}
impl RequestIdOwner for Context {
fn request_id(&self) -> String {
self.get("request_id")
.map(|s| s.to_string())
.unwrap_or_else(|| "unknown".to_string())
}
}
#[macro_export]
macro_rules! shors_error {
(ctx: $ctx:expr, target: $target:expr, $fmt:expr, $($arg:tt)*) => {
$crate::log::rs_log::error!(target: $target, concat!("[{}]: ", $fmt), $crate::log::RequestIdOwner::request_id($ctx), $($arg)*)
};
(ctx: $ctx:expr, $fmt:expr, $($arg:tt)*) => {
$crate::log::rs_log::error!(concat!("[{}]: ", $fmt), $crate::log::RequestIdOwner::request_id($ctx), $($arg)*)
};
(target: $target:expr, $($arg:tt)+) => {
$crate::log::rs_log::error!(target: $target, $($arg)+)
};
($($arg:tt)+) => {
$crate::log::rs_log::error!($($arg)+)
};
}
#[macro_export]
macro_rules! shors_warn {
(ctx: $ctx:expr, target: $target:expr, $fmt:expr, $($arg:tt)*) => {
$crate::log::rs_log::warn!(target: $target, concat!("[{}]: ", $fmt), $crate::log::RequestIdOwner::request_id($ctx), $($arg)*)
};
(ctx: $ctx:expr, $fmt:expr, $($arg:tt)*) => {
$crate::log::rs_log::warn!(concat!("[{}]: ", $fmt), $crate::log::RequestIdOwner::request_id($ctx), $($arg)*)
};
(target: $target:expr, $($arg:tt)+) => {
$crate::log::rs_log::warn!(target: $target, $($arg)+)
};
($($arg:tt)+) => {
$crate::log::rs_log::warn!($($arg)+)
};
}
#[macro_export]
macro_rules! shors_info {
(ctx: $ctx:expr, target: $target:expr, $fmt:expr, $($arg:tt)*) => {
$crate::log::rs_log::info!(target: $target, concat!("[{}]: ", $fmt), $crate::log::RequestIdOwner::request_id($ctx), $($arg)*)
};
(ctx: $ctx:expr, $fmt:expr, $($arg:tt)*) => {
$crate::log::rs_log::info!(concat!("[{}]: ", $fmt), $crate::log::RequestIdOwner::request_id($ctx), $($arg)*)
};
(target: $target:expr, $($arg:tt)+) => {
$crate::log::rs_log::info!(target: $target, $($arg)+)
};
($($arg:tt)+) => {
$crate::log::rs_log::info!($($arg)+)
};
}
#[macro_export]
macro_rules! shors_debug {
(ctx: $ctx:expr, target: $target:expr, $fmt:expr, $($arg:tt)*) => {
$crate::log::rs_log::debug!(target: $target, concat!("[{}]: ", $fmt), $crate::log::RequestIdOwner::request_id($ctx), $($arg)*)
};
(ctx: $ctx:expr, $fmt:expr, $($arg:tt)*) => {
$crate::log::rs_log::debug!(concat!("[{}]: ", $fmt), $crate::log::RequestIdOwner::request_id($ctx), $($arg)*)
};
(target: $target:expr, $($arg:tt)+) => {
$crate::log::rs_log::debug!(target: $target, $($arg)+)
};
($($arg:tt)+) => {
$crate::log::rs_log::debug!($($arg)+)
};
}
#[cfg(test)]
mod test {
use crate::transport::Context;
use log::{Level, LevelFilter, Log, Metadata, Record};
use once_cell::sync::Lazy;
use std::sync::Mutex;
struct BufferedLogger {
buff: Mutex<Vec<(Level, String, String)>>,
}
impl Log for BufferedLogger {
fn enabled(&self, _metadata: &Metadata) -> bool {
true
}
fn log(&self, record: &Record) {
self.buff.lock().unwrap().push((
record.level(),
record.target().to_string(),
record.args().to_string(),
))
}
fn flush(&self) {
self.buff.lock().unwrap().clear();
}
}
static LOGGER: Lazy<BufferedLogger> = Lazy::new(|| BufferedLogger {
buff: Mutex::default(),
});
#[test]
fn test_logger() {
log::set_logger(&*LOGGER)
.map(|()| log::set_max_level(LevelFilter::Debug))
.unwrap();
test_logger_simple();
test_logger_with_custom_target();
test_logger_with_request_id();
test_logger_with_custom_target_and_request_id();
}
fn test_logger_simple() {
LOGGER.flush();
shors_debug!("simple log record {}", 1);
shors_info!("simple log record {}", 2);
shors_warn!("simple log record {}", 3);
shors_error!("simple log record {}", 4);
assert_eq!(
&[
(
Level::Debug,
"shors::builtin::log::test".to_string(),
"simple log record 1".to_string()
),
(
Level::Info,
"shors::builtin::log::test".to_string(),
"simple log record 2".to_string()
),
(
Level::Warn,
"shors::builtin::log::test".to_string(),
"simple log record 3".to_string()
),
(
Level::Error,
"shors::builtin::log::test".to_string(),
"simple log record 4".to_string()
),
],
LOGGER.buff.lock().unwrap().as_slice()
);
}
fn test_logger_with_custom_target() {
LOGGER.flush();
shors_debug!(target: "test", "log record");
shors_info!(target: "test","log record");
shors_warn!(target: "test","log record");
shors_error!(target: "test", "log record");
assert_eq!(
&[
(Level::Debug, "test".to_string(), "log record".to_string()),
(Level::Info, "test".to_string(), "log record".to_string()),
(Level::Warn, "test".to_string(), "log record".to_string()),
(Level::Error, "test".to_string(), "log record".to_string()),
],
LOGGER.buff.lock().unwrap().as_slice()
);
}
fn test_logger_with_request_id() {
LOGGER.flush();
let mut ctx = Context::new();
ctx.put("request_id", "12345");
shors_debug!(ctx: &ctx, "{}", "log record");
shors_info!(ctx: &ctx, "{}", "log record");
shors_warn!(ctx: &ctx, "{}", "log record");
shors_error!(ctx: &ctx, "{}", "log record");
assert_eq!(
&[
(
Level::Debug,
"shors::builtin::log::test".to_string(),
"[12345]: log record".to_string()
),
(
Level::Info,
"shors::builtin::log::test".to_string(),
"[12345]: log record".to_string()
),
(
Level::Warn,
"shors::builtin::log::test".to_string(),
"[12345]: log record".to_string()
),
(
Level::Error,
"shors::builtin::log::test".to_string(),
"[12345]: log record".to_string()
),
],
LOGGER.buff.lock().unwrap().as_slice()
);
}
fn test_logger_with_custom_target_and_request_id() {
LOGGER.flush();
let mut ctx = Context::new();
ctx.put("request_id", "12345");
shors_debug!(ctx: &ctx, target: "test", "{}", "log record");
shors_info!(ctx: &ctx, target: "test", "{}", "log record");
shors_warn!(ctx: &ctx, target: "test", "{}", "log record");
shors_error!(ctx: &ctx, target: "test", "{}", "log record");
assert_eq!(
&[
(
Level::Debug,
"test".to_string(),
"[12345]: log record".to_string()
),
(
Level::Info,
"test".to_string(),
"[12345]: log record".to_string()
),
(
Level::Warn,
"test".to_string(),
"[12345]: log record".to_string()
),
(
Level::Error,
"test".to_string(),
"[12345]: log record".to_string()
),
],
LOGGER.buff.lock().unwrap().as_slice()
);
}
}