use super::init::LogHarness;
use crate::telemetry::scope::Scope;
use slog::{Logger, OwnedKV, SendSyncRefUnwindSafeKV};
use std::ops::Deref;
use std::sync::Arc;
pub(crate) type SharedLog = Arc<parking_lot::RwLock<LoggerWithKvNestingTracking>>;
#[derive(Debug, Clone)]
pub struct LoggerWithKvNestingTracking {
pub(crate) inner: Logger,
pub(crate) nesting_level: u32,
}
impl LoggerWithKvNestingTracking {
pub const MAX_NESTING: u32 = 1000;
pub const EXCEEDED_MAX_NESTING_ERROR: &'static str = "rustfoundry: maximum logger KV nesting exceeded (are add_fields! or set_verbosity being called in a loop?)";
pub(crate) fn new(logger: Logger) -> Self {
Self {
inner: logger,
nesting_level: 0,
}
}
pub(crate) fn check_nesting_level(
mut current_log_lock: parking_lot::lock_api::RwLockWriteGuard<
parking_lot::RawRwLock,
LoggerWithKvNestingTracking,
>,
) -> Option<
parking_lot::lock_api::RwLockWriteGuard<
parking_lot::RawRwLock,
LoggerWithKvNestingTracking,
>,
> {
current_log_lock.nesting_level = current_log_lock.nesting_level.saturating_add(1);
match current_log_lock.nesting_level {
0..Self::MAX_NESTING => Some(current_log_lock), Self::MAX_NESTING => {
if cfg!(feature = "panic_on_too_much_logger_nesting") {
drop(current_log_lock);
panic!("{}", Self::EXCEEDED_MAX_NESTING_ERROR);
} else {
slog::error!(current_log_lock, "{}", Self::EXCEEDED_MAX_NESTING_ERROR; "backtrace"=> std::backtrace::Backtrace::capture().to_string());
None }
}
Self::MAX_NESTING..=u32::MAX => None, }
}
}
impl Deref for LoggerWithKvNestingTracking {
type Target = Logger;
#[inline]
fn deref(&self) -> &Self::Target {
&self.inner
}
}
#[must_use]
pub(crate) struct LogScope {
_inner: Scope<SharedLog>,
}
impl LogScope {
#[inline]
pub(crate) fn new(log: SharedLog) -> Self {
Self {
_inner: Scope::new(&LogHarness::get().log_scope_stack, log),
}
}
}
pub fn add_log_fields<T>(fields: OwnedKV<T>)
where
T: SendSyncRefUnwindSafeKV + 'static,
{
let log = current_log();
let log_lock = log.write();
let Some(mut log_lock) = LoggerWithKvNestingTracking::check_nesting_level(log_lock) else {
return; };
log_lock.inner = log_lock.inner.new(fields);
}
pub fn current_log() -> SharedLog {
let harness = LogHarness::get();
let log = harness.log_scope_stack.current();
log.unwrap_or_else(|| Arc::clone(&harness.root_log))
}
pub(crate) fn fork_log() -> SharedLog {
let parent = current_log();
let log = parent.read().clone();
Arc::new(parking_lot::RwLock::new(log))
}