Skip to main content

nil_log/
lib.rs

1// Copyright (C) Call of Nil contributors
2// SPDX-License-Identifier: AGPL-3.0-only
3
4#![cfg_attr(docsrs, feature(doc_cfg))]
5#![doc(html_favicon_url = "https://nil.dev.br/favicon.png")]
6
7pub mod timer;
8
9use anyhow::Result;
10use bitflags::bitflags;
11use std::{env, fs, io};
12use timer::Timer;
13use tracing::subscriber::set_global_default;
14use tracing_appender::non_blocking::WorkerGuard;
15use tracing_appender::rolling::{RollingFileAppender, Rotation};
16use tracing_subscriber::fmt::Layer;
17use tracing_subscriber::layer::SubscriberExt;
18use tracing_subscriber::{EnvFilter, Layer as _, Registry};
19
20#[bon::builder]
21pub fn setup(
22  #[builder(default)] directives: Directives,
23  #[builder(default)] debug_layers: Layers,
24  #[builder(default)] release_layers: Layers,
25) -> Result<Option<WorkerGuard>> {
26  let mut guard = None::<WorkerGuard>;
27  let mut filter = EnvFilter::builder().from_env()?;
28
29  let layers = if cfg!(debug_assertions) { debug_layers } else { release_layers };
30  let level = env::var("NIL_LOG_LEVEL").unwrap_or_else(|_| String::from("trace"));
31
32  macro_rules! add_directive {
33    ($flag:ident, $krate:literal) => {
34      if directives.contains(Directives::$flag) {
35        let directive = format!("{}={}", $krate, level);
36        filter = filter.add_directive(directive.parse()?);
37      }
38    };
39    ($flag:ident, $krate:literal, $env_var:literal) => {
40      if directives.contains(Directives::$flag) || env::var($env_var).is_ok_and(|it| it == "true") {
41        let directive = format!("{}={}", $krate, level);
42        filter = filter.add_directive(directive.parse()?);
43      }
44    };
45  }
46
47  add_directive!(NIL, "nil");
48  add_directive!(NIL_CLIENT, "nil_client");
49  add_directive!(NIL_CORE, "nil_core");
50  add_directive!(NIL_CRYPTO, "nil_crypto");
51  add_directive!(NIL_LUA, "nil_lua");
52  add_directive!(NIL_LUA_CLI, "nil_lua_cli");
53  add_directive!(NIL_SERVER, "nil_server");
54  add_directive!(NIL_SERVER_DATABASE, "nil_server_database");
55
56  add_directive!(TOWER_HTTP, "tower_http", "NIL_LOG_TOWER_HTTP");
57
58  let mut chosen_layers = Vec::new();
59
60  // The order matters.
61  // See: https://github.com/tokio-rs/tracing/issues/1817
62  if layers.contains(Layers::FILE) {
63    let path = env::var("NIL_LOG_DIR")?;
64    fs::create_dir_all(&path)?;
65
66    let appender = RollingFileAppender::builder()
67      .rotation(Rotation::HOURLY)
68      .filename_suffix("log")
69      .max_log_files(30 * 24)
70      .build(path)?;
71
72    let (writer, worker_guard) = tracing_appender::non_blocking(appender);
73    chosen_layers.push(
74      Layer::default()
75        .with_ansi(false)
76        .with_timer(Timer)
77        .with_writer(writer)
78        .pretty()
79        .boxed(),
80    );
81
82    guard = Some(worker_guard);
83  }
84
85  if layers.contains(Layers::STDERR) {
86    chosen_layers.push(
87      Layer::default()
88        .with_ansi(true)
89        .with_timer(Timer)
90        .with_writer(io::stderr)
91        .pretty()
92        .boxed(),
93    );
94  }
95
96  let subscriber = Registry::default()
97    .with(chosen_layers)
98    .with(filter);
99
100  set_global_default(subscriber)?;
101
102  Ok(guard)
103}
104
105bitflags! {
106  #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
107  pub struct Directives: u32 {
108    const NIL                   = 1 << 0;
109    const NIL_CLIENT            = 1 << 1;
110    const NIL_CORE              = 1 << 2;
111    const NIL_CRYPTO            = 1 << 3;
112    const NIL_LUA               = 1 << 4;
113    const NIL_LUA_CLI           = 1 << 5;
114    const NIL_SERVER            = 1 << 6;
115    const NIL_SERVER_DATABASE   = 1 << 7;
116    const TOWER_HTTP            = 1 << 8;
117  }
118}
119
120impl Default for Directives {
121  fn default() -> Self {
122    Self::all().difference(Self::TOWER_HTTP)
123  }
124}
125
126bitflags! {
127  #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
128  pub struct Layers: u32 {
129    const FILE       = 1 << 0;
130    const STDERR     = 1 << 1;
131  }
132}
133
134impl Default for Layers {
135  fn default() -> Self {
136    Self::STDERR
137  }
138}