1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
|
// containerspy structured logger
use std::fmt::{Display, Formatter};
use chrono::Utc;
#[allow(dead_code)]
pub fn debug<'a>(args: impl Display, rich: impl IntoIterator<Item = (&'a str, &'a str)>) {
log_impl(LogLevel::Debug, args.to_string().as_str(), rich);
}
pub fn info<'a>(args: impl Display, rich: impl IntoIterator<Item = (&'a str, &'a str)>) {
log_impl(LogLevel::Info, args.to_string().as_str(), rich);
}
#[allow(dead_code)]
pub fn warn<'a>(args: impl Display, rich: impl IntoIterator<Item = (&'a str, &'a str)>) {
log_impl(LogLevel::Warn, args.to_string().as_str(), rich);
}
#[allow(dead_code)]
pub fn error<'a>(args: impl Display, rich: impl IntoIterator<Item = (&'a str, &'a str)>) {
log_impl(LogLevel::Error, args.to_string().as_str(), rich);
}
#[allow(dead_code)]
pub fn fatal<'a>(args: impl Display, rich: impl IntoIterator<Item = (&'a str, &'a str)>) {
log_impl(LogLevel::Fatal, args.to_string().as_str(), rich);
}
enum LogLevel {
Fatal,
Error,
Warn,
Info,
Debug,
}
impl Display for LogLevel {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.write_str(
match self {
LogLevel::Fatal => "fatal",
LogLevel::Error => "error",
LogLevel::Warn => "warn",
LogLevel::Info => "info",
LogLevel::Debug => "debug",
}
)
}
}
fn log_impl<'a>(level: LogLevel, msg: &str, rich: impl IntoIterator<Item = (&'a str, &'a str)>) {
let time = Utc::now();
let nice_time = time.format("%F %X%.3f");
let full_time = time.format("%+");
let mut final_rich = vec![
("ts", full_time.to_string()),
("level", level.to_string()),
("msg", msg.to_string())
];
// i don't care anymore, just clone it all temporarily.
final_rich.extend(rich.into_iter().map(|(a, b)| (a, b.to_string())));
let mut buf = format!("{nice_time}");
for (k, v) in final_rich {
if needs_escaping(k) {
continue;
}
if needs_escaping(&v) {
buf += &format!(" {k}=\"{}\"", escape(&v));
} else {
buf += &format!(" {k}={v}");
}
}
println!("{buf}");
}
static SAFE_ALPHABET: &str = r#"abcdefghijklmnopqrstuvxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_+.,/\\|!@#$%^&*()[]{}"#;
fn needs_escaping(val: &str) -> bool {
for char in val.chars() {
if !SAFE_ALPHABET.contains(char) {
return true
}
}
false
}
fn escape(val: &str) -> String {
val.replace("\n", "\\n").replace("\\", "\\\\").replace("\"", "\\\"")
}
|