1use crate::Config;
2use crate::model::errors::{LbResult, core_err_unexpected};
3use crate::service::debug::{generate_panic_content, generate_panic_filename};
4use std::backtrace::Backtrace;
5use std::fs::OpenOptions;
6use std::io::Write;
7use std::{env, panic};
8use tracing::metadata::LevelFilter;
9use tracing_subscriber::fmt::format::FmtSpan;
10use tracing_subscriber::prelude::*;
11use tracing_subscriber::{Layer, filter, fmt};
12
13pub static LOG_FILE: &str = "lockbook.log";
14
15pub fn init(config: &Config) -> LbResult<()> {
16 if config.logs {
17 let lockbook_log_level = env::var("LOG_LEVEL")
18 .ok()
19 .and_then(|s| s.as_str().parse().ok())
20 .unwrap_or(LevelFilter::DEBUG);
21
22 let mut layers = Vec::with_capacity(2);
23
24 #[cfg(not(target_arch = "wasm32"))]
25 layers.push(
26 fmt::Layer::new()
27 .with_span_events(FmtSpan::NEW | FmtSpan::CLOSE)
28 .with_ansi(config.colored_logs)
29 .with_target(true)
30 .with_writer(tracing_appender::rolling::never(&config.writeable_path, LOG_FILE))
31 .with_filter(lockbook_log_level)
32 .with_filter(filter::filter_fn(|metadata| {
33 metadata.target().starts_with("lb_rs")
34 || metadata.target().starts_with("dbrs")
35 || metadata.target().starts_with("workspace")
36 || metadata.target().starts_with("lb_fs")
37 }))
38 .boxed(),
39 );
40
41 if config.stdout_logs {
42 #[cfg(not(target_os = "android"))]
44 layers.push(
45 fmt::Layer::new()
46 .with_span_events(FmtSpan::NEW | FmtSpan::CLOSE)
47 .with_ansi(config.colored_logs)
48 .with_target(true)
49 .with_filter(lockbook_log_level)
50 .with_filter(filter::filter_fn(|metadata| {
51 metadata.target().starts_with("lb_rs")
52 || metadata.target().starts_with("dbrs")
53 || metadata.target().starts_with("workspace")
54 || metadata.target().starts_with("lb_fs")
55 }))
56 .boxed(),
57 );
58 #[cfg(target_os = "android")]
59 if let Some(writer) =
60 tracing_logcat::LogcatMakeWriter::new(tracing_logcat::LogcatTag::Target).ok()
61 {
62 layers.push(
63 fmt::Layer::new()
64 .with_span_events(FmtSpan::NEW | FmtSpan::CLOSE)
65 .with_ansi(false)
66 .with_writer(writer)
67 .with_filter(lockbook_log_level)
68 .with_filter(filter::filter_fn(|metadata| {
69 metadata.target().starts_with("lb_rs")
70 || metadata.target().starts_with("workspace")
71 || metadata.target().starts_with("lb_java")
72 }))
73 .boxed(),
74 );
75 }
76 }
77 tracing::subscriber::set_global_default(
78 tracing_subscriber::Registry::default().with(layers),
79 )
80 .map_err(core_err_unexpected)?;
81 panic_capture(config);
82 }
83 Ok(())
84}
85
86fn panic_capture(config: &Config) {
87 let path = config.writeable_path.clone();
88 panic::set_hook(Box::new(move |error_header| {
89 let bt = Backtrace::force_capture();
90 tracing::error!("panic detected: {error_header} {}", bt);
91 eprintln!("panic detected and logged: {error_header} {bt}");
92 let file_name = generate_panic_filename(&path);
93 let content = generate_panic_content(&error_header.to_string(), &bt.to_string());
94
95 let mut file = OpenOptions::new()
96 .create(true)
97 .append(true)
98 .open(&file_name)
99 .unwrap();
100
101 file.write_all(content.as_bytes()).unwrap();
102 }));
103}