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}