1#![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 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}