1use std::env;
4use std::marker::PhantomData;
5
6use log::{kv, LevelFilter, SetLoggerError};
7
8use crate::format::{Format, Gcloud, Json, LogFmt};
9#[cfg(feature = "log-panic")]
10use crate::PANIC_TARGET;
11use crate::{Logger, Targets};
12
13#[derive(Debug)]
20#[must_use = "the logger must be initialised using `init` or `try_init`"]
21pub struct Config<F, Kvs> {
22 filter: LevelFilter,
23 add_loc: Option<bool>,
24 targets: Targets,
25 kvs: Kvs,
26 format: PhantomData<F>,
27}
28
29impl Config<(), NoKvs> {
30 pub fn logfmt() -> Config<LogFmt, NoKvs> {
32 Config::new(NoKvs)
33 }
34
35 pub fn json() -> Config<Json, NoKvs> {
37 Config::new(NoKvs)
38 }
39
40 pub fn gcloud() -> Config<Gcloud, NoKvs> {
43 Config::new(NoKvs)
44 }
45}
46
47impl<F, Kvs> Config<F, Kvs>
48where
49 F: Format + Send + Sync + 'static,
50 Kvs: kv::Source + Send + Sync + 'static,
51{
52 fn new(kvs: Kvs) -> Config<F, Kvs> {
53 Config {
54 filter: get_max_level(),
55 add_loc: None,
56 targets: get_log_targets(),
57 kvs,
58 format: PhantomData,
59 }
60 }
61
62 pub fn with_kvs<K>(self, kvs: K) -> Config<F, K>
64 where
65 K: kv::Source + Send + Sync + 'static,
66 {
67 Config {
68 filter: self.filter,
69 add_loc: self.add_loc,
70 targets: self.targets,
71 kvs,
72 format: self.format,
73 }
74 }
75
76 pub fn with_call_location(self, enable: bool) -> Config<F, Kvs> {
80 Config {
81 filter: self.filter,
82 add_loc: Some(enable),
83 targets: self.targets,
84 kvs: self.kvs,
85 format: self.format,
86 }
87 }
88
89 pub fn init(self) {
100 self.try_init()
101 .unwrap_or_else(|err| panic!("failed to initialise the logger: {err}"));
102 }
103
104 pub fn try_init(self) -> Result<(), SetLoggerError> {
112 let logger = Box::new(Logger {
113 filter: self.filter,
114 add_loc: self.add_loc.unwrap_or(self.filter >= LevelFilter::Debug),
115 targets: self.targets,
116 kvs: self.kvs,
117 format: self.format,
118 });
119 log::set_boxed_logger(logger)?;
120 log::set_max_level(self.filter);
121
122 #[cfg(feature = "log-panic")]
123 std::panic::set_hook(Box::new(log_panic));
124 Ok(())
125 }
126}
127
128pub(crate) fn get_max_level() -> LevelFilter {
130 for var in &["LOG", "LOG_LEVEL"] {
131 if let Ok(level) = env::var(var) {
132 if let Ok(level) = level.parse() {
133 return level;
134 }
135 }
136 }
137
138 if env::var("TRACE").is_ok() {
139 LevelFilter::Trace
140 } else if env::var("DEBUG").is_ok() {
141 LevelFilter::Debug
142 } else {
143 LevelFilter::Info
144 }
145}
146
147pub(crate) fn get_log_targets() -> Targets {
149 match env::var("LOG_TARGET") {
150 Ok(ref targets) if !targets.is_empty() => {
151 Targets::Only(targets.split(',').map(Into::into).collect())
152 }
153 _ => Targets::All,
154 }
155}
156
157#[cfg(feature = "log-panic")]
159#[allow(deprecated)] fn log_panic(info: &std::panic::PanicInfo<'_>) {
161 use std::backtrace::Backtrace;
162 use std::thread;
163
164 let mut record = log::Record::builder();
165 let thread = thread::current();
166 let thread_name = thread.name().unwrap_or("unnamed");
167 let backtrace = Backtrace::force_capture();
168
169 let key_values = [
170 ("backtrace", kv::Value::from_display(&backtrace)),
171 ("thread_name", kv::Value::from(thread_name)),
172 ];
173 let key_values = key_values.as_slice();
174
175 let _ = record
176 .level(log::Level::Error)
177 .target(PANIC_TARGET)
178 .key_values(&key_values);
179
180 if let Some(location) = info.location() {
181 let _ = record
182 .file(Some(location.file()))
183 .line(Some(location.line()));
184 };
185
186 log::logger().log(
188 &record
189 .args(format_args!("thread '{thread_name}' {info}"))
190 .build(),
191 );
192}
193
194#[derive(Debug)]
196pub struct NoKvs;
197
198impl kv::Source for NoKvs {
199 fn visit<'kvs>(
200 &'kvs self,
201 _: &mut dyn log::kv::VisitSource<'kvs>,
202 ) -> Result<(), log::kv::Error> {
203 Ok(())
204 }
205
206 fn get<'v>(&'v self, _: log::kv::Key<'_>) -> Option<log::kv::Value<'v>> {
207 None
208 }
209
210 fn count(&self) -> usize {
211 0
212 }
213}