captains_log/lib.rs
1//! # captains-log
2//!
3//! A light-weight logger for rust, implementation base on the crate `log`.
4//!
5//! ## Features
6//!
7//! * Allow customize log format and time format.
8//!
9//! * Supports signal listening for log-rotate.
10//!
11//! * Supports multiple log files, each with its own log level.
12//!
13//! * Supports hook on panic.
14//!
15//! * Supports multi-process/thread/coroutine log
16//!
17//! Atomic line appending into the same file can be done on Linux
18//!
19//! * Fine-grain module-level control.
20//!
21//! Provides `LogFilter` to filter specified logs on-the-fly
22//!
23//! * API-level log handling.
24//!
25//! Provides `LogFilterKV` for API logging with additional key.
26//!
27//! For example, you can set `req_id` in `LogFilterKV`, and track the
28//! complete request handling procedure from log.
29//!
30//! * For test suits usage:
31//!
32//! Allow dynamic reconfigure logger setting in different test function.
33//!
34//! (NOTE: currently signal_listener does not support reconfigure).
35//!
36//! Provides an attribute macro #\[logfn\] to wrap test function.
37//! Logging test-start and test-end.
38//!
39//! * Provides a `LogParser` to work on your log files.
40//!
41//! ## Dependency
42//!
43//! ``` toml
44//! [dependencies]
45//! log = { version = "0.4", features = ["std", "kv_unstable"] }
46//! captains_log = "0.3"
47//! ```
48//!
49//! ## Fast setup example:
50//!
51//! <font color=Blue>Refer to recipe in `recipe` module, including console & file output. </font>
52//!
53//! ```rust
54//! // #[macro_use]
55//! // extern crate captains_log;
56//! // #[macro_use]
57//! // extern crate log;
58//! use log::{debug, info, error};
59//! use captains_log::recipe::split_error_file_logger;
60//!
61//! let log_builder = split_error_file_logger("/tmp", "test", log::Level::Debug);
62//! log_builder.build();
63//!
64//! // non-error msg will only appear in /tmp/test.log
65//! debug!("Set a course to Sol system");
66//! info!("Engage");
67//!
68//! // will appear in both /tmp/test.log and /tmp/test.log.wf
69//! error!("Engine over heat!");
70//!
71//! ```
72//!
73//! ## Customize format example
74//!
75//! ``` rust
76//! extern crate signal_hook;
77//! extern crate chrono;
78//! use captains_log::*;
79
80//! fn format_f(r: FormatRecord) -> String {
81//! let time = r.time();
82//! let level = r.level();
83//! let file = r.file();
84//! let line = r.line();
85//! let msg = r.msg();
86//! format!("{time}|{level}|{file}:{line}|{msg}\n").to_string()
87//! }
88//! let debug_format = LogFormat::new(
89//! "%Y%m%d %H:%M:%S%.6f",
90//! format_f,
91//! );
92//! let debug_file = LogFile::new(
93//! "/tmp", "test.log", log::Level::Trace, debug_format);
94//! let config = Builder::default()
95//! .signal(signal_hook::consts::SIGINT)
96//! .file(debug_file);
97//!
98//! config.build();
99//! ```
100//!
101//! ## Fine-grain module-level control
102//!
103//! Place `LogFilter` in Arc and share among coroutines.
104//! Log level can be changed on-the-fly.
105//!
106//! ``` rust
107//! use std::sync::Arc;
108//! use captains_log::*;
109//! log::set_max_level(log::LevelFilter::Debug);
110//! let logger_io = Arc::new(LogFilter::new());
111//! let logger_req = Arc::new(LogFilter::new());
112//! logger_io.set_level(log::Level::Error);
113//! logger_req.set_level(log::Level::Debug);
114//! logger_debug!(logger_req, "Begin handle req ...");
115//! logger_debug!(logger_io, "Issue io to disk ...");
116//! logger_error!(logger_req, "Req invalid ...");
117//!
118//! ```
119//!
120//! ## API-level log handling
121//!
122//! Request log can be track by custom key `req_id`, which kept in `LogFilterKV`.
123//!
124//! ``` rust
125//! use captains_log::*;
126//! use log::*;
127//! fn debug_format_req_id_f(r: FormatRecord) -> String {
128//! let time = r.time();
129//! let level = r.level();
130//! let file = r.file();
131//! let line = r.line();
132//! let msg = r.msg();
133//! let req_id = r.key("req_id");
134//! format!("[{time}][{level}][{file}:{line}] {msg}{req_id}\n").to_string()
135//! }
136//! let builder = recipe::file_logger_custom("/tmp", "log_filter.log", log::Level::Debug,
137//! recipe::DEFAULT_TIME, debug_format_req_id_f);
138//! builder.build().expect("setup_log");
139//! let logger = LogFilterKV::new("req_id", format!("{:016x}", 123).to_string());
140//! info!("API service started");
141//! logger_debug!(logger, "Req / received");
142//! logger_debug!(logger, "header xxx");
143//! logger_info!(logger, "Req / 200 complete");
144//! ```
145//!
146//! The log will be:
147//!
148//! ``` text
149//! [2025-06-11 14:33:08.089090][DEBUG][request.rs:67] API service started
150//! [2025-06-11 14:33:10.099092][DEBUG][request.rs:67] Req / received (000000000000007b)
151//! [2025-06-11 14:33:10.099232][WARN][request.rs:68] header xxx (000000000000007b)
152//! [2025-06-11 14:33:11.009092][DEBUG][request.rs:67] Req / 200 complete (000000000000007b)
153//! ```
154
155extern crate captains_log_helper;
156extern crate log;
157extern crate signal_hook;
158
159#[macro_use]
160extern crate enum_dispatch;
161
162mod config;
163mod console_impl;
164mod file_impl;
165mod formatter;
166mod log_impl;
167mod time;
168
169pub mod macros;
170pub mod parser;
171pub mod recipe;
172
173mod log_filter;
174
175pub use self::{config::*, formatter::FormatRecord, log_filter::*, log_impl::setup_log};
176pub use captains_log_helper::logfn;
177
178#[cfg(test)]
179mod tests;