Skip to main content

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;