author | Pierre-Yves David <pierre-yves.david@octobus.net> |
Tue, 11 Mar 2025 02:29:42 +0100 | |
branch | stable |
changeset 53042 | cdd7bf612c7b |
parent 52167 | 7be39c5110c9 |
permissions | -rw-r--r-- |
46599
1f55cd5b292f
rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
1 |
use crate::errors::{HgError, HgResultExt, IoErrorContext, IoResultExt}; |
52167
7be39c5110c9
hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents:
51864
diff
changeset
|
2 |
use crate::vfs::{Vfs, VfsImpl}; |
46599
1f55cd5b292f
rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
3 |
use std::io::Write; |
52167
7be39c5110c9
hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents:
51864
diff
changeset
|
4 |
use std::path::Path; |
46599
1f55cd5b292f
rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
5 |
|
1f55cd5b292f
rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
6 |
/// An utility to append to a log file with the given name, and optionally |
1f55cd5b292f
rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
7 |
/// rotate it after it reaches a certain maximum size. |
1f55cd5b292f
rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
8 |
/// |
1f55cd5b292f
rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
9 |
/// Rotation works by renaming "example.log" to "example.log.1", after renaming |
1f55cd5b292f
rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
10 |
/// "example.log.1" to "example.log.2" etc up to the given maximum number of |
1f55cd5b292f
rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
11 |
/// files. |
1f55cd5b292f
rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
12 |
pub struct LogFile<'a> { |
51864 | 13 |
vfs: VfsImpl, |
46599
1f55cd5b292f
rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
14 |
name: &'a str, |
1f55cd5b292f
rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
15 |
max_size: Option<u64>, |
1f55cd5b292f
rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
16 |
max_files: u32, |
1f55cd5b292f
rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
17 |
} |
1f55cd5b292f
rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
18 |
|
1f55cd5b292f
rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
19 |
impl<'a> LogFile<'a> { |
51864 | 20 |
pub fn new(vfs: VfsImpl, name: &'a str) -> Self { |
46599
1f55cd5b292f
rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
21 |
Self { |
1f55cd5b292f
rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
22 |
vfs, |
1f55cd5b292f
rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
23 |
name, |
1f55cd5b292f
rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
24 |
max_size: None, |
1f55cd5b292f
rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
25 |
max_files: 0, |
1f55cd5b292f
rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
26 |
} |
1f55cd5b292f
rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
27 |
} |
1f55cd5b292f
rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
28 |
|
1f55cd5b292f
rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
29 |
/// Rotate before writing to a log file that was already larger than the |
1f55cd5b292f
rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
30 |
/// given size, in bytes. `None` disables rotation. |
1f55cd5b292f
rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
31 |
pub fn max_size(mut self, value: Option<u64>) -> Self { |
1f55cd5b292f
rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
32 |
self.max_size = value; |
1f55cd5b292f
rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
33 |
self |
1f55cd5b292f
rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
34 |
} |
1f55cd5b292f
rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
35 |
|
1f55cd5b292f
rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
36 |
/// Keep this many rotated files `{name}.1` up to `{name}.{max}`, in |
1f55cd5b292f
rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
37 |
/// addition to the original `{name}` file. |
1f55cd5b292f
rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
38 |
pub fn max_files(mut self, value: u32) -> Self { |
1f55cd5b292f
rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
39 |
self.max_files = value; |
1f55cd5b292f
rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
40 |
self |
1f55cd5b292f
rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
41 |
} |
1f55cd5b292f
rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
42 |
|
1f55cd5b292f
rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
43 |
/// Append the given `bytes` as-is to the log file, after rotating if |
1f55cd5b292f
rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
44 |
/// needed. |
1f55cd5b292f
rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
45 |
/// |
1f55cd5b292f
rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
46 |
/// No trailing newline is added. Make sure to include one in `bytes` if |
1f55cd5b292f
rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
47 |
/// desired. |
1f55cd5b292f
rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
48 |
pub fn write(&self, bytes: &[u8]) -> Result<(), HgError> { |
1f55cd5b292f
rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
49 |
let path = self.vfs.join(self.name); |
1f55cd5b292f
rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
50 |
let context = || IoErrorContext::WritingFile(path.clone()); |
1f55cd5b292f
rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
51 |
let open = || { |
1f55cd5b292f
rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
52 |
std::fs::OpenOptions::new() |
1f55cd5b292f
rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
53 |
.create(true) |
1f55cd5b292f
rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
54 |
.append(true) |
1f55cd5b292f
rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
55 |
.open(&path) |
1f55cd5b292f
rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
56 |
.with_context(context) |
1f55cd5b292f
rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
57 |
}; |
1f55cd5b292f
rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
58 |
let mut file = open()?; |
1f55cd5b292f
rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
59 |
if let Some(max_size) = self.max_size { |
1f55cd5b292f
rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
60 |
if file.metadata().with_context(context)?.len() >= max_size { |
1f55cd5b292f
rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
61 |
// For example with `max_files == 5`, the first iteration of |
1f55cd5b292f
rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
62 |
// this loop has `i == 4` and renames `{name}.4` to `{name}.5`. |
1f55cd5b292f
rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
63 |
// The last iteration renames `{name}.1` to |
1f55cd5b292f
rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
64 |
// `{name}.2` |
1f55cd5b292f
rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
65 |
for i in (1..self.max_files).rev() { |
1f55cd5b292f
rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
66 |
self.vfs |
1f55cd5b292f
rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
67 |
.rename( |
52167
7be39c5110c9
hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents:
51864
diff
changeset
|
68 |
Path::new(&format!("{}.{}", self.name, i)), |
7be39c5110c9
hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents:
51864
diff
changeset
|
69 |
Path::new(&format!("{}.{}", self.name, i + 1)), |
7be39c5110c9
hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents:
51864
diff
changeset
|
70 |
false, |
46599
1f55cd5b292f
rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
71 |
) |
1f55cd5b292f
rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
72 |
.io_not_found_as_none()?; |
1f55cd5b292f
rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
73 |
} |
1f55cd5b292f
rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
74 |
// Then rename `{name}` to `{name}.1`. This is the |
1f55cd5b292f
rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
75 |
// previously-opened `file`. |
1f55cd5b292f
rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
76 |
self.vfs |
52167
7be39c5110c9
hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents:
51864
diff
changeset
|
77 |
.rename( |
7be39c5110c9
hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents:
51864
diff
changeset
|
78 |
Path::new(&self.name), |
7be39c5110c9
hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents:
51864
diff
changeset
|
79 |
Path::new(&format!("{}.1", self.name)), |
7be39c5110c9
hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents:
51864
diff
changeset
|
80 |
false, |
7be39c5110c9
hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents:
51864
diff
changeset
|
81 |
) |
46599
1f55cd5b292f
rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
82 |
.io_not_found_as_none()?; |
1f55cd5b292f
rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
83 |
// Finally, create a new `{name}` file and replace our `file` |
1f55cd5b292f
rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
84 |
// handle. |
1f55cd5b292f
rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
85 |
file = open()?; |
1f55cd5b292f
rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
86 |
} |
1f55cd5b292f
rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
87 |
} |
1f55cd5b292f
rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
88 |
file.write_all(bytes).with_context(context)?; |
1f55cd5b292f
rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
89 |
file.sync_all().with_context(context) |
1f55cd5b292f
rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
90 |
} |
1f55cd5b292f
rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
91 |
} |
1f55cd5b292f
rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
92 |
|
1f55cd5b292f
rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
93 |
#[test] |
1f55cd5b292f
rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
94 |
fn test_rotation() { |
1f55cd5b292f
rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
95 |
let temp = tempfile::tempdir().unwrap(); |
52167
7be39c5110c9
hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents:
51864
diff
changeset
|
96 |
let vfs = VfsImpl::new(temp.path().to_owned(), false); |
51864 | 97 |
let logger = LogFile::new(vfs.clone(), "log") |
98 |
.max_size(Some(3)) |
|
99 |
.max_files(2); |
|
46599
1f55cd5b292f
rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
100 |
logger.write(b"one\n").unwrap(); |
1f55cd5b292f
rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
101 |
logger.write(b"two\n").unwrap(); |
1f55cd5b292f
rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
102 |
logger.write(b"3\n").unwrap(); |
1f55cd5b292f
rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
103 |
logger.write(b"four\n").unwrap(); |
1f55cd5b292f
rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
104 |
logger.write(b"five\n").unwrap(); |
1f55cd5b292f
rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
105 |
assert_eq!(vfs.read("log").unwrap(), b"five\n"); |
1f55cd5b292f
rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
106 |
assert_eq!(vfs.read("log.1").unwrap(), b"3\nfour\n"); |
1f55cd5b292f
rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
107 |
assert_eq!(vfs.read("log.2").unwrap(), b"two\n"); |
1f55cd5b292f
rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
108 |
assert!(vfs.read("log.3").io_not_found_as_none().unwrap().is_none()); |
1f55cd5b292f
rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff
changeset
|
109 |
} |