endpoint_libs/libs/
log.rs1use std::path::PathBuf;
2use std::str::FromStr;
3use std::sync::Arc;
4
5use eyre::eyre;
6use serde::{Deserialize, Serialize};
7use tracing::{level_filters::LevelFilter, Level};
8use tracing_subscriber::fmt;
9use tracing_subscriber::prelude::__tracing_subscriber_SubscriberExt;
10use tracing_subscriber::util::SubscriberInitExt;
11use tracing_subscriber::Layer;
12use tracing_subscriber::{registry, EnvFilter};
13
14#[derive(Default, Debug, Clone, Copy, Serialize, Deserialize, PartialEq)]
15#[serde(rename_all = "lowercase")]
16pub enum LogLevel {
17 #[default]
18 Off,
19 Error,
20 Warn,
21 Info,
22 Debug,
23 Trace,
24 Detail,
25}
26
27impl From<LogLevel> for LevelFilter {
28 fn from(value: LogLevel) -> Self {
29 match value {
30 LogLevel::Error => LevelFilter::ERROR,
31 LogLevel::Warn => LevelFilter::WARN,
32 LogLevel::Info => LevelFilter::INFO,
33 LogLevel::Debug => LevelFilter::DEBUG,
34 LogLevel::Trace => LevelFilter::TRACE,
35 LogLevel::Detail => LevelFilter::TRACE,
36 LogLevel::Off => LevelFilter::OFF,
37 }
38 }
39}
40
41impl From<LogLevel> for Level {
42 fn from(value: LogLevel) -> Self {
43 match value {
44 LogLevel::Error => Level::ERROR,
45 LogLevel::Warn => Level::WARN,
46 LogLevel::Info => Level::INFO,
47 LogLevel::Debug => Level::DEBUG,
48 LogLevel::Trace => Level::TRACE,
49 LogLevel::Off => Level::TRACE,
50 LogLevel::Detail => Level::TRACE,
51 }
52 }
53}
54
55impl FromStr for LogLevel {
56 type Err = eyre::Error;
57 fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
58 match s.to_ascii_lowercase().as_ref() {
59 "error" => Ok(LogLevel::Error),
60 "warn" => Ok(LogLevel::Warn),
61 "info" => Ok(LogLevel::Info),
62 "debug" => Ok(LogLevel::Debug),
63 "trace" => Ok(LogLevel::Trace),
64 "detail" => Ok(LogLevel::Detail),
65 "off" => Ok(LogLevel::Off),
66 _ => Err(eyre!("Invalid log level: {}", s)),
67 }
68 }
69}
70
71fn build_env_filter(log_level: LogLevel) -> eyre::Result<EnvFilter> {
72 let level: Level = log_level.into();
73 let mut filter = EnvFilter::from_default_env().add_directive(level.into());
74 if log_level != LogLevel::Detail {
75 filter = filter
76 .add_directive("tungstenite::protocol=debug".parse()?)
77 .add_directive("tokio_postgres::connection=debug".parse()?)
78 .add_directive("tokio_util::codec::framed_impl=debug".parse()?)
79 .add_directive("tokio_tungstenite=debug".parse()?)
80 .add_directive("h2=info".parse()?)
81 .add_directive("rustls::client::hs=info".parse()?)
82 .add_directive("rustls::client::tls13=info".parse()?)
83 .add_directive("hyper::client=info".parse()?)
84 .add_directive("hyper::proto=info".parse()?)
85 .add_directive("mio=info".parse()?)
86 .add_directive("want=info".parse()?)
87 .add_directive("sqlparser=info".parse()?);
88 }
89 Ok(filter)
90}
91
92pub enum LoggingGuard {
93 NonBlocking(tracing_appender::non_blocking::WorkerGuard, PathBuf),
94 StdoutWithPath(Option<PathBuf>),
95}
96impl LoggingGuard {
97 pub fn get_file(&self) -> Option<PathBuf> {
98 match self {
99 LoggingGuard::NonBlocking(_guard, path) => Some(path.clone()),
100 LoggingGuard::StdoutWithPath(path) => path.clone(),
101 }
102 }
103}
104pub fn setup_logs(
105 log_level: LogLevel,
106 log_dir_and_file_prefix: Option<(PathBuf, &str, Option<LogLevel>)>,
107) -> eyre::Result<()> {
108 let filter = build_env_filter(log_level)?;
109
110 let stdout_layer: tracing_subscriber::filter::Filtered<
111 fmt::Layer<registry::Registry>,
112 EnvFilter,
113 registry::Registry,
114 > = fmt::layer()
115 .with_thread_names(true)
116 .with_line_number(true)
117 .with_filter(filter);
118
119 if let Some((log_dir, file_prefix, file_log_level)) = log_dir_and_file_prefix {
120 let file_filter = if let Some(file_log_level) = file_log_level {
121 build_env_filter(file_log_level)?
122 } else {
123 build_env_filter(log_level)?
124 };
125
126 registry()
127 .with(stdout_layer)
128 .with(
129 fmt::layer()
130 .with_thread_names(true)
131 .with_line_number(true)
132 .with_ansi(false)
133 .with_writer(tracing_appender::rolling::hourly(log_dir, file_prefix))
134 .with_filter(file_filter),
135 )
136 .init();
137 } else {
138 registry().with(stdout_layer).init();
139 }
140
141 Ok(())
142}
143
144#[derive(Clone)]
145pub struct DynLogger {
146 logger: Arc<dyn Fn(&str) + Send + Sync>,
147}
148impl DynLogger {
149 pub fn new(logger: Arc<dyn Fn(&str) + Send + Sync>) -> Self {
150 Self { logger }
151 }
152 pub fn empty() -> Self {
153 Self {
154 logger: Arc::new(|_| {}),
155 }
156 }
157 pub fn log(&self, msg: impl AsRef<str>) {
158 (self.logger)(msg.as_ref())
159 }
160}
161
162pub fn can_create_file_in_directory(directory: &str) -> bool {
164 let test_file_path: String = format!("{directory}/test_file.txt");
165 match std::fs::File::create(&test_file_path) {
166 Ok(file) => {
167 drop(file);
169 if let Err(err) = std::fs::remove_file(&test_file_path) {
170 eprintln!("Error deleting test file: {err}");
171 }
172 true
173 }
174 Err(_) => false,
175 }
176}