1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
//! Function to setup logging in binaries and web apps.

/// Automatically does the right thing depending on target environment (native vs. web).
///
/// Directs [`log`] calls to stderr on native.
pub fn setup_logging() {
    #[cfg(not(target_arch = "wasm32"))]
    fn setup() {
        if cfg!(debug_assertions) && std::env::var("RUST_BACKTRACE").is_err() {
            // In debug build, default `RUST_BACKTRACE` to `1` if it is not set.
            // This ensures sure we produce backtraces if our examples (etc) panics.

            // Our own crash handler (`re_crash_handler`) always prints a backtraces
            // (currently ignoring `RUST_BACKTRACE`) but we only use that for `rerun-cli`, our main binary.

            // `RUST_BACKTRACE` also turns on printing backtraces for `anyhow::Error`s that
            // are returned from `main` (i.e. if `main` returns `anyhow::Result`).
            std::env::set_var("RUST_BACKTRACE", "1");
        }

        crate::multi_logger::init().expect("Failed to set logger");

        let log_filter = crate::default_log_filter();

        if log_filter.contains("trace") {
            log::set_max_level(log::LevelFilter::Trace);
        } else if log_filter.contains("debug") {
            log::set_max_level(log::LevelFilter::Debug);
        } else {
            log::set_max_level(log::LevelFilter::Info);
        }

        let mut stderr_logger = env_logger::Builder::new();

        // This can be useful to enable to figure out what is causing a log message.
        let log_file_line = false;
        if log_file_line {
            stderr_logger.format(|buf, record| {
                use std::io::Write as _;
                writeln!(
                    buf,
                    "{} {}:{} {}",
                    record.level(),
                    record.file().unwrap_or_default(),
                    record.line().unwrap_or_default(),
                    record.args()
                )
            });
        }

        stderr_logger.parse_filters(&log_filter);
        crate::add_boxed_logger(Box::new(stderr_logger.build())).expect("Failed to install logger");

        if env_var_bool("RERUN_PANIC_ON_WARN") == Some(true) {
            crate::add_boxed_logger(Box::new(PanicOnWarn {}))
                .expect("Failed to enable RERUN_PANIC_ON_WARN");
            crate::info!("RERUN_PANIC_ON_WARN: any warning or error will cause Rerun to panic.");
        }
    }

    #[cfg(target_arch = "wasm32")]
    fn setup() {
        crate::multi_logger::init().expect("Failed to set logger");
        log::set_max_level(log::LevelFilter::Debug);
        crate::add_boxed_logger(Box::new(crate::web_logger::WebLogger::new(
            log::LevelFilter::Debug,
        )))
        .expect("Failed to install logger");
    }

    use std::sync::Once;
    static START: Once = Once::new();
    START.call_once(setup);
}

// ----------------------------------------------------------------------------

#[cfg(not(target_arch = "wasm32"))]
fn env_var_bool(name: &str) -> Option<bool> {
    std::env::var(name).ok()
        .and_then(|s| match s.to_lowercase().as_str() {
            "0" | "false" | "off" | "no" => Some(false),
            "1" | "true" | "on" | "yes" => Some(true),
            _ => {
                crate::warn!(
                    "Invalid value for environment variable {name}={s:?}. Expected 'on' or 'off'. It will be ignored"
                );
                None
            }
        })
}

#[cfg(not(target_arch = "wasm32"))]
struct PanicOnWarn {}

#[cfg(not(target_arch = "wasm32"))]
impl log::Log for PanicOnWarn {
    fn enabled(&self, metadata: &log::Metadata<'_>) -> bool {
        match metadata.level() {
            log::Level::Error | log::Level::Warn => true,
            log::Level::Info | log::Level::Debug | log::Level::Trace => false,
        }
    }

    fn log(&self, record: &log::Record<'_>) {
        let level = match record.level() {
            log::Level::Error => "error",
            log::Level::Warn => "warning",
            log::Level::Info | log::Level::Debug | log::Level::Trace => return,
        };

        panic!("{level} logged with RERUN_PANIC_ON_WARN: {}", record.args());
    }

    fn flush(&self) {}
}