]>
Commit | Line | Data |
---|---|---|
1e80fb8e | 1 | use anyhow::Error; |
3b151414 DM |
2 | use std::io::Write; |
3 | ||
081c37cc | 4 | /// Log messages with optional automatically added timestamps into files |
3b151414 | 5 | /// |
add5861e | 6 | /// Logs messages to file, and optionally to standard output. |
3b151414 DM |
7 | /// |
8 | /// | |
9 | /// #### Example: | |
10 | /// ``` | |
11 | /// #[macro_use] extern crate proxmox_backup; | |
f7d4e4b5 | 12 | /// # use anyhow::{bail, format_err, Error}; |
081c37cc | 13 | /// use proxmox_backup::tools::{FileLogger, FileLogOptions}; |
3b151414 | 14 | /// |
244abab7 | 15 | /// # std::fs::remove_file("test.log"); |
081c37cc TL |
16 | /// let options = FileLogOptions { |
17 | /// to_stdout: true, | |
18 | /// exclusive: true, | |
19 | /// ..Default::default() | |
20 | /// }; | |
21 | /// let mut log = FileLogger::new("test.log", options).unwrap(); | |
3b151414 DM |
22 | /// flog!(log, "A simple log: {}", "Hello!"); |
23 | /// ``` | |
24 | ||
c0df91f8 TL |
25 | #[derive(Debug, Default)] |
26 | /// Options to control the behavior of a ['FileLogger'] instance | |
27 | pub struct FileLogOptions { | |
081c37cc TL |
28 | /// Open underlying log file in append mode, useful when multiple concurrent processes |
29 | /// want to log to the same file (e.g., HTTP access log). Note that it is only atomic | |
30 | /// for writes smaller than the PIPE_BUF (4k on Linux). | |
31 | /// Inside the same process you may need to still use an mutex, for shared access. | |
c0df91f8 TL |
32 | pub append: bool, |
33 | /// Open underlying log file as readable | |
34 | pub read: bool, | |
35 | /// If set, ensure that the file is newly created or error out if already existing. | |
36 | pub exclusive: bool, | |
37 | /// Duplicate logged messages to STDOUT, like tee | |
38 | pub to_stdout: bool, | |
39 | /// Prefix messages logged to the file with the current local time as RFC 3339 | |
40 | pub prefix_time: bool, | |
41 | } | |
3b151414 | 42 | |
3489936e | 43 | #[derive(Debug)] |
3b151414 DM |
44 | pub struct FileLogger { |
45 | file: std::fs::File, | |
c0df91f8 | 46 | options: FileLogOptions, |
3b151414 DM |
47 | } |
48 | ||
49 | /// Log messages to [FileLogger](tools/struct.FileLogger.html) | |
50 | #[macro_export] | |
51 | macro_rules! flog { | |
52 | ($log:expr, $($arg:tt)*) => ({ | |
53 | $log.log(format!($($arg)*)); | |
54 | }) | |
55 | } | |
56 | ||
57 | impl FileLogger { | |
c0df91f8 TL |
58 | pub fn new<P: AsRef<std::path::Path>>( |
59 | file_name: P, | |
60 | options: FileLogOptions, | |
61 | ) -> Result<Self, Error> { | |
3b151414 | 62 | let file = std::fs::OpenOptions::new() |
c0df91f8 | 63 | .read(options.read) |
3b151414 | 64 | .write(true) |
c0df91f8 TL |
65 | .append(options.append) |
66 | .create_new(options.exclusive) | |
67 | .create(!options.exclusive) | |
3b151414 DM |
68 | .open(file_name)?; |
69 | ||
c0df91f8 | 70 | Ok(Self { file, options }) |
3b151414 DM |
71 | } |
72 | ||
73 | pub fn log<S: AsRef<str>>(&mut self, msg: S) { | |
3b151414 DM |
74 | let msg = msg.as_ref(); |
75 | ||
c0df91f8 TL |
76 | if self.options.to_stdout { |
77 | let mut stdout = std::io::stdout(); | |
175eeb87 WB |
78 | stdout.write_all(msg.as_bytes()).unwrap(); |
79 | stdout.write_all(b"\n").unwrap(); | |
3b151414 DM |
80 | } |
81 | ||
6a7be83e DM |
82 | let now = proxmox::tools::time::epoch_i64(); |
83 | let rfc3339 = proxmox::tools::time::epoch_to_rfc3339(now).unwrap(); | |
84 | ||
c0df91f8 TL |
85 | let line = if self.options.prefix_time { |
86 | format!("{}: {}\n", rfc3339, msg) | |
87 | } else { | |
88 | format!("{}\n", msg) | |
89 | }; | |
175eeb87 | 90 | self.file.write_all(line.as_bytes()).unwrap(); |
3b151414 DM |
91 | } |
92 | } | |
93 | ||
94 | impl std::io::Write for FileLogger { | |
95 | fn write(&mut self, buf: &[u8]) -> Result<usize, std::io::Error> { | |
c0df91f8 TL |
96 | if self.options.to_stdout { |
97 | let _ = std::io::stdout().write(buf); | |
98 | } | |
3b151414 DM |
99 | self.file.write(buf) |
100 | } | |
101 | ||
102 | fn flush(&mut self) -> Result<(), std::io::Error> { | |
c0df91f8 TL |
103 | if self.options.to_stdout { |
104 | let _ = std::io::stdout().flush(); | |
105 | } | |
3b151414 DM |
106 | self.file.flush() |
107 | } | |
108 | } |