Skip to main content

hyperlane_plugin_logger/
impl.rs

1use super::*;
2
3impl Default for LoggerConfig {
4    /// Builds the configuration the logger uses when none is given.
5    ///
6    /// # Returns
7    ///
8    /// - `Self` - The default configuration.
9    fn default() -> Self {
10        Self {
11            level: LevelFilter::Info,
12            file: None,
13        }
14    }
15}
16
17impl log::Log for LoggerPlugin {
18    /// Reports whether the level is written.
19    ///
20    /// # Arguments
21    ///
22    /// - `&log::Metadata<'_>` - The metadata of the record.
23    ///
24    /// # Returns
25    ///
26    /// - `bool` - Whether the record is written.
27    fn enabled(&self, _: &log::Metadata<'_>) -> bool {
28        true
29    }
30
31    /// Writes one record.
32    ///
33    /// # Arguments
34    ///
35    /// - `&log::Record<'_>` - The record to write.
36    fn log(&self, record: &log::Record<'_>) {
37        let line: String = format!("{} {}", record.level(), record.args());
38        if let Some(file) = FILE.get()
39            && let Ok(mut file) = file.lock()
40            && let Some(file) = file.as_mut()
41        {
42            let _: Result<(), Error> = writeln!(file, "{line}");
43        }
44        println!("{line}");
45    }
46
47    /// Flushes the file the log is appended to.
48    fn flush(&self) {
49        if let Some(file) = FILE.get()
50            && let Ok(mut file) = file.lock()
51            && let Some(file) = file.as_mut()
52        {
53            let _: Result<(), Error> = file.flush();
54        }
55    }
56}
57
58impl LoggerPlugin {
59    /// Installs the logger.
60    ///
61    /// # Arguments
62    ///
63    /// - `LoggerConfig` - The configuration to work from.
64    ///
65    /// # Returns
66    ///
67    /// - `Result<(), String>` - Nothing, or the reason the logger was refused.
68    pub fn init(config: LoggerConfig) -> Result<(), String> {
69        let file: Option<File> = match config.file.as_ref() {
70            Some(path) => match OpenOptions::new().create(true).append(true).open(path) {
71                Ok(file) => Some(file),
72                Err(error) => return Err(format!("the log file could not be opened: {error}")),
73            },
74            None => None,
75        };
76        CONFIG
77            .set(config)
78            .map_err(|_: LoggerConfig| "the logger configuration was already set".to_owned())?;
79        FILE.set(Mutex::new(file))
80            .map_err(|_: Mutex<Option<File>>| "the log file was already set".to_owned())?;
81        log::set_logger(&LoggerPlugin)
82            .map_err(|_: log::SetLoggerError| "a logger was already installed".to_owned())?;
83        log::set_max_level(
84            CONFIG
85                .get()
86                .map(|config: &LoggerConfig| config.level)
87                .unwrap_or(LevelFilter::Info),
88        );
89        Ok(())
90    }
91
92    /// The configuration the logger works from.
93    ///
94    /// # Returns
95    ///
96    /// - `Option<&'static LoggerConfig>` - The configuration, when it was set.
97    pub fn get() -> Option<&'static LoggerConfig> {
98        CONFIG.get()
99    }
100
101    /// Writes a line at the info level.
102    ///
103    /// # Arguments
104    ///
105    /// - `T` - The line to write.
106    pub fn log_info<T>(data: T)
107    where
108        T: AsRef<str>,
109    {
110        log::info!("{}", data.as_ref());
111    }
112
113    /// Writes a line at the error level.
114    ///
115    /// # Arguments
116    ///
117    /// - `T` - The line to write.
118    pub fn log_error<T>(data: T)
119    where
120        T: AsRef<str>,
121    {
122        log::error!("{}", data.as_ref());
123    }
124
125    /// Writes a line at the warn level.
126    ///
127    /// # Arguments
128    ///
129    /// - `T` - The line to write.
130    pub fn log_warn<T>(data: T)
131    where
132        T: AsRef<str>,
133    {
134        log::warn!("{}", data.as_ref());
135    }
136
137    /// Writes a line at the debug level.
138    ///
139    /// # Arguments
140    ///
141    /// - `T` - The line to write.
142    pub fn log_debug<T>(data: T)
143    where
144        T: AsRef<str>,
145    {
146        log::debug!("{}", data.as_ref());
147    }
148
149    /// Writes a line at the trace level.
150    ///
151    /// # Arguments
152    ///
153    /// - `T` - The line to write.
154    pub fn log_trace<T>(data: T)
155    where
156        T: AsRef<str>,
157    {
158        log::trace!("{}", data.as_ref());
159    }
160}
161
162impl ServerHook for RequestLogger {
163    /// Builds the middleware.
164    ///
165    /// # Arguments
166    ///
167    /// - `&mut Stream` - The stream the request arrived on.
168    /// - `&mut Context` - The context of the request.
169    ///
170    /// # Returns
171    ///
172    /// - `Self` - The middleware.
173    async fn new(_: &mut Stream, _: &mut Context) -> Self {
174        Self
175    }
176
177    /// Notes when the request arrived.
178    ///
179    /// # Arguments
180    ///
181    /// - `Self` - The middleware.
182    /// - `&mut Stream` - The stream the request arrived on.
183    /// - `&mut Context` - The context of the request.
184    ///
185    /// # Returns
186    ///
187    /// - `Status` - Whether the pipeline continues.
188    async fn handle(self, _: &mut Stream, ctx: &mut Context) -> Status {
189        if CONFIG.get().is_none() {
190            return Status::Continue;
191        }
192        ctx.set_attribute(ATTRIBUTE_STARTED_AT, Instant::now());
193        Status::Continue
194    }
195}
196
197impl ServerHook for ResponseLogger {
198    /// Builds the middleware.
199    ///
200    /// # Arguments
201    ///
202    /// - `&mut Stream` - The stream the request arrived on.
203    /// - `&mut Context` - The context of the request.
204    ///
205    /// # Returns
206    ///
207    /// - `Self` - The middleware.
208    async fn new(_: &mut Stream, _: &mut Context) -> Self {
209        Self
210    }
211
212    /// Writes the line of the answered request.
213    ///
214    /// # Arguments
215    ///
216    /// - `Self` - The middleware.
217    /// - `&mut Stream` - The stream the request arrived on.
218    /// - `&mut Context` - The context of the request.
219    ///
220    /// # Returns
221    ///
222    /// - `Status` - Whether the pipeline continues.
223    async fn handle(self, _: &mut Stream, ctx: &mut Context) -> Status {
224        if CONFIG.get().is_none() {
225            return Status::Continue;
226        }
227        let started_at: Option<Instant> = ctx.try_get_attribute(ATTRIBUTE_STARTED_AT);
228        let elapsed: u128 = match started_at {
229            Some(started_at) => started_at.elapsed().as_millis(),
230            None => 0,
231        };
232        let method: String = ctx.get_request().get_method().to_string();
233        let path: String = ctx.get_request().get_path().to_owned();
234        let status: usize = ctx.get_response().get_status_code();
235        log::info!(
236            "{} {} {} {}ms",
237            if method.is_empty() {
238                METHOD_UNKNOWN
239            } else {
240                method.as_str()
241            },
242            if path.is_empty() {
243                PATH_UNKNOWN
244            } else {
245                path.as_str()
246            },
247            status,
248            elapsed
249        );
250        Status::Continue
251    }
252}