sozu_command_lib/logging/
display.rs1use std::fmt;
2
3use crate::{
4 logging::{
5 EndpointRecord, FullTags, LogContext, LogDuration, LogLevel, LogMessage, LoggerBackend,
6 Rfc3339Time,
7 },
8 AsStr,
9};
10
11impl LogLevel {
12 pub const fn as_str(&self, access: bool, colored: bool) -> &'static str {
13 match (self, access, colored) {
14 (LogLevel::Error, false, false) => "ERROR",
15 (LogLevel::Warn, false, false) => "WARN ",
16 (LogLevel::Info, false, false) => "INFO ",
17 (LogLevel::Debug, false, false) => "DEBUG",
18 (LogLevel::Trace, false, false) => "TRACE",
19
20 (LogLevel::Error, false, true) => "\x1b[;31;1mERROR",
21 (LogLevel::Warn, false, true) => "\x1b[;33;1mWARN ",
22 (LogLevel::Info, false, true) => "\x1b[;32;1mINFO ",
23 (LogLevel::Debug, false, true) => "\x1b[;34mDEBUG",
24 (LogLevel::Trace, false, true) => "\x1b[;90mTRACE",
25
26 (LogLevel::Error, true, false) => "ERROR-ACCESS",
27 (LogLevel::Info, true, false) => "INFO-ACCESS ",
28 (_, true, false) => "???",
29
30 (LogLevel::Error, true, true) => "\x1b[;35;1mERROR-ACCESS",
31 (LogLevel::Info, true, true) => "\x1b[;35;1mINFO-ACCESS ",
32 (_, true, true) => "\x1b[;35;1m???",
33 }
34 }
35}
36
37impl AsRef<str> for LoggerBackend {
38 fn as_ref(&self) -> &str {
39 match self {
40 LoggerBackend::Stdout(_) => "stdout",
41 LoggerBackend::Unix(_) => "UNIX socket",
42 LoggerBackend::Udp(_, _) => "UDP socket",
43 LoggerBackend::Tcp(_) => "TCP socket",
44 LoggerBackend::File(_) => "file",
45 }
46 }
47}
48
49impl fmt::Display for Rfc3339Time {
50 fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
51 let t = self.inner;
52 write!(
53 f,
54 "{}-{:02}-{:02}T{:02}:{:02}:{:02}.{:06}Z",
55 t.year(),
56 t.month() as u8,
57 t.day(),
58 t.hour(),
59 t.minute(),
60 t.second(),
61 t.microsecond()
62 )
63 }
64}
65
66impl fmt::Display for LogMessage<'_> {
67 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
68 match self.0 {
69 Some(message) => write!(f, " | {message}"),
70 None => Ok(()),
71 }
72 }
73}
74
75impl fmt::Display for LogDuration {
76 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
77 match self.0 {
78 None => write!(f, "-"),
79 Some(duration) => {
80 let secs = duration.as_secs();
81 if secs >= 10 {
82 return write!(f, "{secs}s");
83 }
84
85 let ms = duration.as_millis();
86 if ms < 10 {
87 let us = duration.as_micros();
88 if us >= 10 {
89 return write!(f, "{us}μs");
90 }
91
92 let ns = duration.as_nanos();
93 return write!(f, "{ns}ns");
94 }
95
96 write!(f, "{ms}ms")
97 }
98 }
99 }
100}
101
102impl fmt::Display for LogContext<'_> {
103 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
104 write!(
105 f,
106 "[{} {} {}]",
107 self.request_id,
108 self.cluster_id.unwrap_or("-"),
109 self.backend_id.unwrap_or("-")
110 )
111 }
112}
113
114impl fmt::Display for EndpointRecord<'_> {
115 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
116 match self {
117 Self::Http {
118 method,
119 authority,
120 path,
121 status,
122 ..
123 } => write!(
124 f,
125 "{} {} {} {}",
126 authority.as_str_or("-"),
127 method.as_str_or("-"),
128 path.as_str_or("-"),
129 display_status(*status, f.alternate()),
130 ),
131 Self::Tcp => {
132 write!(f, "-")
133 }
134 }
135 }
136}
137
138fn display_status(status: Option<u16>, pretty: bool) -> String {
139 match (status, pretty) {
140 (Some(s @ 200..=299), true) => format!("\x1b[32m{s}"),
141 (Some(s @ 300..=399), true) => format!("\x1b[34m{s}"),
142 (Some(s @ 400..=499), true) => format!("\x1b[33m{s}"),
143 (Some(s @ 500..=599), true) => format!("\x1b[31m{s}"),
144 (Some(s), _) => s.to_string(),
145 (None, _) => "-".to_string(),
146 }
147}
148
149impl<'a> fmt::Display for FullTags<'a> {
150 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
151 match (self.concatenated, self.user_agent) {
152 (None, None) => Ok(()),
153 (Some(tags), None) => write!(f, "{tags}"),
154 (Some(tags), Some(ua)) if !tags.is_empty() => {
155 write!(f, "{tags}, user-agent={}", prepare_user_agent(ua))
156 }
157 (_, Some(ua)) => write!(f, "user-agent={}", prepare_user_agent(ua)),
158 }
159 }
160}
161
162fn prepare_user_agent(user_agent: &str) -> String {
163 user_agent
164 .replace(' ', "_")
165 .replace('[', "{")
166 .replace(']', "}")
167}