1use std::ops::BitAnd;
5use std::{
6 fmt::{Display, Formatter},
7 mem::forget,
8 sync::OnceLock,
9};
10use tracing::{
11 instrument::WithSubscriber, level_filters::LevelFilter, subscriber::Interest, Metadata,
12};
13use tracing_appender::{non_blocking::NonBlocking, rolling::Rotation};
14use tracing_subscriber::{
15 filter::Filtered,
16 fmt::{
17 self,
18 format::{DefaultFields, Format},
19 Layer as FmtLayer,
20 },
21 layer::{Context, Filter, Layered, SubscriberExt},
22 reload::{Handle, Layer as ReloadLayer},
23 util::SubscriberInitExt,
24 Layer, Registry,
25};
26
27use cuprate_helper::fs::logs_path;
28
29use crate::config::Config;
30
31static FILE_WRITER_FILTER_HANDLE: OnceLock<Handle<CupratedTracingFilter, Registry>> =
35 OnceLock::new();
36
37#[expect(clippy::type_complexity)] static STDOUT_FILTER_HANDLE: OnceLock<
42 Handle<
43 CupratedTracingFilter,
44 Layered<
45 Filtered<
46 FmtLayer<Registry, DefaultFields, Format, NonBlocking>,
47 ReloadLayer<CupratedTracingFilter, Registry>,
48 Registry,
49 >,
50 Registry,
51 Registry,
52 >,
53 >,
54> = OnceLock::new();
55
56#[derive(Debug)]
58pub struct CupratedTracingFilter {
59 pub level: LevelFilter,
60}
61
62impl Display for CupratedTracingFilter {
64 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
65 f.debug_struct("Filter")
66 .field("minimum_level", &self.level.to_string())
67 .finish()
68 }
69}
70
71impl<S> Filter<S> for CupratedTracingFilter {
72 fn enabled(&self, meta: &Metadata<'_>, cx: &Context<'_, S>) -> bool {
73 Filter::<S>::enabled(&self.level, meta, cx)
74 }
75
76 fn callsite_enabled(&self, meta: &'static Metadata<'static>) -> Interest {
77 Filter::<S>::callsite_enabled(&self.level, meta)
78 }
79
80 fn max_level_hint(&self) -> Option<LevelFilter> {
81 Some(self.level)
82 }
83}
84
85pub fn init_logging(config: &Config) {
87 let (stdout_filter, stdout_handle) = ReloadLayer::new(CupratedTracingFilter {
89 level: config.tracing.stdout.level,
90 });
91
92 STDOUT_FILTER_HANDLE.set(stdout_handle).unwrap();
93
94 let stdout_layer = FmtLayer::default()
95 .with_target(false)
96 .with_filter(stdout_filter);
97
98 let appender_config = &config.tracing.file;
100 let (appender, guard) = tracing_appender::non_blocking(
101 tracing_appender::rolling::Builder::new()
102 .rotation(Rotation::DAILY)
103 .max_log_files(appender_config.max_log_files)
104 .build(logs_path(&config.fs.data_directory, config.network()))
105 .unwrap(),
106 );
107
108 forget(guard);
110
111 let (appender_filter, appender_handle) = ReloadLayer::new(CupratedTracingFilter {
113 level: appender_config.level,
114 });
115 FILE_WRITER_FILTER_HANDLE.set(appender_handle).unwrap();
116
117 let appender_layer = fmt::layer()
118 .with_target(false)
119 .with_ansi(false)
120 .with_writer(appender)
121 .with_filter(appender_filter);
122
123 tracing_subscriber::registry()
125 .with(appender_layer)
126 .with(stdout_layer)
127 .init();
128}
129
130pub fn modify_stdout_output(f: impl FnOnce(&mut CupratedTracingFilter)) {
134 STDOUT_FILTER_HANDLE.get().unwrap().modify(f).unwrap();
135}
136
137pub fn modify_file_output(f: impl FnOnce(&mut CupratedTracingFilter)) {
141 FILE_WRITER_FILTER_HANDLE.get().unwrap().modify(f).unwrap();
142}
143
144pub fn eprintln_red(s: &str) {
146 eprintln!("{}", nu_ansi_term::Color::Red.bold().paint(s));
147}