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        "assets_manager::anycache=info",
70        "polling::epoll=info",
71        "h2=info",
72        "tokio_util=info",
73        "rustls=info",
74        "naga=info",
75        "gfx_backend_vulkan=info",
76        "wgpu_core=info",
77        "wgpu_core::device=warn",
78        "wgpu_core::swap_chain=info",
79        "veloren_network_protocol=info",
80        "quinn_proto::connection=info",
81        "refinery_core::traits::divergent=off",
82        "veloren_server::persistence::character=info",
83        "veloren_server::settings=info",
84        "veloren_query_server=info",
85        "symphonia_format_ogg::demuxer=off",
86        "symphonia_core::probe=off",
87        "wgpu_hal::dx12::device=off",
88    ];
89
90    for s in default_directives {
91        filter = filter.add_directive(s.parse().unwrap());
92    }
93
94    match std::env::var(RUST_LOG_ENV) {
95        Ok(env) => {
96            for s in env.split(',') {
97                match s.parse() {
98                    Ok(d) => filter = filter.add_directive(d),
99                    Err(err) => eprintln!("WARN ignoring log directive: `{s}`: {err}"),
100                }
101            }
102        },
103        Err(std::env::VarError::NotUnicode(os_string)) => {
104            eprintln!("WARN ignoring log directives due to non-unicode data: {os_string:?}");
105        },
106        Err(std::env::VarError::NotPresent) => {},
107    };
108
109    let filter = filter; // mutation is done
110
111    let registry = registry();
112    #[cfg(not(feature = "tracy"))]
113    let mut file_setup = false;
114    #[cfg(feature = "tracy")]
115    let file_setup = false;
116    #[cfg(feature = "tracy")]
117    let _terminal = terminal;
118
119    // Create the terminal writer layer.
120    #[cfg(feature = "tracy")]
121    let registry = registry.with(tracing_tracy::TracyLayer::new(
122        tracing_tracy::DefaultConfig::default(),
123    ));
124    #[cfg(not(feature = "tracy"))]
125    let registry = {
126        let (non_blocking, stdio_guard) = tracing_appender::non_blocking(terminal.make_writer());
127        guards.push(stdio_guard);
128        registry.with(tracing_subscriber::fmt::layer().with_writer(non_blocking))
129    };
130
131    // Try to create the log file's parent folders.
132    #[cfg(not(feature = "tracy"))]
133    if let Some((path, file)) = log_path_file {
134        match fs::create_dir_all(path) {
135            Ok(_) => {
136                let file_appender = tracing_appender::rolling::never(path, file); // It is actually rolling daily since the log name is changing daily
137                let (non_blocking_file, file_guard) = tracing_appender::non_blocking(file_appender);
138                guards.push(file_guard);
139                file_setup = true;
140                registry
141                    .with(tracing_subscriber::fmt::layer().with_writer(non_blocking_file))
142                    .with(filter)
143                    .init();
144            },
145            Err(e) => {
146                tracing::error!(
147                    ?e,
148                    "Failed to create log file!. Falling back to terminal logging only.",
149                );
150                registry.with(filter).init();
151            },
152        }
153    } else {
154        registry.with(filter).init();
155    }
156    #[cfg(feature = "tracy")]
157    registry.with(filter).init();
158
159    if file_setup {
160        let (path, file) = log_path_file.unwrap();
161        info!(?path, ?file, "Setup terminal and file logging.");
162    }
163
164    if tracing::level_enabled!(tracing::Level::TRACE) {
165        info!("Tracing Level: TRACE");
166    } else if tracing::level_enabled!(tracing::Level::DEBUG) {
167        info!("Tracing Level: DEBUG");
168    };
169
170    // Return the guards
171    guards
172}
173
174pub fn init_stdout(log_path_file: Option<(&Path, &str)>) -> Vec<impl Drop + use<>> {
175    init(log_path_file, &|| StandardStream::stdout(ColorChoice::Auto))
176}