1use crate::{LogClock, LogEvent, LogPolicy, LogSink, Sensitivity, Severity};
14use std::borrow::Cow;
15use std::sync::atomic::{AtomicU64, Ordering};
16use std::sync::Arc;
17
18#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
20pub struct LogStats {
21 pub filtered: u64,
23 pub sink_failures: u64,
25 pub dropped: u64,
27 pub invalid: u64,
29}
30
31#[derive(Debug, Clone, Copy, PartialEq, Eq)]
33pub struct SinkStats {
34 pub name: &'static str,
36 pub failures: u64,
38}
39
40struct SinkSlot {
41 sink: Arc<dyn LogSink>,
42 failures: AtomicU64,
43}
44
45pub struct LogDispatcher {
47 policy: LogPolicy,
48 sinks: Vec<SinkSlot>,
49 filtered: AtomicU64,
50 sink_failures: AtomicU64,
51 dropped: AtomicU64,
52 invalid: AtomicU64,
53}
54
55impl LogDispatcher {
56 pub fn new(policy: LogPolicy, sinks: Vec<Arc<dyn LogSink>>) -> Self {
58 Self {
59 policy,
60 sinks: sinks
61 .into_iter()
62 .map(|sink| SinkSlot {
63 sink,
64 failures: AtomicU64::new(0),
65 })
66 .collect(),
67 filtered: AtomicU64::new(0),
68 sink_failures: AtomicU64::new(0),
69 dropped: AtomicU64::new(0),
70 invalid: AtomicU64::new(0),
71 }
72 }
73 pub fn emit(&self, event: LogEvent) {
75 if self.sinks.is_empty() {
76 return;
77 }
78 if event.validate().is_err() {
79 self.invalid.fetch_add(1, Ordering::Relaxed);
80 return;
81 }
82 if !self.policy.allows(&event) {
83 self.filtered.fetch_add(1, Ordering::Relaxed);
84 return;
85 }
86 let severity = event.severity;
87 let sanitized = self.policy.sanitize_owned(event);
88 for slot in &self.sinks {
89 if self.policy.sensitivity == Sensitivity::Sensitive && !slot.sink.accepts_sensitive() {
90 self.sink_failures.fetch_add(1, Ordering::Relaxed);
91 slot.failures.fetch_add(1, Ordering::Relaxed);
92 continue;
93 }
94 if slot.sink.emit(&sanitized).is_err() {
95 self.sink_failures.fetch_add(1, Ordering::Relaxed);
96 slot.failures.fetch_add(1, Ordering::Relaxed);
97 if severity < Severity::Error {
98 self.dropped.fetch_add(1, Ordering::Relaxed);
99 }
100 }
101 }
102 }
103
104 pub fn event<'a>(
106 &'a self,
107 timestamp_ms: u64,
108 component: impl Into<Cow<'a, str>>,
109 ) -> LogBuilder<'a> {
110 LogBuilder {
111 dispatcher: self,
112 timestamp_ms,
113 component: component.into(),
114 verbosity: crate::Verbosity::V4,
115 }
116 }
117
118 pub fn event_now<'a>(
120 &'a self,
121 clock: &dyn LogClock,
122 component: impl Into<Cow<'a, str>>,
123 ) -> LogBuilder<'a> {
124 self.event(clock.now_ms(), component)
125 }
126
127 pub fn enabled(&self, component: &str, verbosity: crate::Verbosity) -> bool {
129 !self.sinks.is_empty() && self.policy.allows_component(component, verbosity)
130 }
131 pub fn stats(&self) -> LogStats {
133 LogStats {
134 filtered: self.filtered.load(Ordering::Relaxed),
135 sink_failures: self.sink_failures.load(Ordering::Relaxed),
136 dropped: self.dropped.load(Ordering::Relaxed),
137 invalid: self.invalid.load(Ordering::Relaxed),
138 }
139 }
140
141 pub fn sink_stats(&self) -> Vec<SinkStats> {
143 self.sinks
144 .iter()
145 .map(|slot| SinkStats {
146 name: slot.sink.name(),
147 failures: slot.failures.load(Ordering::Relaxed),
148 })
149 .collect()
150 }
151}
152
153#[derive(Clone)]
155pub struct LogBuilder<'a> {
156 dispatcher: &'a LogDispatcher,
157 timestamp_ms: u64,
158 component: Cow<'a, str>,
159 verbosity: crate::Verbosity,
160}
161
162impl<'a> LogBuilder<'a> {
163 #[must_use]
165 pub fn component(&self, component: impl Into<Cow<'a, str>>) -> Self {
166 Self {
167 component: component.into(),
168 ..self.clone()
169 }
170 }
171
172 #[must_use]
176 pub fn verbosity(&self, verbosity: u8) -> Self {
177 Self {
178 verbosity: crate::Verbosity::new(verbosity).unwrap_or(crate::Verbosity::V4),
179 ..self.clone()
180 }
181 }
182
183 pub fn try_verbosity(&self, verbosity: u8) -> Result<Self, crate::VerbosityError> {
185 let verbosity = crate::Verbosity::new(verbosity).ok_or(crate::VerbosityError)?;
186 Ok(Self {
187 verbosity,
188 ..self.clone()
189 })
190 }
191
192 pub fn trace(&self, message: impl Into<String>) {
194 self.emit(Severity::Trace, message);
195 }
196
197 pub fn debug(&self, message: impl Into<String>) {
199 self.emit(Severity::Debug, message);
200 }
201
202 pub fn info(&self, message: impl Into<String>) {
204 self.emit(Severity::Info, message);
205 }
206
207 pub fn warn(&self, message: impl Into<String>) {
209 self.emit(Severity::Warn, message);
210 }
211
212 pub fn error(&self, message: impl Into<String>) {
214 self.emit(Severity::Error, message);
215 }
216
217 pub fn critical(&self, message: impl Into<String>) {
219 self.emit(Severity::Critical, message);
220 }
221
222 fn emit(&self, severity: Severity, message: impl Into<String>) {
223 if self.dispatcher.enabled(&self.component, self.verbosity) {
224 self.dispatcher.emit(LogEvent::new(
225 self.timestamp_ms,
226 severity,
227 self.verbosity,
228 self.component.to_string(),
229 message.into(),
230 ));
231 }
232 }
233}