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,
pub(crate) frozen: bool,
}
impl LoggerWithKvNestingTracking {
pub const MAX_NESTING: u32 = 1000;
pub const EXCEEDED_MAX_NESTING_ERROR: &'static str = "foundations: maximum logger KV nesting exceeded (are add_fields! or set_verbosity being called in a loop?)";
pub const FROZEN_LOGGER_ERROR: &'static str = "foundations: attempt to modify a frozen logger \
(are you modifying the root logger from a spawned task/thread that lacks a forked context?)";
pub(crate) fn new(logger: Logger) -> Self {
Self {
inner: logger,
nesting_level: 0,
frozen: false,
}
}
pub(crate) fn check_frozen(
current_log_lock: parking_lot::lock_api::RwLockWriteGuard<
parking_lot::RawRwLock,
LoggerWithKvNestingTracking,
>,
) -> Option<
parking_lot::lock_api::RwLockWriteGuard<
parking_lot::RawRwLock,
LoggerWithKvNestingTracking,
>,
> {
if !current_log_lock.frozen {
return Some(current_log_lock);
}
if cfg!(feature = "panic_on_frozen_logger") {
drop(current_log_lock);
panic!("{}", Self::FROZEN_LOGGER_ERROR);
} else {
slog::error!(current_log_lock, "{}", Self::FROZEN_LOGGER_ERROR; "backtrace" => std::backtrace::Backtrace::capture().to_string());
None
}
}
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 }
}
_ => 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(log_lock) = LoggerWithKvNestingTracking::check_frozen(log_lock) else {
return; };
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 mut log = parent.read().clone();
log.frozen = false;
Arc::new(parking_lot::RwLock::new(log))
}
pub(crate) fn freeze() {
current_log().write().frozen = true;
}
pub(crate) fn unfreeze() {
current_log().write().frozen = false;
}
pub(crate) fn is_frozen() -> bool {
current_log().read().frozen
}