Coverage Report

Created: 2025-08-28 06:06

/rust/registry/src/index.crates.io-6f17d22bba15001f/syslog-6.0.1/src/format.rs
Line
Count
Source (jump to first uncovered line)
1
use std::collections::HashMap;
2
use std::fmt::Display;
3
use std::io::Write;
4
use time;
5
6
use errors::*;
7
use facility::Facility;
8
use get_hostname;
9
use get_process_info;
10
use Priority;
11
12
#[allow(non_camel_case_types)]
13
#[derive(Copy, Clone)]
14
pub enum Severity {
15
    LOG_EMERG,
16
    LOG_ALERT,
17
    LOG_CRIT,
18
    LOG_ERR,
19
    LOG_WARNING,
20
    LOG_NOTICE,
21
    LOG_INFO,
22
    LOG_DEBUG,
23
}
24
25
pub trait LogFormat<T> {
26
    fn format<W: Write>(&self, w: &mut W, severity: Severity, message: T) -> Result<()>;
27
28
0
    fn emerg<W: Write>(&mut self, w: &mut W, message: T) -> Result<()> {
29
0
        self.format(w, Severity::LOG_EMERG, message)
30
0
    }
31
32
0
    fn alert<W: Write>(&mut self, w: &mut W, message: T) -> Result<()> {
33
0
        self.format(w, Severity::LOG_ALERT, message)
34
0
    }
35
36
0
    fn crit<W: Write>(&mut self, w: &mut W, message: T) -> Result<()> {
37
0
        self.format(w, Severity::LOG_CRIT, message)
38
0
    }
39
40
0
    fn err<W: Write>(&mut self, w: &mut W, message: T) -> Result<()> {
41
0
        self.format(w, Severity::LOG_ERR, message)
42
0
    }
43
44
0
    fn warning<W: Write>(&mut self, w: &mut W, message: T) -> Result<()> {
45
0
        self.format(w, Severity::LOG_WARNING, message)
46
0
    }
47
48
0
    fn notice<W: Write>(&mut self, w: &mut W, message: T) -> Result<()> {
49
0
        self.format(w, Severity::LOG_NOTICE, message)
50
0
    }
51
52
0
    fn info<W: Write>(&mut self, w: &mut W, message: T) -> Result<()> {
53
0
        self.format(w, Severity::LOG_INFO, message)
54
0
    }
55
56
0
    fn debug<W: Write>(&mut self, w: &mut W, message: T) -> Result<()> {
57
0
        self.format(w, Severity::LOG_DEBUG, message)
58
0
    }
59
}
60
61
#[derive(Clone, Debug)]
62
pub struct Formatter3164 {
63
    pub facility: Facility,
64
    pub hostname: Option<String>,
65
    pub process: String,
66
    pub pid: u32,
67
}
68
69
impl<T: Display> LogFormat<T> for Formatter3164 {
70
0
    fn format<W: Write>(&self, w: &mut W, severity: Severity, message: T) -> Result<()> {
71
0
        let format =
72
0
            time::format_description::parse("[month repr:short] [day] [hour]:[minute]:[second]")
73
0
                .unwrap();
74
75
0
        if let Some(ref hostname) = self.hostname {
76
0
            write!(
77
0
                w,
78
0
                "<{}>{} {} {}[{}]: {}",
79
0
                encode_priority(severity, self.facility),
80
0
                now_local()
81
0
                    .map(|timestamp| timestamp.format(&format).unwrap())
Unexecuted instantiation: <syslog::format::Formatter3164 as syslog::format::LogFormat<alloc::string::String>>::format::<std::io::stdio::StderrLock>::{closure#2}
Unexecuted instantiation: <syslog::format::Formatter3164 as syslog::format::LogFormat<alloc::string::String>>::format::<syslog::LoggerBackend>::{closure#2}
82
0
                    .unwrap(),
83
0
                hostname,
84
0
                self.process,
85
0
                self.pid,
86
0
                message
87
0
            )
88
0
            .chain_err(|| ErrorKind::Format)
Unexecuted instantiation: <syslog::format::Formatter3164 as syslog::format::LogFormat<alloc::string::String>>::format::<std::io::stdio::StderrLock>::{closure#0}
Unexecuted instantiation: <syslog::format::Formatter3164 as syslog::format::LogFormat<alloc::string::String>>::format::<syslog::LoggerBackend>::{closure#0}
89
        } else {
90
0
            write!(
91
0
                w,
92
0
                "<{}>{} {}[{}]: {}",
93
0
                encode_priority(severity, self.facility),
94
0
                now_local()
95
0
                    .map(|timestamp| timestamp.format(&format).unwrap())
Unexecuted instantiation: <syslog::format::Formatter3164 as syslog::format::LogFormat<alloc::string::String>>::format::<std::io::stdio::StderrLock>::{closure#3}
Unexecuted instantiation: <syslog::format::Formatter3164 as syslog::format::LogFormat<alloc::string::String>>::format::<syslog::LoggerBackend>::{closure#3}
96
0
                    .unwrap(),
97
0
                self.process,
98
0
                self.pid,
99
0
                message
100
0
            )
101
0
            .chain_err(|| ErrorKind::Format)
Unexecuted instantiation: <syslog::format::Formatter3164 as syslog::format::LogFormat<alloc::string::String>>::format::<std::io::stdio::StderrLock>::{closure#1}
Unexecuted instantiation: <syslog::format::Formatter3164 as syslog::format::LogFormat<alloc::string::String>>::format::<syslog::LoggerBackend>::{closure#1}
102
        }
103
0
    }
Unexecuted instantiation: <syslog::format::Formatter3164 as syslog::format::LogFormat<alloc::string::String>>::format::<std::io::stdio::StderrLock>
Unexecuted instantiation: <syslog::format::Formatter3164 as syslog::format::LogFormat<alloc::string::String>>::format::<syslog::LoggerBackend>
104
}
105
106
impl Default for Formatter3164 {
107
    /// Returns a `Formatter3164` with default settings.
108
    ///
109
    /// The default settings are as follows:
110
    ///
111
    /// * `facility`: `LOG_USER`, as [specified by POSIX].
112
    /// * `hostname`: Automatically detected using [the `hostname` crate], if possible.
113
    /// * `process`: Automatically detected using [`std::env::current_exe`], or if that fails, an empty string.
114
    /// * `pid`: Automatically detected using [`libc::getpid`].
115
    ///
116
    /// [`libc::getpid`]: https://docs.rs/libc/0.2/libc/fn.getpid.html
117
    /// [specified by POSIX]: https://pubs.opengroup.org/onlinepubs/9699919799/functions/closelog.html
118
    /// [`std::env::current_exe`]: https://doc.rust-lang.org/std/env/fn.current_exe.html
119
    /// [the `hostname` crate]: https://crates.io/crates/hostname
120
0
    fn default() -> Self {
121
0
        let (process, pid) = get_process_info().unwrap_or((String::new(), std::process::id()));
122
0
        let hostname = get_hostname().ok();
123
0
124
0
        Self {
125
0
            facility: Default::default(),
126
0
            hostname,
127
0
            process,
128
0
            pid,
129
0
        }
130
0
    }
131
}
132
133
/// RFC 5424 structured data
134
pub type StructuredData = HashMap<String, HashMap<String, String>>;
135
136
#[derive(Clone, Debug)]
137
pub struct Formatter5424 {
138
    pub facility: Facility,
139
    pub hostname: Option<String>,
140
    pub process: String,
141
    pub pid: u32,
142
}
143
144
impl Formatter5424 {
145
0
    pub fn format_5424_structured_data(&self, data: StructuredData) -> String {
146
0
        if data.is_empty() {
147
0
            "-".to_string()
148
        } else {
149
0
            let mut res = String::new();
150
0
            for (id, params) in &data {
151
0
                res = res + "[" + id;
152
0
                for (name, value) in params {
153
0
                    res = res + " " + name + "=\"" + value + "\"";
154
0
                }
155
0
                res += "]";
156
            }
157
158
0
            res
159
        }
160
0
    }
161
}
162
163
impl<T: Display> LogFormat<(u32, StructuredData, T)> for Formatter5424 {
164
0
    fn format<W: Write>(
165
0
        &self,
166
0
        w: &mut W,
167
0
        severity: Severity,
168
0
        log_message: (u32, StructuredData, T),
169
0
    ) -> Result<()> {
170
0
        let (message_id, data, message) = log_message;
171
0
172
0
        write!(
173
0
            w,
174
0
            "<{}>1 {} {} {} {} {} {} {}", // v1
175
0
            encode_priority(severity, self.facility),
176
0
            time::OffsetDateTime::now_utc()
177
0
                .format(&time::format_description::well_known::Rfc3339)
178
0
                .unwrap(),
179
0
            self.hostname
180
0
                .as_ref()
181
0
                .map(|x| &x[..])
182
0
                .unwrap_or("localhost"),
183
0
            self.process,
184
0
            self.pid,
185
0
            message_id,
186
0
            self.format_5424_structured_data(data),
187
0
            message
188
0
        )
189
0
        .chain_err(|| ErrorKind::Format)
190
0
    }
191
}
192
193
impl Default for Formatter5424 {
194
    /// Returns a `Formatter5424` with default settings.
195
    ///
196
    /// The default settings are as follows:
197
    ///
198
    /// * `facility`: `LOG_USER`, as [specified by POSIX].
199
    /// * `hostname`: Automatically detected using [the `hostname` crate], if possible.
200
    /// * `process`: Automatically detected using [`std::env::current_exe`], or if that fails, an empty string.
201
    /// * `pid`: Automatically detected using [`libc::getpid`].
202
    ///
203
    /// [`libc::getpid`]: https://docs.rs/libc/0.2/libc/fn.getpid.html
204
    /// [specified by POSIX]: https://pubs.opengroup.org/onlinepubs/9699919799/functions/closelog.html
205
    /// [`std::env::current_exe`]: https://doc.rust-lang.org/std/env/fn.current_exe.html
206
    /// [the `hostname` crate]: https://crates.io/crates/hostname
207
0
    fn default() -> Self {
208
0
        // Get the defaults from `Formatter3164` and move them over.
209
0
        let Formatter3164 {
210
0
            facility,
211
0
            hostname,
212
0
            process,
213
0
            pid,
214
0
        } = Default::default();
215
0
        Self {
216
0
            facility,
217
0
            hostname,
218
0
            process,
219
0
            pid,
220
0
        }
221
0
    }
222
}
223
224
0
fn encode_priority(severity: Severity, facility: Facility) -> Priority {
225
0
    facility as u8 | severity as u8
226
0
}
227
228
#[cfg(unix)]
229
// On unix platforms, time::OffsetDateTime::now_local always returns an error so use UTC instead
230
// https://github.com/time-rs/time/issues/380
231
0
fn now_local() -> std::result::Result<time::OffsetDateTime, time::error::IndeterminateOffset> {
232
0
    Ok(time::OffsetDateTime::now_utc())
233
0
}
234
235
#[cfg(not(unix))]
236
fn now_local() -> std::result::Result<time::OffsetDateTime, time::error::IndeterminateOffset> {
237
    time::OffsetDateTime::now_local()
238
}
239
240
#[test]
241
fn test_formatter3164_defaults() {
242
    let d = Formatter3164::default();
243
244
    // `Facility` doesn't implement `PartialEq`, so we use a `match` instead.
245
    assert!(match d.facility {
246
        Facility::LOG_USER => true,
247
        _ => false,
248
    });
249
250
    assert!(match &d.hostname {
251
        Some(hostname) => !hostname.is_empty(),
252
        None => false,
253
    });
254
255
    assert!(!d.process.is_empty());
256
257
    // Can't really make any assertions about the pid.
258
}
259
260
#[test]
261
fn test_formatter5424_defaults() {
262
    let d = Formatter5424::default();
263
264
    // `Facility` doesn't implement `PartialEq`, so we use a `match` instead.
265
    assert!(match d.facility {
266
        Facility::LOG_USER => true,
267
        _ => false,
268
    });
269
270
    assert!(match &d.hostname {
271
        Some(hostname) => !hostname.is_empty(),
272
        None => false,
273
    });
274
275
    assert!(!d.process.is_empty());
276
277
    // Can't really make any assertions about the pid.
278
}