1use crate::context::Context;
4use crate::redaction::redact_field;
5use crate::schema::{Event, Level, StandardFields};
6use tracing::{event, Level as TracingLevel};
7use tracing_subscriber::{
8 fmt::{self, time::ChronoUtc},
9 layer::SubscriberExt,
10 util::SubscriberInitExt,
11 EnvFilter, Registry,
12};
13
14#[derive(Debug, thiserror::Error)]
16pub enum LoggerError {
17 #[error("Failed to initialize logger: {0}")]
18 Initialization(String),
19}
20
21pub fn init_logger(service_name: &str, default_level: &str) -> Result<(), LoggerError> {
36 let env_filter = EnvFilter::try_from_default_env()
37 .unwrap_or_else(|_| EnvFilter::new(default_level));
38
39 let fmt_layer = fmt::layer()
40 .json()
41 .with_timer(ChronoUtc::rfc_3339())
42 .with_target(false)
43 .with_current_span(false)
44 .with_span_list(false)
45 .with_file(false)
46 .with_line_number(false)
47 .with_writer(std::io::stdout);
48
49 Registry::default()
50 .with(env_filter)
51 .with(fmt_layer)
52 .try_init()
53 .map_err(|e| LoggerError::Initialization(e.to_string()))?;
54
55 std::env::set_var("LOG_SERVICE_NAME", service_name);
57
58 Ok(())
59}
60
61pub struct Logger {
63 service_name: String,
64 module: Option<String>,
65 context: Context,
66}
67
68impl Logger {
69 pub fn new(module: impl Into<Option<String>>) -> Self {
84 let service_name = std::env::var("LOG_SERVICE_NAME")
85 .unwrap_or_else(|_| "unknown-service".to_string());
86
87 Self {
88 service_name,
89 module: module.into(),
90 context: Context::new(),
91 }
92 }
93
94 pub fn with_context(module: impl Into<Option<String>>, context: Context) -> Self {
96 let service_name = std::env::var("LOG_SERVICE_NAME")
97 .unwrap_or_else(|_| "unknown-service".to_string());
98
99 Self {
100 service_name,
101 module: module.into(),
102 context,
103 }
104 }
105
106 pub fn set_context(&mut self, context: Context) {
108 self.context = context;
109 }
110
111 pub fn merge_context(&mut self, context: Context) {
113 self.context = self.context.clone().merge(context);
114 }
115
116 pub fn trace(&self, message: impl Into<String>) {
118 self.log(Level::Trace, message, None::<fn(&mut EventBuilder)>);
119 }
120
121 pub fn debug(&self, message: impl Into<String>) {
123 self.log(Level::Debug, message, None::<fn(&mut EventBuilder)>);
124 }
125
126 pub fn info(&self, message: impl Into<String>) {
128 self.log(Level::Info, message, None::<fn(&mut EventBuilder)>);
129 }
130
131 pub fn warn(&self, message: impl Into<String>) {
133 self.log(Level::Warn, message, None::<fn(&mut EventBuilder)>);
134 }
135
136 pub fn error(&self, message: impl Into<String>) {
138 self.log(Level::Error, message, None::<fn(&mut EventBuilder)>);
139 }
140
141 pub fn trace_with<F>(&self, message: impl Into<String>, f: F)
143 where
144 F: FnOnce(&mut EventBuilder),
145 {
146 self.log(Level::Trace, message, Some(f));
147 }
148
149 pub fn debug_with<F>(&self, message: impl Into<String>, f: F)
151 where
152 F: FnOnce(&mut EventBuilder),
153 {
154 self.log(Level::Debug, message, Some(f));
155 }
156
157 pub fn info_with<F>(&self, message: impl Into<String>, f: F)
159 where
160 F: FnOnce(&mut EventBuilder),
161 {
162 self.log(Level::Info, message, Some(f));
163 }
164
165 pub fn warn_with<F>(&self, message: impl Into<String>, f: F)
167 where
168 F: FnOnce(&mut EventBuilder),
169 {
170 self.log(Level::Warn, message, Some(f));
171 }
172
173 pub fn error_with<F>(&self, message: impl Into<String>, f: F)
175 where
176 F: FnOnce(&mut EventBuilder),
177 {
178 self.log(Level::Error, message, Some(f));
179 }
180
181 pub fn log_error(&self, message: impl Into<String>, error: &dyn std::error::Error) {
183 self.error_with(message, |e| {
184 e.error(error);
185 });
186 }
187
188 fn log<F>(&self, level: Level, message: impl Into<String>, fields_fn: Option<F>)
190 where
191 F: FnOnce(&mut EventBuilder),
192 {
193 let message = message.into();
194 let tracing_level = level.to_tracing_level();
195
196 let mut builder = EventBuilder::new();
198
199 builder.field(StandardFields::SERVICE, &self.service_name);
201 if let Some(ref module) = self.module {
202 builder.field(StandardFields::MODULE, module);
203 }
204 builder.field(StandardFields::LEVEL, level.to_string());
205 builder.field(StandardFields::MESSAGE, &message);
206
207 for (key, value) in self.context.to_fields() {
209 builder.field(key, &value);
210 }
211
212 if let Some(f) = fields_fn {
214 f(&mut builder);
215 }
216
217 let fields = builder.build();
219 let fields_map = fields.as_object().unwrap();
220
221 self.log_with_fields(tracing_level, &message, fields_map);
223 }
224
225 fn log_with_fields(
227 &self,
228 level: TracingLevel,
229 message: &str,
230 fields: &serde_json::Map<String, serde_json::Value>,
231 ) {
232 let fields_json = serde_json::to_string(fields).unwrap_or_else(|_| "{}".to_string());
235
236 match level {
240 TracingLevel::TRACE => {
241 event!(
242 TracingLevel::TRACE,
243 message = %message,
244 service = %self.service_name,
245 fields = %fields_json
246 );
247 }
248 TracingLevel::DEBUG => {
249 event!(
250 TracingLevel::DEBUG,
251 message = %message,
252 service = %self.service_name,
253 fields = %fields_json
254 );
255 }
256 TracingLevel::INFO => {
257 event!(
258 TracingLevel::INFO,
259 message = %message,
260 service = %self.service_name,
261 fields = %fields_json
262 );
263 }
264 TracingLevel::WARN => {
265 event!(
266 TracingLevel::WARN,
267 message = %message,
268 service = %self.service_name,
269 fields = %fields_json
270 );
271 }
272 TracingLevel::ERROR => {
273 event!(
274 TracingLevel::ERROR,
275 message = %message,
276 service = %self.service_name,
277 fields = %fields_json
278 );
279 }
280 }
281 }
282}
283
284
285pub struct EventBuilder {
287 fields: serde_json::Map<String, serde_json::Value>,
288}
289
290impl EventBuilder {
291 pub fn new() -> Self {
293 Self {
294 fields: serde_json::Map::new(),
295 }
296 }
297
298 pub fn field(&mut self, name: &str, value: impl ToString) -> &mut Self {
302 let value_str = value.to_string();
303 let redacted = redact_field(name, &value_str);
304 self.fields.insert(name.to_string(), serde_json::Value::String(redacted));
305 self
306 }
307
308 pub fn field_raw(&mut self, name: &str, value: impl Into<serde_json::Value>) -> &mut Self {
310 self.fields.insert(name.to_string(), value.into());
311 self
312 }
313
314 pub fn error(&mut self, error: &dyn std::error::Error) -> &mut Self {
316 let error_obj = Event::format_error(error);
317 self.fields.insert(StandardFields::ERROR.to_string(), error_obj);
318 self
319 }
320
321 pub fn build(self) -> serde_json::Value {
323 serde_json::Value::Object(self.fields)
324 }
325}
326
327impl Default for EventBuilder {
328 fn default() -> Self {
329 Self::new()
330 }
331}
332
333#[cfg(test)]
334mod tests {
335 use super::*;
336
337 #[test]
338 fn test_event_builder() {
339 let mut builder = EventBuilder::new();
340 builder.field("user_id", "user123");
341 builder.field("action", "login");
342
343 let fields = builder.build();
344 assert!(fields.is_object());
345 }
346}