tracing_formatters/layer/
syslog.rs

1use 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) = &current_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        //<165>1 2003-10-11T22:14:15.003Z mymachine.example.com evntslog - ID47 [exampleSDID@32473 iut="3" eventSource="Application" eventID="1011"] BOMAn application event log entry...
74        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        // let mut buffer = Vec::new();
114        //
115        self.message =
116            Option::from(self.format_event_message(&current_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}