1use std::{
97 collections::HashMap,
98 fmt,
99 fmt::Write,
100 sync::{Arc, RwLock},
101};
102
103use dlt_rs::DltContextHandle;
104pub use dlt_rs::{DltApplication, DltError, DltId, DltLogLevel, DltSysError};
106use indexmap::IndexMap;
107use tracing_core::{Event, Subscriber, span};
108use tracing_subscriber::{Layer, filter::LevelFilter, layer::Context, registry::LookupSpan};
109
110const DLT_CONTEXT_FIELD: &str = "dlt_context";
112
113pub struct DltLayer {
120 pub app: Arc<DltApplication>,
121 default_context: Arc<DltContextHandle>,
123 context_cache: Arc<RwLock<HashMap<String, Arc<DltContextHandle>>>>,
125}
126impl DltLayer {
127 pub fn new(app_id: &DltId, app_description: &str) -> Result<Self, DltError> {
138 let app = Arc::new(DltApplication::register(app_id, app_description)?);
140 let default_context =
142 Arc::new(app.create_context(&DltId::new(b"DFLT")?, "Default context")?);
143
144 Self::register_context_level_changed(&default_context)?;
145
146 Ok(DltLayer {
147 app,
148 default_context,
149 context_cache: Arc::new(RwLock::new(HashMap::new())),
150 })
151 }
152
153 fn register_context_level_changed(context: &DltContextHandle) -> Result<(), DltError> {
154 let mut receiver = context.register_log_level_changed_listener()?;
155 tokio::spawn(async move {
157 while let Ok(_event) = receiver.recv().await {
158 tracing_core::callsite::rebuild_interest_cache();
162 }
163 });
164 Ok(())
165 }
166
167 fn get_or_create_context_for_span<S>(
173 &self,
174 span: &tracing_subscriber::registry::SpanRef<'_, S>,
175 ) -> Result<Arc<DltContextHandle>, DltError>
176 where
177 S: Subscriber + for<'a> LookupSpan<'a>,
178 {
179 let span_name = span.name();
180
181 let dlt_context_id = {
183 let extensions = span.extensions();
184 extensions
185 .get::<IndexMap<String, FieldValue>>()
186 .and_then(|fields| {
187 fields.get(DLT_CONTEXT_FIELD).and_then(|value| match value {
188 FieldValue::Str(s) => Some(s.clone()),
189 _ => None,
190 })
191 })
192 };
193
194 let Some(custom_id) = dlt_context_id else {
196 return Ok(Arc::clone(&self.default_context));
197 };
198
199 {
201 let cache = self.context_cache.read().map_err(|_| DltError::BadLock)?;
202 if let Some(context) = cache.get(&custom_id) {
203 return Ok(Arc::clone(context));
204 }
205 }
206
207 let ctx_id = DltId::from_str_clamped(&custom_id)?;
209 let context = Arc::new(self.app.create_context(&ctx_id, span_name)?);
210
211 let mut cache = self.context_cache.write().map_err(|_| DltError::BadLock)?;
212 cache.insert(custom_id, Arc::clone(&context));
213 Self::register_context_level_changed(&context)?;
214
215 Ok(context)
216 }
217
218 #[cfg(feature = "dlt_layer_internal_logging")]
219 fn log_dlt_error(metadata: &tracing_core::Metadata, level: tracing::Level, e: DltSysError) {
220 eprintln!("DLT error occurred: {e:?}");
221 tracing::warn!(
222 target: "dlt_layer_internal",
223 error = ?e,
224 event_target = metadata.target(),
225 event_level = ?level,
226 "DLT error occurred"
227 );
228 }
229
230 #[cfg(not(feature = "dlt_layer_internal_logging"))]
231 fn log_dlt_error(_metadata: &tracing_core::Metadata, _level: tracing::Level, _e: DltSysError) {
232 }
234
235 fn max_dlt_level(&self) -> DltLogLevel {
236 if let Ok(cache) = self.context_cache.read() {
237 cache
238 .values()
239 .map(|ctx| ctx.log_level())
240 .chain(std::iter::once(self.default_context.log_level()))
241 .max_by_key(|&level| level as i32)
242 .unwrap_or(DltLogLevel::Default)
243 } else {
244 DltLogLevel::Default
245 }
246 }
247}
248
249impl<S> Layer<S> for DltLayer
250where
251 S: Subscriber + for<'a> LookupSpan<'a>,
252{
253 fn enabled(&self, metadata: &tracing::Metadata<'_>, _ctx: Context<'_, S>) -> bool {
254 metadata.target() != "dlt_layer_internal"
256 }
257
258 fn on_new_span(&self, attrs: &span::Attributes<'_>, id: &span::Id, ctx: Context<'_, S>) {
259 if let Some(span) = ctx.span(id) {
261 let mut visitor = FieldVisitor::new();
262 attrs.record(&mut visitor);
263
264 let mut extensions = span.extensions_mut();
265 extensions.insert(visitor.fields);
266 }
267 }
268
269 fn on_record(&self, span: &span::Id, values: &span::Record<'_>, ctx: Context<'_, S>) {
270 if let Some(span) = ctx.span(span) {
272 let mut visitor = FieldVisitor::new();
273 values.record(&mut visitor);
274
275 let mut extensions = span.extensions_mut();
276 if let Some(fields) = extensions.get_mut::<IndexMap<String, FieldValue>>() {
277 fields.extend(visitor.fields);
278 } else {
279 extensions.insert(visitor.fields);
280 }
281 }
282 }
283
284 fn on_event(&self, event: &Event<'_>, ctx: Context<'_, S>) {
285 let metadata = event.metadata();
286 let level = metadata.level();
287
288 let dlt_context = ctx
290 .event_scope(event)
291 .and_then(|scope| scope.from_root().last())
292 .map_or(Arc::clone(&self.default_context), |span| {
293 self.get_or_create_context_for_span(&span)
294 .unwrap_or_else(|_| Arc::clone(&self.default_context))
295 });
296
297 let dlt_level = map_level_to_dlt(*level);
298 let context_log_level = dlt_context.log_level();
299 if (dlt_level as i32) > (context_log_level as i32) {
300 return; }
302
303 let mut log_writer = match dlt_context.log_write_start(dlt_level) {
305 Ok(log_writer) => log_writer,
306 Err(e) => {
307 Self::log_dlt_error(metadata, *level, e);
308 return;
309 }
310 };
311
312 if let Some(scope) = ctx.event_scope(event) {
313 let mut span_context = String::new();
314 for span in scope.from_root() {
315 if !span_context.is_empty() {
316 span_context.push(':');
317 }
318 span_context.push_str(span.name());
319
320 let extensions = span.extensions();
321 if let Some(fields) = extensions.get::<IndexMap<String, FieldValue>>() {
322 let display_fields: Vec<_> = fields
324 .iter()
325 .filter(|(name, _)| *name != DLT_CONTEXT_FIELD)
326 .collect();
327
328 if !display_fields.is_empty() {
329 span_context.push('{');
330 for (i, (k, v)) in display_fields.iter().enumerate() {
331 if i > 0 {
332 span_context.push_str(", ");
333 }
334 let _ = write!(span_context, "{k}={v}");
335 }
336 span_context.push('}');
337 }
338 }
339 }
340
341 if !span_context.is_empty() {
342 span_context.push(':');
343 let _ = log_writer.write_string(&span_context);
344 }
345 }
346
347 let mut visitor = FieldVisitor::new();
349 event.record(&mut visitor);
350
351 let mut fields = visitor.fields;
355 let message_field = fields.shift_remove("message");
356 let target = metadata.target();
357 if let Some(msg) = message_field {
358 let formatted = if target.is_empty() {
359 msg.to_string()
360 } else {
361 format!("{target}: {msg}")
362 };
363 let _ = log_writer.write_string(&formatted);
364 } else if !target.is_empty() {
365 let _ = log_writer.write_string(target);
366 }
367
368 if let Err(e) = write_fields(&mut log_writer, fields) {
370 Self::log_dlt_error(metadata, *level, e);
371 return;
372 }
373
374 if let Err(e) = log_writer.finish() {
376 Self::log_dlt_error(metadata, *level, e);
377 }
378 }
379}
380
381impl<S> tracing_subscriber::layer::Filter<S> for DltLayer {
383 fn enabled(&self, meta: &tracing_core::Metadata<'_>, _cx: &Context<'_, S>) -> bool {
392 if meta.target() == "dlt_layer_internal" {
394 return false;
395 }
396
397 let dlt_level = map_level_to_dlt(*meta.level());
399
400 let max_level = self.max_dlt_level();
402
403 (dlt_level as i32) <= (max_level as i32)
405 }
406
407 fn max_level_hint(&self) -> Option<LevelFilter> {
419 let max_level = self.max_dlt_level();
420
421 Some(map_dlt_to_level_filter(max_level))
422 }
423}
424
425fn map_level_to_dlt(level: tracing::Level) -> DltLogLevel {
427 match level {
428 tracing::Level::ERROR => DltLogLevel::Error,
429 tracing::Level::WARN => DltLogLevel::Warn,
430 tracing::Level::INFO => DltLogLevel::Info,
431 tracing::Level::DEBUG => DltLogLevel::Debug,
432 tracing::Level::TRACE => DltLogLevel::Verbose,
433 }
434}
435
436fn map_dlt_to_level_filter(dlt_level: DltLogLevel) -> LevelFilter {
438 match dlt_level {
439 DltLogLevel::Off | DltLogLevel::Default => LevelFilter::OFF,
440 DltLogLevel::Fatal | DltLogLevel::Error => LevelFilter::ERROR,
441 DltLogLevel::Warn => LevelFilter::WARN,
442 DltLogLevel::Info => LevelFilter::INFO,
443 DltLogLevel::Debug => LevelFilter::DEBUG,
444 DltLogLevel::Verbose => LevelFilter::TRACE,
445 }
446}
447
448fn write_fields(
450 log_writer: &mut dlt_rs::DltLogWriter,
451 fields: IndexMap<String, FieldValue>,
452) -> Result<(), DltSysError> {
453 for (field_name, field_value) in fields {
454 log_writer.write_string(&field_name)?;
456 log_writer.write_string("=")?;
457
458 match field_value {
460 FieldValue::I64(v) => {
461 log_writer.write_int64(v)?;
462 }
463 FieldValue::U64(v) => {
464 log_writer.write_uint64(v)?;
465 }
466 FieldValue::I128(v) => {
467 log_writer.write_string(&v.to_string())?;
469 }
470 FieldValue::U128(v) => {
471 log_writer.write_string(&v.to_string())?;
473 }
474 FieldValue::F64(v) => {
475 log_writer.write_float64(v)?;
476 }
477 FieldValue::Bool(v) => {
478 log_writer.write_bool(v)?;
479 }
480 FieldValue::Str(s) | FieldValue::Debug(s) => {
481 log_writer.write_string(&s)?;
482 }
483 }
484 }
485 Ok(())
486}
487
488#[derive(Debug, Clone)]
490enum FieldValue {
491 Str(String),
492 I64(i64),
493 U64(u64),
494 I128(i128),
495 U128(u128),
496 F64(f64),
497 Bool(bool),
498 Debug(String),
499}
500
501impl fmt::Display for FieldValue {
502 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
503 match self {
504 FieldValue::I64(v) => write!(f, "{v}"),
505 FieldValue::U64(v) => write!(f, "{v}"),
506 FieldValue::I128(v) => write!(f, "{v}"),
507 FieldValue::U128(v) => write!(f, "{v}"),
508 FieldValue::F64(v) => write!(f, "{v}"),
509 FieldValue::Bool(v) => write!(f, "{v}"),
510 FieldValue::Str(s) => write!(f, "\"{s}\""),
511 FieldValue::Debug(s) => write!(f, "{s}"),
512 }
513 }
514}
515
516struct FieldVisitor {
518 fields: IndexMap<String, FieldValue>,
519}
520
521impl FieldVisitor {
522 fn new() -> Self {
523 FieldVisitor {
524 fields: IndexMap::new(),
525 }
526 }
527}
528
529impl tracing::field::Visit for FieldVisitor {
530 fn record_f64(&mut self, field: &tracing::field::Field, value: f64) {
531 self.fields
532 .insert(field.name().to_string(), FieldValue::F64(value));
533 }
534
535 fn record_i64(&mut self, field: &tracing::field::Field, value: i64) {
536 self.fields
537 .insert(field.name().to_string(), FieldValue::I64(value));
538 }
539
540 fn record_u64(&mut self, field: &tracing::field::Field, value: u64) {
541 self.fields
542 .insert(field.name().to_string(), FieldValue::U64(value));
543 }
544
545 fn record_i128(&mut self, field: &tracing::field::Field, value: i128) {
546 self.fields
547 .insert(field.name().to_string(), FieldValue::I128(value));
548 }
549
550 fn record_u128(&mut self, field: &tracing::field::Field, value: u128) {
551 self.fields
552 .insert(field.name().to_string(), FieldValue::U128(value));
553 }
554
555 fn record_bool(&mut self, field: &tracing::field::Field, value: bool) {
556 self.fields
557 .insert(field.name().to_string(), FieldValue::Bool(value));
558 }
559
560 fn record_str(&mut self, field: &tracing::field::Field, value: &str) {
561 self.fields
562 .insert(field.name().to_string(), FieldValue::Str(value.to_string()));
563 }
564
565 fn record_debug(&mut self, field: &tracing::field::Field, value: &dyn fmt::Debug) {
566 self.fields.insert(
567 field.name().to_string(),
568 FieldValue::Debug(format!("{value:?}")),
569 );
570 }
571}
572
573#[cfg(test)]
574mod tests {
575 use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
576
577 use super::*;
578
579 #[tokio::test]
580 async fn test_dlt_appender() {
581 let app_id = DltId::new(b"APP").unwrap();
584
585 tracing_subscriber::registry()
586 .with(DltLayer::new(&app_id, "test").expect("Failed to create DLT layer"))
587 .init();
588
589 let outer_span = tracing::info_span!("outer", level = 0);
590 let _outer_entered = outer_span.enter();
591
592 let inner_span = tracing::error_span!("inner", level = 1);
593 let _inner_entered = inner_span.enter();
594
595 tracing::info!(a_bool = true, answer = 42, message = "first example");
596 }
597}