Skip to main content

rivet_logger/processors/
web.rs

1use std::collections::BTreeMap;
2
3use crate::logger::{BoxError, LogRecord, LogValue, Processor};
4
5pub struct Web {
6    server_data: BTreeMap<String, String>,
7    extra_fields: BTreeMap<String, String>,
8}
9
10impl Web {
11    pub fn new(
12        server_data: Option<BTreeMap<String, String>>,
13        extra_fields: Option<BTreeMap<String, String>>,
14    ) -> Self {
15        let server_data = server_data.unwrap_or_else(default_server_data);
16
17        let mut default_fields = default_extra_fields();
18        if server_data.contains_key("UNIQUE_ID") {
19            default_fields.insert("unique_id".to_string(), "UNIQUE_ID".to_string());
20        }
21
22        Self {
23            server_data,
24            extra_fields: extra_fields.unwrap_or(default_fields),
25        }
26    }
27
28    pub fn add_extra_field(
29        &mut self,
30        extra_name: impl Into<String>,
31        server_name: impl Into<String>,
32    ) {
33        self.extra_fields
34            .insert(extra_name.into(), server_name.into());
35    }
36}
37
38impl Default for Web {
39    fn default() -> Self {
40        Self::new(None, None)
41    }
42}
43
44impl Processor for Web {
45    fn process(&self, mut record: LogRecord) -> Result<LogRecord, BoxError> {
46        if !self.server_data.contains_key("REQUEST_URI") {
47            return Ok(record);
48        }
49
50        for (extra_name, server_name) in &self.extra_fields {
51            let value = self
52                .server_data
53                .get(server_name)
54                .map(|value| LogValue::String(value.clone()))
55                .unwrap_or(LogValue::Null);
56            record.extra.insert(extra_name.clone(), value);
57        }
58
59        Ok(record)
60    }
61}
62
63fn default_server_data() -> BTreeMap<String, String> {
64    std::env::vars().collect()
65}
66
67fn default_extra_fields() -> BTreeMap<String, String> {
68    BTreeMap::from([
69        ("url".to_string(), "REQUEST_URI".to_string()),
70        ("ip".to_string(), "REMOTE_ADDR".to_string()),
71        ("http_method".to_string(), "REQUEST_METHOD".to_string()),
72        ("server".to_string(), "SERVER_NAME".to_string()),
73        ("referrer".to_string(), "HTTP_REFERER".to_string()),
74    ])
75}
76
77#[cfg(test)]
78mod tests {
79    use super::*;
80    use crate::logger::{Level, LogRecord};
81    use time::OffsetDateTime;
82
83    #[test]
84    fn adds_default_web_fields_when_request_uri_exists() {
85        let mut server_data = BTreeMap::new();
86        server_data.insert("REQUEST_URI".to_string(), "/status".to_string());
87        server_data.insert("REQUEST_METHOD".to_string(), "GET".to_string());
88        let processor = Web::new(Some(server_data), None);
89        let record = LogRecord {
90            datetime: OffsetDateTime::now_utc(),
91            channel: "test".to_string(),
92            level: Level::Info,
93            message: "msg".to_string(),
94            context: BTreeMap::new(),
95            extra: BTreeMap::new(),
96        };
97
98        let processed = processor.process(record).expect("processor should run");
99        assert!(processed.extra.contains_key("url"));
100        assert!(processed.extra.contains_key("http_method"));
101    }
102}