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_CLIENT, "nil_client");
58  add_directive!(NIL_CORE, "nil_core");
59  add_directive!(NIL_CRYPTO, "nil_crypto");
60  add_directive!(NIL_SERVER, "nil_server");
61  add_directive!(NIL_SERVER_DATABASE, "nil_server_database");
62
63  add_directive!(TOWER_HTTP, "tower_http", log_tower_http);
64
65  let mut chosen_layers = Vec::new();
66
67  // The order matters.
68  // See: https://github.com/tokio-rs/tracing/issues/1817
69  if layers.contains(Layers::FILE) {
70    let path = nil_env::log_dir()?;
71    fs::create_dir_all(&path)?;
72
73    let appender = RollingFileAppender::builder()
74      .rotation(Rotation::HOURLY)
75      .filename_suffix("log")
76      .max_log_files(30 * 24)
77      .build(path)?;
78
79    let (writer, worker_guard) = tracing_appender::non_blocking(appender);
80    chosen_layers.push(
81      Layer::default()
82        .with_ansi(false)
83        .with_timer(Timer)
84        .with_writer(writer)
85        .pretty()
86        .boxed(),
87    );
88
89    guard = Some(worker_guard);
90  }
91
92  if layers.contains(Layers::STDERR) {
93    chosen_layers.push(
94      Layer::default()
95        .with_ansi(true)
96        .with_timer(Timer)
97        .with_writer(io::stderr)
98        .pretty()
99        .boxed(),
100    );
101  }
102
103  let subscriber = Registry::default()
104    .with(chosen_layers)
105    .with(filter);
106
107  set_global_default(subscriber)?;
108
109  Ok(guard)
110}
111
112bitflags! {
113  #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
114  pub struct Directives: u32 {
115    const NIL                   = 1 << 0;
116    const NIL_CLIENT            = 1 << 1;
117    const NIL_CORE              = 1 << 2;
118    const NIL_CRYPTO            = 1 << 3;
119    const NIL_SERVER            = 1 << 4;
120    const NIL_SERVER_DATABASE   = 1 << 5;
121    const TOWER_HTTP            = 1 << 6;
122  }
123}
124
125impl const Default for Directives {
126  fn default() -> Self {
127    Self::all().difference(Self::TOWER_HTTP)
128  }
129}
130
131bitflags! {
132  #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
133  pub struct Layers: u32 {
134    const FILE       = 1 << 0;
135    const STDERR     = 1 << 1;
136  }
137}
138
139impl const Default for Layers {
140  fn default() -> Self {
141    Self::STDERR
142  }
143}