1use std::path::Path;
2
3use tracing_subscriber::{
4 filter::filter_fn, fmt, layer::SubscriberExt, util::SubscriberInitExt, EnvFilter, Layer,
5};
6
7pub fn init_logging_with_level(level: &str) {
11 let filter = EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new(level));
12
13 fmt()
14 .with_env_filter(filter)
15 .with_target(true)
16 .with_thread_ids(true)
17 .with_file(true)
18 .with_line_number(true)
19 .init();
20}
21
22pub fn init_logging() {
24 init_logging_with_level("info");
25}
26
27pub fn init_json_logging_with_level(level: &str) {
29 let filter = EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new(level));
30
31 fmt()
32 .json()
33 .with_env_filter(filter)
34 .with_target(true)
35 .with_thread_ids(true)
36 .init();
37}
38
39pub fn init_json_logging() {
41 init_json_logging_with_level("info");
42}
43
44pub fn init_logging_with_audit(
52 level: &str,
53 audit_path: Option<&Path>,
54) -> std::io::Result<Option<tracing_appender::non_blocking::WorkerGuard>> {
55 let filter = EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new(level));
56
57 let fmt_layer = fmt::layer()
58 .with_target(true)
59 .with_thread_ids(true)
60 .with_file(true)
61 .with_line_number(true)
62 .with_writer(std::io::stderr);
63
64 let registry = tracing_subscriber::registry().with(filter).with(fmt_layer);
65
66 if let Some(path) = audit_path {
67 let (writer, guard) = open_audit_writer(path)?;
69 let audit_layer = fmt::layer()
70 .json()
71 .flatten_event(true)
72 .with_current_span(false)
73 .with_span_list(false)
74 .with_target(true)
75 .with_writer(writer)
76 .with_filter(filter_fn(|meta| meta.target() == "futu_audit"));
77 registry.with(audit_layer).init();
78 Ok(Some(guard))
79 } else {
80 registry.init();
81 Ok(None)
82 }
83}
84
85fn open_audit_writer(
89 path: &Path,
90) -> std::io::Result<(
91 tracing_appender::non_blocking::NonBlocking,
92 tracing_appender::non_blocking::WorkerGuard,
93)> {
94 let is_dir_hint = path.is_dir()
95 || path.as_os_str().to_string_lossy().ends_with('/')
96 || path.extension().is_none();
97 if is_dir_hint {
98 std::fs::create_dir_all(path)?;
99 let appender = tracing_appender::rolling::daily(path, "futu-audit.log");
100 Ok(tracing_appender::non_blocking(appender))
101 } else {
102 if let Some(parent) = path.parent() {
103 if !parent.as_os_str().is_empty() {
104 std::fs::create_dir_all(parent)?;
105 }
106 }
107 let file = std::fs::OpenOptions::new()
108 .append(true)
109 .create(true)
110 .open(path)?;
111 Ok(tracing_appender::non_blocking(file))
112 }
113}