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