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