tracing_systemd/format/
mod.rs1use std::borrow::Cow;
4
5#[cfg(feature = "colors")]
6pub(crate) mod color;
7pub(crate) mod event;
8pub(crate) mod span_chain;
9
10#[cfg(feature = "colors")]
11pub use color::{ColorMode, ColorTheme};
12
13#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
18pub enum TimestampFormat {
19 #[default]
21 None,
22 UnixSeconds,
25 Uptime,
28}
29
30#[derive(Debug, Clone)]
35#[allow(clippy::struct_excessive_bools)] pub(crate) struct FormatConfig {
37 pub show_target: bool,
38 pub show_thread_id: bool,
39 pub show_timestamp: bool,
40 pub timestamp_format: TimestampFormat,
41 pub use_level_prefix: bool,
42 pub span_separator: Cow<'static, str>,
43 pub message_separator: Cow<'static, str>,
44 pub level_separator: Cow<'static, str>,
45 pub function_bracket_left: Cow<'static, str>,
46 pub function_bracket_right: Cow<'static, str>,
47 pub arguments_equality: Cow<'static, str>,
48 pub arguments_separator: Cow<'static, str>,
49 pub thread_id_prefix: Cow<'static, str>,
50 pub thread_id_suffix: Cow<'static, str>,
51}
52
53impl Default for FormatConfig {
54 fn default() -> Self {
55 Self {
56 show_target: false,
57 show_thread_id: false,
58 show_timestamp: false,
59 timestamp_format: TimestampFormat::None,
60 use_level_prefix: true,
61 span_separator: Cow::Borrowed("::"),
62 message_separator: Cow::Borrowed(": "),
63 level_separator: Cow::Borrowed(" "),
64 function_bracket_left: Cow::Borrowed("("),
65 function_bracket_right: Cow::Borrowed(")"),
66 arguments_equality: Cow::Borrowed(": "),
67 arguments_separator: Cow::Borrowed(", "),
68 thread_id_prefix: Cow::Borrowed("["),
69 thread_id_suffix: Cow::Borrowed("] "),
70 }
71 }
72}
73
74pub(crate) fn syslog_prefix(level: tracing::Level) -> &'static str {
78 match level {
79 tracing::Level::ERROR => "<3>",
80 tracing::Level::WARN => "<4>",
81 tracing::Level::INFO => "<5>",
82 tracing::Level::DEBUG => "<6>",
83 tracing::Level::TRACE => "<7>",
84 }
85}
86
87pub(crate) fn current_thread_id_int() -> String {
92 let id = format!("{:?}", std::thread::current().id());
93 id.split_once('(')
95 .and_then(|(_, rest)| rest.split_once(')'))
96 .map_or_else(|| id.clone(), |(digits, _)| digits.to_owned())
97}
98
99pub(crate) fn format_timestamp(format: TimestampFormat) -> String {
102 use std::time::{SystemTime, UNIX_EPOCH};
103
104 match format {
105 TimestampFormat::None => String::new(),
106 TimestampFormat::UnixSeconds => SystemTime::now()
107 .duration_since(UNIX_EPOCH)
108 .map_or_else(|_| String::from("0.000"), |d| {
109 format!("{}.{:03}", d.as_secs(), d.subsec_millis())
110 }),
111 TimestampFormat::Uptime => {
112 let elapsed = process_start().elapsed();
113 format!("{}.{:03}", elapsed.as_secs(), elapsed.subsec_millis())
114 }
115 }
116}
117
118fn process_start() -> std::time::Instant {
119 use std::sync::OnceLock;
120 static START: OnceLock<std::time::Instant> = OnceLock::new();
121 *START.get_or_init(std::time::Instant::now)
122}
123
124#[cfg(test)]
125mod tests {
126 use super::*;
127
128 #[test]
129 fn syslog_prefix_maps_levels() {
130 assert_eq!(syslog_prefix(tracing::Level::ERROR), "<3>");
131 assert_eq!(syslog_prefix(tracing::Level::WARN), "<4>");
132 assert_eq!(syslog_prefix(tracing::Level::INFO), "<5>");
133 assert_eq!(syslog_prefix(tracing::Level::DEBUG), "<6>");
134 assert_eq!(syslog_prefix(tracing::Level::TRACE), "<7>");
135 }
136
137 #[test]
138 fn current_thread_id_int_is_numeric() {
139 let s = current_thread_id_int();
140 if let Ok(n) = s.parse::<u64>() {
144 assert!(n > 0);
146 }
147 }
148
149 #[test]
150 fn format_timestamp_none_is_empty() {
151 assert_eq!(format_timestamp(TimestampFormat::None), "");
152 }
153
154 #[test]
155 fn format_timestamp_unix_has_dot() {
156 let s = format_timestamp(TimestampFormat::UnixSeconds);
157 assert!(s.contains('.'), "unexpected {s:?}");
158 }
159
160 #[test]
161 fn format_timestamp_uptime_has_dot() {
162 let s = format_timestamp(TimestampFormat::Uptime);
163 assert!(s.contains('.'), "unexpected {s:?}");
164 }
165}