1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
//! One-call service logger setup for autocore binaries.
//!
//! Every long-running autocore process (the server and each module) needs the
//! same three things wired together, in the same order, every time:
//!
//! 1. a per-run log file in the shared log dir, keeping the newest N runs
//! ([`crate::run_log`]),
//! 2. a hard size cap on that file so a single run can never fill the disk
//! ([`RotatingFileWriter`]),
//! 3. a `simplelog` sink at `Info` (plus a terminal tee in debug builds).
//!
//! Historically each binary open-coded all three, and the size cap (step 2)
//! was simply forgotten everywhere — `RotatingFileWriter` existed but was wired
//! into nothing, so run files grew without bound (multi-GB files that filled
//! field disks). [`init_service_logger`] makes the capped path the *only* path:
//! call it once at startup and the cap can't be left out.
//!
//! # Memory
//!
//! This is disk-bounded, not RAM-bounded: [`RotatingFileWriter`] streams each
//! record straight to the file and holds only a byte counter in memory. There
//! is deliberately no in-RAM ring buffer — these targets are memory
//! constrained, so the FIFO behaviour lives on disk (rotate to one `.old`
//! backup), not in a heap buffer.
//!
//! # Example
//!
//! ```no_run
//! // In a module's `main`, service path only:
//! match mechutil::init_service_logger("autocore-ni", 7) {
//! Ok(info) => println!("autocore-ni log file: {:?}", info.path),
//! Err(e) => {
//! eprintln!("FATAL: cannot open log file: {e}");
//! std::process::exit(1);
//! }
//! }
//! ```
use io;
use PathBuf;
use ;
use crateRotatingFileWriter;
use crate;
/// Size threshold (bytes) at which a run's log file rotates to its single
/// `.old` backup. With one backup kept, the worst-case on-disk footprint for a
/// run is ~2x this value, so 2.5 MB keeps each run at or under ~5 MB total —
/// the agreed ceiling. Anything past the most recent ~5 MB of a run is dropped
/// (oldest first), which is the intended FIFO behaviour: nobody reads gigabytes
/// of a single run anyway.
pub const MAX_RUN_BYTES: u64 = 2_500_000;
/// File log level. Kept at `Info`: the file is the operator's record, so it
/// should hold startup, state changes, warnings and errors — but not per-sample
/// or per-message chatter. (Hot-path logging belongs at `Debug`/`Trace`, which
/// this filter drops in release.)
pub const FILE_LEVEL: LevelFilter = Info;
/// What [`init_service_logger`] set up, for the caller to report. The file
/// handle is owned by the installed logger, so it is intentionally not exposed.
/// Open this run's size-capped log file and install it as the global logger.
///
/// Combines [`open_default_run_log`] (shared dir + keep newest `keep` runs),
/// [`RotatingFileWriter`] (rotate at [`MAX_RUN_BYTES`], one `.old` backup), and
/// `simplelog`. In debug builds a `Debug`-level terminal tee is added; in
/// release the capped file is the only sink.
///
/// Returns an error if the log file can't be opened or a global logger is
/// already installed; callers decide whether that is fatal (the server and
/// modules exit) or recoverable (fall back to a terminal-only logger).