aboutsummaryrefslogtreecommitdiff
path: root/src/s_log.rs
blob: 16b47269ae5b18cf05935e92e76076c275fd3ac6 (plain)
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("\"", "\\\"")
}