re_log/
lib.rs

1//! Text logging (nothing to do with rerun logging) for use in rerun libraries.
2//!
3//! Provides helpers for adding multiple loggers,
4//! and for setting up logging on native and on web.
5//!
6//! * `trace`: spammy things
7//! * `debug`: things that might be useful when debugging
8//! * `info`: things that we want to show to users
9//! * `warn`: problems that we can recover from
10//! * `error`: problems that lead to loss of functionality or data
11//!
12//! The `warn_once` etc macros are for when you want to suppress repeated
13//! logging of the exact same message.
14
15mod channel_logger;
16mod result_extensions;
17
18#[cfg(feature = "setup")]
19mod multi_logger;
20
21#[cfg(feature = "setup")]
22mod setup;
23
24#[cfg(all(feature = "setup", target_arch = "wasm32"))]
25mod web_logger;
26
27pub use log::{Level, LevelFilter};
28
29pub use result_extensions::ResultExt;
30
31// The tracing macros support more syntax features than the log, that's why we use them:
32pub use tracing::{debug, error, info, trace, warn};
33
34// The `re_log::info_once!(…)` etc are nice helpers, but the `log-once` crate is a bit lacking.
35// In the future we should implement our own macros to de-duplicate based on the callsite,
36// similar to how the log console in a browser will automatically suppress duplicates.
37pub use log_once::{debug_once, error_once, info_once, log_once, trace_once, warn_once};
38
39pub use channel_logger::*;
40
41#[cfg(feature = "setup")]
42pub use multi_logger::{add_boxed_logger, add_logger, MultiLoggerNotSetupError};
43
44#[cfg(feature = "setup")]
45pub use setup::{setup_logging, setup_logging_with_filter};
46
47#[cfg(all(feature = "setup", not(target_arch = "wasm32")))]
48pub use setup::PanicOnWarnScope;
49
50/// Re-exports of other crates.
51pub mod external {
52    pub use log;
53}
54
55/// Never log anything less serious than a `ERROR` from these crates.
56const CRATES_AT_ERROR_LEVEL: &[&str] = &[
57    // silence rustls in release mode: https://github.com/rerun-io/rerun/issues/3104
58    #[cfg(not(debug_assertions))]
59    "rustls",
60];
61
62/// Never log anything less serious than a `WARN` from these crates.
63const CRATES_AT_WARN_LEVEL: &[&str] = &[
64    // wgpu crates spam a lot on info level, which is really annoying
65    // TODO(emilk): remove once https://github.com/gfx-rs/wgpu/issues/3206 is fixed
66    "naga",
67    "tracing",
68    "wgpu_core",
69    "wgpu_hal",
70    "zbus",
71];
72
73/// Never log anything less serious than a `INFO` from these crates.
74const CRATES_AT_INFO_LEVEL: &[&str] = &[
75    // These are quite spammy on debug, drowning out what we care about:
76    "h2",
77    "hyper",
78    "prost_build",
79    "tower",
80    "ureq",
81    // only let rustls log in debug mode: https://github.com/rerun-io/rerun/issues/3104
82    #[cfg(debug_assertions)]
83    "rustls",
84    // walkers generates noise around tile download, see https://github.com/podusowski/walkers/issues/199
85    "walkers",
86    // winit 0.30.5 spams about `set_cursor_visible` calls. It's gone on winit master, so hopefully gone in next winit release.
87    "winit",
88];
89
90/// Determines the default log filter.
91///
92/// Native: Get `RUST_LOG` environment variable or `info`, if not set.
93/// Also sets some other log levels on crates that are too loud.
94///
95/// Web: `debug` since web console allows arbitrary filtering.
96#[cfg(not(target_arch = "wasm32"))]
97pub fn default_log_filter() -> String {
98    let base_log_filter = if cfg!(debug_assertions) {
99        // We want the DEBUG level to be useful yet not too spammy.
100        // This is a good way to enforce that.
101        "debug"
102    } else {
103        // Important to keep the default at (at least) "info",
104        // as we print crucial information at INFO,
105        // e.g. the ip:port when hosting a server with `rerun-cli`.
106        "info"
107    };
108    log_filter_from_env_or_default(base_log_filter)
109}
110
111/// Determines the default log filter.
112///
113/// Native: Get `RUST_LOG` environment variable or `info`, if not set.
114/// Also sets some other log levels on crates that are too loud.
115///
116/// Web: `debug` since web console allows arbitrary filtering.
117#[cfg(target_arch = "wasm32")]
118pub fn default_log_filter() -> String {
119    "debug".to_owned()
120}
121
122/// Determines the log filter from the `RUST_LOG` environment variable or an explicit default.
123///
124/// Always adds builtin filters as well.
125#[cfg(not(target_arch = "wasm32"))]
126pub fn log_filter_from_env_or_default(default_base_log_filter: &str) -> String {
127    let rust_log = std::env::var("RUST_LOG").unwrap_or_else(|_| default_base_log_filter.to_owned());
128    add_builtin_log_filter(&rust_log)
129}
130
131/// Adds builtin log level filters for crates that are too verbose.
132#[cfg(not(target_arch = "wasm32"))]
133fn add_builtin_log_filter(base_log_filter: &str) -> String {
134    let mut rust_log = base_log_filter.to_lowercase();
135
136    if base_log_filter != "off" {
137        // If base level is `off`, don't opt-in to anything.
138
139        for crate_name in crate::CRATES_AT_ERROR_LEVEL {
140            if !rust_log.contains(&format!("{crate_name}=")) {
141                rust_log += &format!(",{crate_name}=error");
142            }
143        }
144
145        if base_log_filter != "error" {
146            // If base level is `error`, don't opt-in to `warn` or `info`.
147
148            for crate_name in crate::CRATES_AT_WARN_LEVEL {
149                if !rust_log.contains(&format!("{crate_name}=")) {
150                    rust_log += &format!(",{crate_name}=warn");
151                }
152            }
153
154            if base_log_filter != "warn" {
155                // If base level is not `error`/`warn`, don't opt-in to `info`.
156
157                for crate_name in crate::CRATES_AT_INFO_LEVEL {
158                    if !rust_log.contains(&format!("{crate_name}=")) {
159                        rust_log += &format!(",{crate_name}=info");
160                    }
161                }
162            }
163        }
164    }
165
166    //TODO(#8077): should be removed as soon as the upstream issue is resolved
167    rust_log += ",walkers::download=off";
168
169    rust_log
170}
171
172/// Should we log this message given the filter?
173fn is_log_enabled(filter: log::LevelFilter, metadata: &log::Metadata<'_>) -> bool {
174    if CRATES_AT_ERROR_LEVEL
175        .iter()
176        .any(|crate_name| metadata.target().starts_with(crate_name))
177    {
178        return metadata.level() <= log::LevelFilter::Error;
179    }
180
181    if CRATES_AT_WARN_LEVEL
182        .iter()
183        .any(|crate_name| metadata.target().starts_with(crate_name))
184    {
185        return metadata.level() <= log::LevelFilter::Warn;
186    }
187
188    if CRATES_AT_INFO_LEVEL
189        .iter()
190        .any(|crate_name| metadata.target().starts_with(crate_name))
191    {
192        return metadata.level() <= log::LevelFilter::Info;
193    }
194
195    metadata.level() <= filter
196}
197
198/// Shorten a path to a Rust source file.
199///
200/// Example input:
201/// * `/Users/emilk/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-1.24.1/src/runtime/runtime.rs`
202/// * `crates/rerun/src/main.rs`
203/// * `/rustc/d5a82bbd26e1ad8b7401f6a718a9c57c96905483/library/core/src/ops/function.rs`
204///
205/// Example output:
206/// * `tokio-1.24.1/src/runtime/runtime.rs`
207/// * `rerun/src/main.rs`
208/// * `core/src/ops/function.rs`
209#[allow(dead_code)] // only used on web and in tests
210fn shorten_file_path(file_path: &str) -> &str {
211    if let Some(i) = file_path.rfind("/src/") {
212        if let Some(prev_slash) = file_path[..i].rfind('/') {
213            &file_path[prev_slash + 1..]
214        } else {
215            file_path
216        }
217    } else {
218        file_path
219    }
220}
221
222#[test]
223fn test_shorten_file_path() {
224    for (before, after) in [
225        (
226            "/Users/emilk/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-1.24.1/src/runtime/runtime.rs",
227            "tokio-1.24.1/src/runtime/runtime.rs",
228        ),
229        ("crates/rerun/src/main.rs", "rerun/src/main.rs"),
230        (
231            "/rustc/d5a82bbd26e1ad8b7401f6a718a9c57c96905483/library/core/src/ops/function.rs",
232            "core/src/ops/function.rs",
233        ),
234        ("/weird/path/file.rs", "/weird/path/file.rs"),
235    ] {
236        assert_eq!(shorten_file_path(before), after);
237    }
238}