rs_utils/log/kv/
format.rs1use std::{io, thread};
4use env_logger;
5use log;
6
7#[derive(Clone, Copy, Debug)]
8pub struct EnvLoggerFormatConfig {
9 pub thread : bool,
10 pub target : bool,
11 pub file : bool,
12 pub timestamp_precision : env_logger::TimestampPrecision
13}
14
15pub fn env_logger_json_formatter (config : EnvLoggerFormatConfig)
17 -> impl Fn(&mut env_logger::fmt::Formatter, &log::Record<'_>) -> io::Result<()>
18{
19 use io::Write;
20 #[derive(Default)]
21 struct KVVisitor(pub String);
22 impl <'kvs> log::kv::VisitSource <'kvs> for KVVisitor {
23 fn visit_pair (&mut self, key : log::kv::Key <'kvs>, value : log::kv::Value <'kvs>)
24 -> Result <(), log::kv::Error>
25 {
26 self.0 += format!(",\"{}\":{}", key, serde_json::to_string (&value).unwrap())
27 .as_str();
28 Ok(())
29 }
30 }
31 move |buf : &mut env_logger::fmt::Formatter, record : &log::Record|{
32 let thread_string = if config.thread {
33 thread::current().name().map_or_else (
34 || format!(",\"thread\":\"{:?}\"", thread::current().id())
35 .replace ("ThreadId", "unnamed"),
36 |name| format!(",\"thread\":\"{name}\""))
37 } else {
38 "".to_string()
39 };
40 let target_string = if config.target {
41 format!(",\"target\":\"{}\"", record.target())
42 } else {
43 "".to_string()
44 };
45 let file_string = if config.file {
46 format!(",\"file\":\"{}:{}\"",
47 record.file().unwrap_or ("<unknown>"), record.line().unwrap_or (0))
48 } else {
49 "".to_string()
50 };
51 let mut kvv = KVVisitor::default();
52 record.key_values().visit (&mut kvv).unwrap();
53 let ts = match config.timestamp_precision {
54 env_logger::TimestampPrecision::Seconds => buf.timestamp_seconds(),
55 env_logger::TimestampPrecision::Millis => buf.timestamp_millis(),
56 env_logger::TimestampPrecision::Micros => buf.timestamp_micros(),
57 env_logger::TimestampPrecision::Nanos => buf.timestamp_nanos()
58 };
59 writeln!(buf, "{{\"ts\":\"{ts}\",\"level\":\"{}\"{}{}{},\"msg\":\"{}\"{}}}",
60 record.level(), thread_string, target_string, file_string, record.args(), kvv.0
61 )
62 }
63}
64
65pub fn env_logger_custom_formatter (config : EnvLoggerFormatConfig)
70 -> impl Fn(&mut env_logger::fmt::Formatter, &log::Record<'_>) -> io::Result<()>
71{
72 use io::Write;
73 #[derive(Default)]
74 struct KVVisitor(pub String);
75 impl <'kvs> log::kv::VisitSource <'kvs> for KVVisitor {
76 fn visit_pair (&mut self, key : log::kv::Key <'kvs>, value : log::kv::Value <'kvs>)
77 -> Result <(), log::kv::Error>
78 {
79 let mut value_string = serde_json::to_string (&value).unwrap();
80 let mut value_str = value_string.as_str();
81 if !value_str.contains (char::is_whitespace) {
82 value_string = value_str.replace ("\\\"", "\"");
85 value_str = if value_string.starts_with ('"') {
86 &value_string[1..value_string.len()-1]
87 } else {
88 &value_string[..]
89 };
90 }
91 self.0 += format!(" {key}={value_str}").as_str();
92 Ok(())
93 }
94 }
95 move |buf : &mut env_logger::fmt::Formatter, record : &log::Record|{
96 let mut kvv = KVVisitor::default();
97 record.key_values().visit (&mut kvv).unwrap();
98 let kvs = if kvv.0.is_empty() {
99 "".to_string()
100 } else {
101 format!(" {}", kvv.0)
102 };
103 let ts = match config.timestamp_precision {
104 env_logger::TimestampPrecision::Seconds => buf.timestamp_seconds(),
105 env_logger::TimestampPrecision::Millis => buf.timestamp_millis(),
106 env_logger::TimestampPrecision::Micros => buf.timestamp_micros(),
107 env_logger::TimestampPrecision::Nanos => buf.timestamp_nanos()
108 };
109 if !config.file && !config.thread && !config.target {
110 let level_string = format!("{}:", record.level());
111 writeln!(buf, "{ts} {level_string:6} {}{kvs}", record.args())
112 } else {
113 let thread_string = if config.thread {
114 thread::current().name().map_or_else (
115 || format!(" {:?}", thread::current().id())
116 .replace ("ThreadId", "unnamed"),
117 |name| format!(" {name}"))
118 } else {
119 "".to_string()
120 };
121 let target_string = if config.target {
122 format!(" {}", record.target())
123 } else {
124 "".to_string()
125 };
126 let file_string = if config.file {
127 format!(" {}:{}",
128 record.file().unwrap_or ("<unknown>"), record.line().unwrap_or (0))
129 } else {
130 "".to_string()
131 };
132 writeln!(buf, "{ts} {:5}{}{}{}: {}{kvs}",
133 record.level(), thread_string, target_string, file_string, record.args())
134 }
135 }
136}
137
138impl EnvLoggerFormatConfig {
139 pub const fn thread (&mut self, thread : bool) -> &mut Self {
140 self.thread = thread;
141 self
142 }
143
144 pub const fn target (&mut self, target : bool) -> &mut Self {
145 self.target = target;
146 self
147 }
148
149 pub const fn file (&mut self, file : bool) -> &mut Self {
150 self.file = file;
151 self
152 }
153
154 pub const fn timestamp_precision (&mut self, precision : env_logger::TimestampPrecision)
155 -> &mut Self
156 {
157 self.timestamp_precision = precision;
158 self
159 }
160
161 pub const fn build (&mut self) -> Self {
162 *self
163 }
164}
165
166impl Default for EnvLoggerFormatConfig {
167 fn default() -> Self {
168 EnvLoggerFormatConfig {
169 thread: true,
170 target: true,
171 file: true,
172 timestamp_precision: env_logger::TimestampPrecision::Seconds
173 }
174 }
175}