1use crate::log::Log;
12use crate::log_level::LogLevel;
13use euxis_commons::counter::Counter;
14use std::sync::atomic::Ordering;
15use tracing_core::field::{Field, Visit};
16use tracing_core::{Event, Level, Metadata, Subscriber};
17
18fn map_tracing_level(level: Level) -> LogLevel {
20 if level == Level::ERROR {
21 LogLevel::ERROR
22 } else if level == Level::WARN {
23 LogLevel::WARN
24 } else if level == Level::INFO {
25 LogLevel::INFO
26 } else if level == Level::DEBUG {
27 LogLevel::DEBUG
28 } else {
29 LogLevel::TRACE
30 }
31}
32
33static SPAN_ID_COUNTER: Counter = Counter::new(1);
35
36#[derive(Debug, Default, Clone, Copy)]
38pub struct RlgSubscriber;
39
40impl RlgSubscriber {
41 #[must_use]
43 pub const fn new() -> Self {
44 Self
45 }
46}
47
48impl Subscriber for RlgSubscriber {
49 fn enabled(&self, metadata: &Metadata<'_>) -> bool {
50 map_tracing_level(*metadata.level()).to_numeric()
51 >= crate::engine::ENGINE.filter_level()
52 }
53
54 fn new_span(
55 &self,
56 _span: &tracing_core::span::Attributes<'_>,
57 ) -> tracing_core::span::Id {
58 tracing_core::span::Id::from_u64(
59 SPAN_ID_COUNTER.fetch_add(1, Ordering::Relaxed),
60 )
61 }
62
63 fn record(
64 &self,
65 _span: &tracing_core::span::Id,
66 _values: &tracing_core::span::Record<'_>,
67 ) {
68 }
69
70 fn record_follows_from(
71 &self,
72 _span: &tracing_core::span::Id,
73 _follows: &tracing_core::span::Id,
74 ) {
75 }
76
77 fn event(&self, event: &Event<'_>) {
78 let metadata = event.metadata();
79 let level = map_tracing_level(*metadata.level());
80
81 let mut visitor = RlgVisitor::default();
82 event.record(&mut visitor);
83
84 let mut log = Log::build(level, &visitor.message);
85 log.component =
86 std::borrow::Cow::Owned(metadata.target().to_string());
87
88 for (key, value) in visitor.fields {
89 log = log.with(&key, value);
90 }
91
92 log.fire();
93 }
94
95 fn enter(&self, _span: &tracing_core::span::Id) {}
96
97 fn exit(&self, _span: &tracing_core::span::Id) {}
98}
99
100#[derive(Default)]
101struct RlgVisitor {
102 message: String,
103 fields: std::collections::BTreeMap<String, serde_json::Value>,
104}
105
106macro_rules! impl_record_field {
107 ($method:ident, $ty:ty) => {
108 fn $method(&mut self, field: &Field, value: $ty) {
109 self.fields.insert(
110 field.name().to_string(),
111 serde_json::json!(value),
112 );
113 }
114 };
115 (stringify $method:ident, $ty:ty) => {
116 fn $method(&mut self, field: &Field, value: $ty) {
117 self.fields.insert(
118 field.name().to_string(),
119 serde_json::json!(value.to_string()),
120 );
121 }
122 };
123}
124
125impl Visit for RlgVisitor {
126 fn record_debug(
127 &mut self,
128 field: &Field,
129 value: &dyn std::fmt::Debug,
130 ) {
131 if field.name() == "message" {
132 self.message = format!("{value:?}");
133 } else {
134 self.fields.insert(
135 field.name().to_string(),
136 serde_json::json!(format!("{value:?}")),
137 );
138 }
139 }
140
141 fn record_str(&mut self, field: &Field, value: &str) {
142 if field.name() == "message" {
143 self.message = value.to_string();
144 } else {
145 self.fields.insert(
146 field.name().to_string(),
147 serde_json::json!(value),
148 );
149 }
150 }
151
152 fn record_error(
153 &mut self,
154 field: &Field,
155 value: &(dyn std::error::Error + 'static),
156 ) {
157 self.fields.insert(
158 field.name().to_string(),
159 serde_json::json!(value.to_string()),
160 );
161 }
162
163 impl_record_field!(record_u64, u64);
164 impl_record_field!(record_i64, i64);
165 impl_record_field!(record_bool, bool);
166 impl_record_field!(record_f64, f64);
167 impl_record_field!(stringify record_u128, u128);
168 impl_record_field!(stringify record_i128, i128);
169}
170
171#[cfg(feature = "tracing-layer")]
191#[derive(Debug, Clone, Copy)]
192pub struct RlgLayer {
193 format: crate::log_format::LogFormat,
194}
195
196#[cfg(feature = "tracing-layer")]
197impl Default for RlgLayer {
198 fn default() -> Self {
199 Self::new()
200 }
201}
202
203#[cfg(feature = "tracing-layer")]
204impl RlgLayer {
205 #[must_use]
207 pub const fn new() -> Self {
208 Self {
209 format: crate::log_format::LogFormat::MCP,
210 }
211 }
212
213 #[must_use]
215 pub const fn with_format(
216 mut self,
217 format: crate::log_format::LogFormat,
218 ) -> Self {
219 self.format = format;
220 self
221 }
222}
223
224#[cfg(feature = "tracing-layer")]
225impl<S> tracing_subscriber::Layer<S> for RlgLayer
226where
227 S: Subscriber
228 + for<'lookup> tracing_subscriber::registry::LookupSpan<'lookup>,
229{
230 fn enabled(
231 &self,
232 metadata: &Metadata<'_>,
233 _ctx: tracing_subscriber::layer::Context<'_, S>,
234 ) -> bool {
235 map_tracing_level(*metadata.level()).to_numeric()
236 >= crate::engine::ENGINE.filter_level()
237 }
238
239 fn on_event(
240 &self,
241 event: &Event<'_>,
242 _ctx: tracing_subscriber::layer::Context<'_, S>,
243 ) {
244 let metadata = event.metadata();
245 let level = map_tracing_level(*metadata.level());
246
247 let mut visitor = RlgVisitor::default();
248 event.record(&mut visitor);
249
250 let mut log = Log::build(level, &visitor.message);
251 log.component =
252 std::borrow::Cow::Owned(metadata.target().to_string());
253 log.format = self.format;
254
255 for (key, value) in visitor.fields {
256 log = log.with(&key, value);
257 }
258
259 log.fire();
260 }
261
262 fn on_new_span(
263 &self,
264 _attrs: &tracing_core::span::Attributes<'_>,
265 _id: &tracing_core::span::Id,
266 _ctx: tracing_subscriber::layer::Context<'_, S>,
267 ) {
268 crate::engine::ENGINE.inc_spans();
269 }
270
271 fn on_close(
272 &self,
273 _id: tracing_core::span::Id,
274 _ctx: tracing_subscriber::layer::Context<'_, S>,
275 ) {
276 crate::engine::ENGINE.dec_spans();
277 }
278}