tracing_formatters/layer/
syslog.rs1use crate::facility::Facility;
2use crate::formatter::LogFormatter;
3use crate::log_layer::{LogLayer, Type};
4use crate::log_value::LogValue;
5use crate::severity::Severity;
6use crate::storage::Storage;
7use chrono::{DateTime, SecondsFormat, Utc};
8use core::fmt;
9use std::collections::HashMap;
10use std::ops::{Deref, DerefMut};
11use tracing::{Event, Subscriber};
12use tracing_subscriber::fmt::MakeWriter;
13use tracing_subscriber::registry::{LookupSpan, SpanRef};
14
15#[derive(Debug, Default)]
16pub struct Syslog {
17 facility: Option<Facility>,
18 severity: Option<Severity>,
19 version: Option<i32>,
20 timestamp: Option<DateTime<Utc>>,
21 hostname: Option<String>,
22 application: Option<String>,
23 proc_id: Option<u32>,
24 message_id: Option<Vec<String>>,
25 message: Option<String>,
26 data: Option<StructuredData>,
27 file: Option<String>,
28 line: Option<u32>,
29}
30
31impl Syslog {
32 fn format_event_message<
33 S: Subscriber + for<'a> tracing_subscriber::registry::LookupSpan<'a>,
34 >(
35 &self,
36 current_span: &Option<SpanRef<S>>,
37 event: &Event,
38 event_visitor: &Storage<'_>,
39 ) -> String {
40 let mut message = event_visitor
41 .values()
42 .get("message")
43 .and_then(|v| match v {
44 LogValue::String(s) => Some(format!("{} {}", event.metadata().name(), s.as_str())),
45 _ => None,
46 })
47 .unwrap_or_else(|| {
48 format!("{} {}", event.metadata().name(), event.metadata().target())
49 });
50
51 if let Some(span) = ¤t_span {
52 message = format!(
53 "{} {}",
54 self.format_span_context(span, &Type::Event),
55 message
56 );
57 }
58
59 message
60 }
61
62 fn format_span_context<S: Subscriber + for<'a> tracing_subscriber::registry::LookupSpan<'a>>(
63 &self,
64 span: &SpanRef<S>,
65 ty: &Type,
66 ) -> String {
67 format!("({})", span.metadata().name())
68 }
69}
70
71impl fmt::Display for Syslog {
72 fn fmt(&self, f: &mut fmt::Formatter) -> core::fmt::Result {
73 let default_string = "-".to_string();
75 write!(f, "<{priority_value}>{version} {timestamp} {hostname} {app_name} {proc_id} {msgid} {structured_data} {msg}",
76 priority_value = self.severity.as_ref().map(|t| t.to_level(self.facility.as_ref())).unwrap_or(0),
77 version = self.version.map(|v| v.to_string()).as_ref().unwrap_or(&default_string),
78 timestamp = self.timestamp.map(|t| t.to_rfc3339_opts(SecondsFormat::Millis, true)).as_ref().unwrap_or(&default_string),
79 hostname = self.hostname.as_ref().unwrap_or(&default_string),
80 app_name = self.application.as_ref().unwrap_or(&default_string),
81 proc_id = self.proc_id.map(|t| t.to_string()).as_ref().unwrap_or(&default_string),
82 msgid = self.message_id.as_ref().map(|t| t.join("-")).as_ref().unwrap_or(&default_string),
83 structured_data = self.data.as_ref().unwrap_or(&StructuredData::default()).to_string(),
84 msg = self.message.as_ref().unwrap_or(&default_string))
85 }
86}
87
88impl LogFormatter for Syslog {
89 fn from_log_layer<W: for<'a> MakeWriter<'a> + 'static, F: LogFormatter + Default>(
90 layer: &LogLayer<W, F>,
91 ) -> Self {
92 Self {
93 facility: None,
94 severity: None,
95 version: Some(1),
96 timestamp: Some(Utc::now()),
97 hostname: layer.hostname().clone(),
98 application: layer.application().clone(),
99 proc_id: layer.proc_id().clone(),
100 message_id: None,
101 message: None,
102 data: None,
103 file: None,
104 line: None,
105 }
106 }
107 fn format_event<S: Subscriber + for<'a> tracing_subscriber::registry::LookupSpan<'a>>(
108 &mut self,
109 current_span: &Option<SpanRef<S>>,
110 event: &Event,
111 event_visitor: &Storage<'_>,
112 ) -> String {
113 self.message =
116 Option::from(self.format_event_message(¤t_span, event, &event_visitor));
117 self.message_id = current_span.as_ref().map(|t| {
118 vec![
119 t.id().into_non_zero_u64().to_string(),
120 Type::Event.to_string(),
121 ]
122 });
123
124 self.line = event.metadata().line();
125 self.file = event.metadata().file().map(|t| t.to_string());
126 self.severity = event_visitor
127 .get("severity")
128 .and_then(|t| t.try_into().ok())
129 .or_else(|| event_visitor.get("level").and_then(|t| t.try_into().ok()))
130 .or_else(|| Option::from(Severity::from(event.metadata().level())));
131
132 self.to_string()
133 }
134
135 fn format_span<S: Subscriber + for<'a> LookupSpan<'a>>(
136 &mut self,
137 span: &SpanRef<S>,
138 ty: Type,
139 ) -> String {
140 self.message = Option::from(self.format_span_context(span, &ty));
141 self.line = span.metadata().line();
142 self.file = span.metadata().file().map(|t| t.to_string());
143 self.severity = Option::from(Severity::from(span.metadata().level()));
144 self.message_id = Option::from(vec![
145 span.id().into_non_zero_u64().to_string(),
146 ty.to_string(),
147 ]);
148 self.to_string()
149 }
150}
151
152#[derive(Debug)]
153pub struct StructuredData(HashMap<String, LogValue>);
154
155impl ToString for StructuredData {
156 fn to_string(&self) -> String {
157 let mut strings = Vec::new();
158 for (key, val) in self.deref() {
159 strings.push(format!(
160 r#"{}="{}""#,
161 key,
162 val.to_string().replace(r#"""#, r#"\\""#)
163 ));
164 }
165 strings.join(" ")
166 }
167}
168
169impl Default for StructuredData {
170 fn default() -> Self {
171 StructuredData(Default::default())
172 }
173}
174
175impl Deref for StructuredData {
176 type Target = HashMap<String, LogValue>;
177
178 fn deref(&self) -> &Self::Target {
179 &self.0
180 }
181}
182
183impl DerefMut for StructuredData {
184 fn deref_mut(&mut self) -> &mut Self::Target {
185 &mut self.0
186 }
187}