rust_loguru/formatters/
json.rs1use crate::formatters::{FormatFn, FormatterTrait};
2use crate::record::Record;
3use serde_json;
4use serde_json::json;
5use std::fmt;
6use std::sync::Arc;
7
8#[derive(Clone)]
10pub struct JsonFormatter {
11 use_colors: bool,
13 include_timestamp: bool,
15 include_level: bool,
17 include_module: bool,
19 include_location: bool,
21 format_fn: Option<FormatFn>,
23}
24
25impl fmt::Debug for JsonFormatter {
26 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
27 f.debug_struct("JsonFormatter")
28 .field("use_colors", &self.use_colors)
29 .field("include_timestamp", &self.include_timestamp)
30 .field("include_level", &self.include_level)
31 .field("include_module", &self.include_module)
32 .field("include_location", &self.include_location)
33 .field("format_fn", &"<format_fn>")
34 .finish()
35 }
36}
37
38impl Default for JsonFormatter {
39 fn default() -> Self {
40 Self {
41 use_colors: true,
42 include_timestamp: true,
43 include_level: true,
44 include_module: true,
45 include_location: true,
46 format_fn: None,
47 }
48 }
49}
50
51impl JsonFormatter {
52 pub fn new() -> Self {
53 Self::default()
54 }
55
56 pub fn with_colors(self, _use_colors: bool) -> Self {
57 self
58 }
59
60 pub fn with_timestamp(mut self, include_timestamp: bool) -> Self {
61 self.include_timestamp = include_timestamp;
62 self
63 }
64
65 pub fn with_level(mut self, include_level: bool) -> Self {
66 self.include_level = include_level;
67 self
68 }
69
70 pub fn with_module(mut self, include_module: bool) -> Self {
71 self.include_module = include_module;
72 self
73 }
74
75 pub fn with_location(mut self, include_location: bool) -> Self {
76 self.include_location = include_location;
77 self
78 }
79
80 pub fn with_format<F>(mut self, format_fn: F) -> Self
81 where
82 F: Fn(&Record) -> String + Send + Sync + 'static,
83 {
84 self.format_fn = Some(Arc::new(format_fn));
85 self
86 }
87}
88
89impl FormatterTrait for JsonFormatter {
90 fn fmt(&self, record: &Record) -> String {
91 if let Some(format_fn) = &self.format_fn {
92 return format_fn(record);
93 }
94
95 let mut json = json!({
96 "message": record.message(),
97 });
98
99 if self.include_timestamp {
100 json["timestamp"] = json!(record.timestamp().to_rfc3339());
101 }
102 if self.include_level {
103 json["level"] = json!(record.level().to_string());
104 }
105 if self.include_module {
106 json["module"] = json!(record.module());
107 }
108 if self.include_location {
109 json["location"] = json!(record.location().to_string());
110 }
111
112 json.to_string()
113 }
114
115 fn with_colors(&mut self, use_colors: bool) {
116 self.use_colors = use_colors;
117 }
118
119 fn with_timestamp(&mut self, include_timestamp: bool) {
120 self.include_timestamp = include_timestamp;
121 }
122
123 fn with_level(&mut self, include_level: bool) {
124 self.include_level = include_level;
125 }
126
127 fn with_module(&mut self, include_module: bool) {
128 self.include_module = include_module;
129 }
130
131 fn with_location(&mut self, include_location: bool) {
132 self.include_location = include_location;
133 }
134
135 fn with_pattern(&mut self, _pattern: String) {
136 }
138
139 fn with_format(&mut self, format_fn: FormatFn) {
140 self.format_fn = Some(format_fn);
141 }
142
143 fn box_clone(&self) -> Box<dyn FormatterTrait + Send + Sync> {
144 Box::new(self.clone())
145 }
146}
147
148#[cfg(test)]
149mod tests {
150 use super::*;
151 use crate::level::LogLevel;
152
153 #[test]
154 fn test_json_formatter_default() {
155 let formatter = JsonFormatter::default();
156 let record = Record::new(
157 LogLevel::Info,
158 "Test message",
159 Some("test".to_string()),
160 Some("test.rs".to_string()),
161 Some(42),
162 );
163
164 let formatted = formatter.fmt(&record);
165 assert!(formatted.contains("Test message"));
166 assert!(formatted.contains("INFO"));
167 assert!(formatted.contains("test"));
168 assert!(formatted.contains("test.rs:42"));
169 }
170
171 #[test]
172 fn test_json_formatter_no_timestamp() {
173 let formatter = JsonFormatter::default().with_timestamp(false);
174 let record = Record::new(
175 LogLevel::Info,
176 "Test message",
177 Some("test".to_string()),
178 Some("test.rs".to_string()),
179 Some(42),
180 );
181
182 let formatted = formatter.fmt(&record);
183 assert!(formatted.contains("Test message"));
184 assert!(formatted.contains("INFO"));
185 assert!(formatted.contains("test"));
186 assert!(formatted.contains("test.rs:42"));
187 assert!(!formatted.contains("timestamp"));
188 }
189
190 #[test]
191 fn test_json_formatter_no_level() {
192 let formatter = JsonFormatter::default().with_level(false);
193 let record = Record::new(
194 LogLevel::Info,
195 "Test message",
196 Some("test".to_string()),
197 Some("test.rs".to_string()),
198 Some(42),
199 );
200
201 let formatted = formatter.fmt(&record);
202 assert!(formatted.contains("Test message"));
203 assert!(!formatted.contains("INFO"));
204 assert!(formatted.contains("test"));
205 assert!(formatted.contains("test.rs:42"));
206 }
207
208 #[test]
209 fn test_json_formatter_no_module() {
210 let formatter = JsonFormatter::default().with_module(false);
211 let record = Record::new(
212 LogLevel::Info,
213 "Test message",
214 Some("test".to_string()),
215 Some("main.rs".to_string()),
216 Some(42),
217 );
218
219 let formatted = formatter.fmt(&record);
220 assert!(formatted.contains("Test message"));
221 assert!(formatted.contains("INFO"));
222 assert!(!formatted.contains("test"));
223 assert!(formatted.contains("main.rs:42"));
224 }
225
226 #[test]
227 fn test_json_formatter_no_location() {
228 let formatter = JsonFormatter::default().with_location(false);
229 let record = Record::new(
230 LogLevel::Info,
231 "Test message",
232 Some("test".to_string()),
233 Some("test.rs".to_string()),
234 Some(42),
235 );
236
237 let formatted = formatter.fmt(&record);
238 assert!(formatted.contains("Test message"));
239 assert!(formatted.contains("INFO"));
240 assert!(formatted.contains("test"));
241 }
242
243 #[test]
244 fn test_json_formatter_custom_format() {
245 let formatter =
246 JsonFormatter::default().with_format(|record| format!("CUSTOM: {}", record.message()));
247 let record = Record::new(
248 LogLevel::Info,
249 "Test message",
250 Some("test".to_string()),
251 Some("test.rs".to_string()),
252 Some(42),
253 );
254
255 let formatted = formatter.fmt(&record);
256 assert_eq!(formatted, "CUSTOM: Test message");
257 }
258}