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