dontpanic 0.6.0

Get instant notifications about any panic!(); in your codebase.
Documentation
use std::{
    sync::atomic::Ordering,
    time::{SystemTime, UNIX_EPOCH},
};

use log::{Level, Log, Metadata, Record};
use ring_channel::{RingReceiver, RingSender};

use super::{send_report, Config, LogEvent, ReportLocation};

impl From<&Record<'_>> for LogEvent {
    fn from(record: &Record) -> Self {
        Self {
            timestamp: SystemTime::now()
                .duration_since(UNIX_EPOCH)
                .map(|d| d.as_secs())
                .unwrap_or_default(),
            level: record.level() as u8,
            message: format!("{}", record.args()),
            module: record.module_path().map(String::from),
            file: record.file().map(String::from),
            line: record.line(),
        }
    }
}

pub(crate) struct LogWrapper<T> {
    pub next: T,
    pub tx: RingSender<LogEvent>,
    pub rx: RingReceiver<LogEvent>,
    pub config: Config,
}

impl<T> Log for LogWrapper<T>
where
    T: Log,
{
    fn enabled(&self, metadata: &Metadata) -> bool {
        self.next.enabled(metadata)
    }

    fn log(&self, record: &Record) {
        self.next.log(record);

        if !self.config.is_enabled.load(Ordering::Relaxed) {
            return;
        }

        let _ = self.tx.send(LogEvent::from(record));

        // Level::Error is the only level that we want to report on. Don't Panic is not intended to
        // be a full log storage system, but rather a way to get a quick notification when
        // something goes wrong.

        if record.level() == Level::Error && self.config.report_on_log_errors {
            let title = format!("{}", record.args());

            let loc = if let (Some(file), Some(line)) = (record.file(), record.line()) {
                Some(ReportLocation {
                    file: file.to_string(),
                    line,
                    col: None,
                })
            } else {
                None
            };

            send_report(&self.config, title, loc, &self.rx)
        }
    }

    fn flush(&self) {}
}