logger_nx/lib.rs
1//! # logger-nx
2//!
3//! A high-performance hourly-rotating file logger for Rust, implementing the
4//! [`log`] facade. Behaviorally equivalent to the Node.js
5//! [`@imcooder/node-logger`](https://github.com/imcooder/node-logger) library.
6//!
7//! ## Features
8//!
9//! - Active log written to `<app_name>.log`
10//! - Every hour the active file is renamed to `<app_name>.log.YYYYMMDDHH`
11//! - Files older than `ttl_hours` (default **72 h**) are deleted automatically
12//! - All I/O runs on a dedicated background thread (lock-free channel) —
13//! calling threads are **never** blocked
14//! - Zero unsafe code
15//!
16//! ## Log format
17//!
18//! ```text
19//! [2026-04-21 10:28:35.123] [INFO] my-app - Application started
20//! ```
21//!
22//! ## Quick start
23//!
24//! ```rust,no_run
25//! use logger_nx::{Config, init};
26//! use log::LevelFilter;
27//! use std::path::PathBuf;
28//!
29//! init(Config {
30//! app_name: "my-app".to_string(),
31//! log_dir: PathBuf::from("/var/log/my-app"),
32//! ttl_hours: 72,
33//! level: LevelFilter::Info,
34//! console: true,
35//! }).expect("logger init failed");
36//!
37//! log::info!("Application started");
38//! log::warn!("Low disk space");
39//! log::error!("Connection failed: {}", "timeout");
40//!
41//! // Before process exit:
42//! logger_nx::shutdown();
43//! ```
44
45mod cleaner;
46mod formatter;
47mod logger;
48
49pub use logger::{Config, Logger};
50
51use log::SetLoggerError;
52use std::sync::OnceLock;
53
54static LOGGER: OnceLock<Logger> = OnceLock::new();
55
56/// Initialise the global logger.
57///
58/// Call **once** at application startup. Returns `Err` if another logger has
59/// already been registered via the `log` crate.
60///
61/// # Example
62/// ```rust,no_run
63/// use logger_nx::{Config, init};
64/// use log::LevelFilter;
65/// use std::path::PathBuf;
66///
67/// init(Config {
68/// app_name: "my-app".to_string(),
69/// log_dir: PathBuf::from("/tmp/my-app-logs"),
70/// ttl_hours: 72,
71/// level: LevelFilter::Info,
72/// console: false,
73/// }).unwrap();
74/// ```
75pub fn init(config: Config) -> Result<(), SetLoggerError> {
76 let level = config.level;
77 let logger = LOGGER.get_or_init(|| Logger::new(config));
78 log::set_logger(logger)?;
79 log::set_max_level(level);
80 Ok(())
81}
82
83/// Flush pending writes and stop the background I/O thread gracefully.
84///
85/// Waits up to 2 seconds for the writer thread to drain. Call this before
86/// process exit.
87///
88/// # Example
89/// ```rust,no_run
90/// logger_nx::shutdown();
91/// ```
92pub fn shutdown() {
93 if let Some(logger) = LOGGER.get() {
94 logger.shutdown();
95 }
96}
97
98/// Convenience: build a [`Config`] with sensible defaults and a single call.
99///
100/// ```rust,no_run
101/// logger_nx::init(logger_nx::config("my-app", "/var/log/my-app")).unwrap();
102/// ```
103pub fn config(app_name: impl Into<String>, log_dir: impl Into<std::path::PathBuf>) -> Config {
104 Config {
105 app_name: app_name.into(),
106 log_dir: log_dir.into(),
107 ..Config::default()
108 }
109}
110
111#[cfg(test)]
112mod tests;