rust/hg-core/src/logging.rs
author Rapha?l Gom?s <rgomes@octobus.net>
Tue, 05 Nov 2024 15:21:09 +0100
branchstable
changeset 52186 e6a44bc91bc2
parent 51864 db7dbe6f7bb2
child 52167 7be39c5110c9
permissions -rw-r--r--
rust-update: make `update_from_null` respect `worker.numcpu` config option This was overlooked in the original series. This is important for tests (because we run many at once), and for the occasional end user that wants to keep their CPU usage in check. A future series should clean up this `worker` parameter tunelling business by rewriting the config in Rust, but doing so on stable would be a very bad idea.
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
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};
51864
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 47952
diff changeset
     2
use crate::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;
1f55cd5b292f rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
     4
1f55cd5b292f rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
     5
/// 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
     6
/// 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
     7
///
1f55cd5b292f rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
     8
/// 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
     9
/// "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
    10
/// files.
1f55cd5b292f rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    11
pub struct LogFile<'a> {
51864
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 47952
diff changeset
    12
    vfs: VfsImpl,
46599
1f55cd5b292f rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    13
    name: &'a str,
1f55cd5b292f rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    14
    max_size: Option<u64>,
1f55cd5b292f rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    15
    max_files: u32,
1f55cd5b292f rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    16
}
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
impl<'a> LogFile<'a> {
51864
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 47952
diff changeset
    19
    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
    20
        Self {
1f55cd5b292f rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    21
            vfs,
1f55cd5b292f rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    22
            name,
1f55cd5b292f rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    23
            max_size: None,
1f55cd5b292f rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    24
            max_files: 0,
1f55cd5b292f rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    25
        }
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
    /// 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
    29
    /// given size, in bytes. `None` disables rotation.
1f55cd5b292f rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    30
    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
    31
        self.max_size = value;
1f55cd5b292f rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    32
        self
1f55cd5b292f rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    33
    }
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
    /// 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
    36
    /// addition to the original `{name}` file.
1f55cd5b292f rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    37
    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
    38
        self.max_files = value;
1f55cd5b292f rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    39
        self
1f55cd5b292f rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    40
    }
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
    /// 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
    43
    /// needed.
1f55cd5b292f rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    44
    ///
1f55cd5b292f rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    45
    /// 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
    46
    /// desired.
1f55cd5b292f rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    47
    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
    48
        let path = self.vfs.join(self.name);
1f55cd5b292f rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    49
        let context = || IoErrorContext::WritingFile(path.clone());
1f55cd5b292f rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    50
        let open = || {
1f55cd5b292f rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    51
            std::fs::OpenOptions::new()
1f55cd5b292f rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    52
                .create(true)
1f55cd5b292f rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    53
                .append(true)
1f55cd5b292f rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    54
                .open(&path)
1f55cd5b292f rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    55
                .with_context(context)
1f55cd5b292f rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    56
        };
1f55cd5b292f rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    57
        let mut file = open()?;
1f55cd5b292f rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    58
        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
    59
            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
    60
                // 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
    61
                // 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
    62
                // The last iteration renames `{name}.1` to
1f55cd5b292f rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    63
                // `{name}.2`
1f55cd5b292f rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    64
                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
    65
                    self.vfs
1f55cd5b292f rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    66
                        .rename(
1f55cd5b292f rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    67
                            format!("{}.{}", self.name, i),
1f55cd5b292f rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    68
                            format!("{}.{}", self.name, i + 1),
1f55cd5b292f rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    69
                        )
1f55cd5b292f rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    70
                        .io_not_found_as_none()?;
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
                // 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
    73
                // previously-opened `file`.
1f55cd5b292f rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    74
                self.vfs
1f55cd5b292f rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    75
                    .rename(self.name, format!("{}.1", self.name))
1f55cd5b292f rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    76
                    .io_not_found_as_none()?;
1f55cd5b292f rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    77
                // 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
    78
                // handle.
1f55cd5b292f rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    79
                file = open()?;
1f55cd5b292f rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    80
            }
1f55cd5b292f rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    81
        }
1f55cd5b292f rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    82
        file.write_all(bytes).with_context(context)?;
1f55cd5b292f rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    83
        file.sync_all().with_context(context)
1f55cd5b292f rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    84
    }
1f55cd5b292f rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    85
}
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
#[test]
1f55cd5b292f rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    88
fn test_rotation() {
1f55cd5b292f rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    89
    let temp = tempfile::tempdir().unwrap();
51864
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 47952
diff changeset
    90
    let vfs = VfsImpl {
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 47952
diff changeset
    91
        base: temp.path().to_owned(),
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 47952
diff changeset
    92
    };
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 47952
diff changeset
    93
    let logger = LogFile::new(vfs.clone(), "log")
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 47952
diff changeset
    94
        .max_size(Some(3))
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 47952
diff changeset
    95
        .max_files(2);
46599
1f55cd5b292f rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    96
    logger.write(b"one\n").unwrap();
1f55cd5b292f rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    97
    logger.write(b"two\n").unwrap();
1f55cd5b292f rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    98
    logger.write(b"3\n").unwrap();
1f55cd5b292f rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    99
    logger.write(b"four\n").unwrap();
1f55cd5b292f rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   100
    logger.write(b"five\n").unwrap();
1f55cd5b292f rust: Add a log file rotation utility
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   101
    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
   102
    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
   103
    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
   104
    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
   105
}