veloren_common_frontend/
lib.rs

1#[cfg(not(feature = "tracy"))] use std::fs;
2use std::path::Path;
3
4use termcolor::{ColorChoice, StandardStream};
5use tracing::info;
6use tracing_appender::non_blocking::WorkerGuard;
7use tracing_subscriber::{
8    EnvFilter, filter::LevelFilter, fmt::writer::MakeWriter, prelude::*, registry,
9};
10
11const RUST_LOG_ENV: &str = "RUST_LOG";
12
13/// Initialise tracing and logging for the logs_path.
14///
15/// This function will attempt to set up both a file and a terminal logger,
16/// falling back to just a terminal logger if the file is unable to be created.
17///
18/// The logging level is by default set to `INFO`, to change this for any
19/// particular crate or module you must use the `RUST_LOG` environment
20/// variable.
21///
22/// For example to set this crate's debug level to `TRACE` you would need the
23/// following in your environment.
24/// `RUST_LOG="veloren_voxygen=trace"`
25///
26/// more complex tracing can be done by concatenating with a `,` as separator:
27///  - warn for `prometheus_hyper`, `dot_vox`, `gfx_device_gl::factory,
28///    `gfx_device_gl::shade` trace for `veloren_voxygen`, info for everything
29///    else
30///
31/// `RUST_LOG="prometheus_hyper=warn,dot_vox::parser=warn,gfx_device_gl::
32/// factory=warn,gfx_device_gl::shade=warn,veloren_voxygen=trace,info"`
33///
34/// By default a few directives are set to `warn` by default, until explicitly
35/// overwritten! e.g. `RUST_LOG="gfx_device_gl=debug"`
36pub fn init<W2>(
37    log_path_file: Option<(&Path, &str)>,
38    terminal: &'static W2,
39) -> Vec<impl Drop + use<W2>>
40where
41    W2: MakeWriter<'static> + 'static,
42    <W2 as MakeWriter<'static>>::Writer: 'static + Send + Sync,
43{
44    // To hold the guards that we create, they will cause the logs to be
45    // flushed when they're dropped.
46    #[cfg(not(feature = "tracy"))]
47    let mut guards: Vec<WorkerGuard> = Vec::new();
48    #[cfg(feature = "tracy")]
49    let guards: Vec<WorkerGuard> = Vec::new();
50
51    // We will do lower logging than the default (INFO) by INCLUSION. This
52    // means that if you need lower level logging for a specific module, then
53    // put it in the environment in the correct format i.e. DEBUG logging for
54    // this crate would be veloren_voxygen=debug.
55
56    let mut filter = EnvFilter::default().add_directive(LevelFilter::INFO.into());
57
58    let default_directives = [
59        "dot_vox::parser=warn",
60        "veloren_common::trade=info",
61        "veloren_world::sim=info",
62        "veloren_world::civ=info",
63        "veloren_world::site::economy=info",
64        "veloren_server::events::entity_manipulation=info",
65        "hyper=info",
66        "prometheus_hyper=info",
67        "mio::poll=info",
68        "mio::sys::windows=info",
69        "h2=info",
70        "tokio_util=info",
71        "rustls=info",
72        "naga=info",
73        "gfx_backend_vulkan=info",
74        "wgpu_core=info",
75        "wgpu_core::device=warn",
76        "wgpu_core::swap_chain=info",
77        "veloren_network_protocol=info",
78        "quinn_proto::connection=info",
79        "refinery_core::traits::divergent=off",
80        "veloren_server::persistence::character=info",
81        "veloren_server::settings=info",
82        "veloren_query_server=info",
83        "symphonia_format_ogg::demuxer=off",
84        "symphonia_core::probe=off",
85        "wgpu_hal::dx12::device=off",
86    ];
87
88    for s in default_directives {
89        filter = filter.add_directive(s.parse().unwrap());
90    }
91
92    match std::env::var(RUST_LOG_ENV) {
93        Ok(env) => {
94            for s in env.split(',') {
95                match s.parse() {
96                    Ok(d) => filter = filter.add_directive(d),
97                    Err(err) => eprintln!("WARN ignoring log directive: `{s}`: {err}"),
98                }
99            }
100        },
101        Err(std::env::VarError::NotUnicode(os_string)) => {
102            eprintln!("WARN ignoring log directives due to non-unicode data: {os_string:?}");
103        },
104        Err(std::env::VarError::NotPresent) => {},
105    };
106
107    let filter = filter; // mutation is done
108
109    let registry = registry();
110    #[cfg(not(feature = "tracy"))]
111    let mut file_setup = false;
112    #[cfg(feature = "tracy")]
113    let file_setup = false;
114    #[cfg(feature = "tracy")]
115    let _terminal = terminal;
116
117    // Create the terminal writer layer.
118    #[cfg(feature = "tracy")]
119    let registry = registry.with(tracing_tracy::TracyLayer::new(
120        tracing_tracy::DefaultConfig::default(),
121    ));
122    #[cfg(not(feature = "tracy"))]
123    let registry = {
124        let (non_blocking, stdio_guard) = tracing_appender::non_blocking(terminal.make_writer());
125        guards.push(stdio_guard);
126        registry.with(tracing_subscriber::fmt::layer().with_writer(non_blocking))
127    };
128
129    // Try to create the log file's parent folders.
130    #[cfg(not(feature = "tracy"))]
131    if let Some((path, file)) = log_path_file {
132        match fs::create_dir_all(path) {
133            Ok(_) => {
134                let file_appender = tracing_appender::rolling::never(path, file); // It is actually rolling daily since the log name is changing daily
135                let (non_blocking_file, file_guard) = tracing_appender::non_blocking(file_appender);
136                guards.push(file_guard);
137                file_setup = true;
138                registry
139                    .with(tracing_subscriber::fmt::layer().with_writer(non_blocking_file))
140                    .with(filter)
141                    .init();
142            },
143            Err(e) => {
144                tracing::error!(
145                    ?e,
146                    "Failed to create log file!. Falling back to terminal logging only.",
147                );
148                registry.with(filter).init();
149            },
150        }
151    } else {
152        registry.with(filter).init();
153    }
154    #[cfg(feature = "tracy")]
155    registry.with(filter).init();
156
157    if file_setup {
158        let (path, file) = log_path_file.unwrap();
159        info!(?path, ?file, "Setup terminal and file logging.");
160    }
161
162    if tracing::level_enabled!(tracing::Level::TRACE) {
163        info!("Tracing Level: TRACE");
164    } else if tracing::level_enabled!(tracing::Level::DEBUG) {
165        info!("Tracing Level: DEBUG");
166    };
167
168    // Return the guards
169    guards
170}
171
172pub fn init_stdout(log_path_file: Option<(&Path, &str)>) -> Vec<impl Drop + use<>> {
173    init(log_path_file, &|| StandardStream::stdout(ColorChoice::Auto))
174}