#![cfg_attr(all(feature = "json"), doc = "```no_run")]
#![cfg_attr(not(all(feature = "json")), doc = "```ignore")]
use std::sync::OnceLock;
use crate::error::Result;
use crate::level::Level;
use crate::logger::{Logger, in_dispatch};
use crate::record::{Field, Metadata};
use crate::sink::on_background_worker;
static GLOBAL: OnceLock<Logger> = OnceLock::new();
pub fn set(logger: Logger) -> core::result::Result<(), SetGlobalError> {
GLOBAL.set(logger).map_err(SetGlobalError)
}
pub fn init(logger: Logger) -> core::result::Result<FlushGuard, SetGlobalError> {
let guard = FlushGuard::new(logger.clone());
set(logger)?;
Ok(guard)
}
#[must_use]
pub fn get() -> Option<&'static Logger> {
GLOBAL.get()
}
pub fn flush() -> Result<()> {
GLOBAL.get().map_or(Ok(()), Logger::flush)
}
pub fn shutdown() -> Result<()> {
GLOBAL.get().map_or(Ok(()), Logger::shutdown)
}
pub fn install_panic_hook() {
let previous = std::panic::take_hook();
std::panic::set_hook(Box::new(move |info| {
if !in_dispatch() && !on_background_worker() {
if let Some(logger) = GLOBAL.get() {
log_panic(logger, info);
}
}
previous(info);
}));
}
fn log_panic(logger: &Logger, info: &std::panic::PanicHookInfo<'_>) {
let payload = info.payload();
let message = payload
.downcast_ref::<&str>()
.copied()
.or_else(|| payload.downcast_ref::<String>().map(String::as_str))
.unwrap_or("panic with a non-string payload");
let thread = std::thread::current();
let thread_name = thread.name().unwrap_or("<unnamed>");
let mut metadata = Metadata::new(Level::Error, "panic");
let mut fields = [
Field::new("thread", thread_name),
Field::new("column", 0_u32),
];
let mut count = 1;
if let Some(location) = info.location() {
metadata = metadata.with_location(location.file(), location.line());
fields[1] = Field::new("column", location.column());
count = 2;
}
let _reported = logger.try_emit(metadata, message, &fields[..count]);
let _flushed = logger.flush();
}
#[must_use = "the guard flushes the logger when dropped; bind it to a named variable"]
#[derive(Debug)]
pub struct FlushGuard {
logger: Logger,
}
impl FlushGuard {
pub(crate) const fn new(logger: Logger) -> Self {
Self { logger }
}
#[must_use]
pub fn logger(&self) -> &Logger {
&self.logger
}
}
impl Drop for FlushGuard {
fn drop(&mut self) {
let _reported = self.logger.shutdown();
}
}
pub struct SetGlobalError(Logger);
impl SetGlobalError {
#[must_use]
pub fn into_inner(self) -> Logger {
self.0
}
}
impl core::fmt::Debug for SetGlobalError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.write_str("SetGlobalError(..)")
}
}
impl core::fmt::Display for SetGlobalError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.write_str("a global logger is already installed")
}
}
impl std::error::Error for SetGlobalError {}