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