1use core::cell::RefCell;
4use core::fmt::{self, Write as _};
5use core::ops::Range;
6
7use tracing_core::field::{Field as TracingField, Visit};
8use tracing_core::span::{Attributes, Id, Record as SpanRecord};
9use tracing_core::{Event, Subscriber};
10use tracing_subscriber::layer::{Context, Layer};
11use tracing_subscriber::registry::LookupSpan;
12
13use crate::level::Level;
14use crate::logger::Logger;
15use crate::record::{Field, Metadata};
16use crate::value::{OwnedField, Value, write_error_chain, write_hex};
17
18#[cfg_attr(all(feature = "json", feature = "tracing"), doc = "```")]
46#[cfg_attr(not(all(feature = "json", feature = "tracing")), doc = "```ignore")]
47#[derive(Debug, Clone)]
61pub struct TracingLayer {
62 logger: Logger,
63}
64
65impl TracingLayer {
66 #[must_use]
68 pub const fn new(logger: Logger) -> Self {
69 Self { logger }
70 }
71
72 #[must_use]
74 pub const fn logger(&self) -> &Logger {
75 &self.logger
76 }
77}
78
79const fn from_tracing_level(level: tracing_core::Level) -> Level {
80 match level {
81 tracing_core::Level::ERROR => Level::Error,
82 tracing_core::Level::WARN => Level::Warn,
83 tracing_core::Level::INFO => Level::Info,
84 tracing_core::Level::DEBUG => Level::Debug,
85 tracing_core::Level::TRACE => Level::Trace,
86 }
87}
88
89#[derive(Default)]
91struct SpanFields(Vec<OwnedField>);
92
93impl SpanFields {
94 fn set(&mut self, key: &'static str, value: Value<'_>) {
95 let owned = OwnedField::new(key, value);
96 if let Some(existing) = self.0.iter_mut().find(|f| &*f.key == key) {
97 *existing = owned;
98 } else {
99 self.0.push(owned);
100 }
101 }
102}
103
104impl Visit for SpanFields {
105 fn record_f64(&mut self, field: &TracingField, value: f64) {
106 self.set(field.name(), Value::F64(value));
107 }
108 fn record_i64(&mut self, field: &TracingField, value: i64) {
109 self.set(field.name(), Value::I64(value));
110 }
111 fn record_u64(&mut self, field: &TracingField, value: u64) {
112 self.set(field.name(), Value::U64(value));
113 }
114 fn record_i128(&mut self, field: &TracingField, value: i128) {
115 self.set(field.name(), Value::I128(value));
116 }
117 fn record_u128(&mut self, field: &TracingField, value: u128) {
118 self.set(field.name(), Value::U128(value));
119 }
120 fn record_bool(&mut self, field: &TracingField, value: bool) {
121 self.set(field.name(), Value::Bool(value));
122 }
123 fn record_str(&mut self, field: &TracingField, value: &str) {
124 self.set(field.name(), Value::Str(value));
125 }
126 fn record_bytes(&mut self, field: &TracingField, value: &[u8]) {
127 self.set(field.name(), Value::Bytes(value));
128 }
129 fn record_error(&mut self, field: &TracingField, value: &(dyn std::error::Error + 'static)) {
130 self.set(field.name(), Value::Error(value));
131 }
132 fn record_debug(&mut self, field: &TracingField, value: &dyn fmt::Debug) {
133 self.set(field.name(), Value::Debug(value));
134 }
135}
136
137#[derive(Clone)]
139enum Slot {
140 Scalar(Value<'static>),
142 Text(Range<usize>),
144}
145
146#[derive(Clone)]
148enum Key {
149 Static(&'static str),
150 Text(Range<usize>),
151}
152
153struct Arena {
156 text: String,
157 message: Option<Range<usize>>,
158 event: Vec<(Key, Slot)>,
159 spans: Vec<(Key, Slot)>,
161 scratch: Vec<(Key, Slot)>,
165 blocks: Vec<usize>,
166}
167
168impl Arena {
169 const fn new() -> Self {
170 Self {
171 text: String::new(),
172 message: None,
173 event: Vec::new(),
174 spans: Vec::new(),
175 scratch: Vec::new(),
176 blocks: Vec::new(),
177 }
178 }
179
180 fn clear(&mut self) {
181 self.text.clear();
182 self.message = None;
183 self.event.clear();
184 self.spans.clear();
185 self.scratch.clear();
186 self.blocks.clear();
187 }
188
189 fn order_spans_root_first(&mut self) {
191 let mut end = self.scratch.len();
192 for &start in self.blocks.iter().rev() {
193 if let Some(block) = self.scratch.get(start..end) {
194 self.spans.extend_from_slice(block);
195 }
196 end = start;
197 }
198 }
199
200 fn push_text(&mut self, f: impl FnOnce(&mut String) -> fmt::Result) -> Range<usize> {
201 let start = self.text.len();
202 let _partial_text_is_kept = f(&mut self.text);
204 start..self.text.len()
205 }
206
207 fn text(&self, range: &Range<usize>) -> &str {
208 self.text.get(range.clone()).unwrap_or("")
209 }
210
211 fn key(&self, key: &Key) -> &str {
212 match key {
213 Key::Static(s) => s,
214 Key::Text(r) => self.text(r),
215 }
216 }
217
218 fn value(&self, slot: &Slot) -> Value<'_> {
219 match slot {
220 Slot::Scalar(v) => *v,
221 Slot::Text(r) => Value::Str(self.text(r)),
222 }
223 }
224
225 fn add_span_field(&mut self, field: &OwnedField) {
226 let key = Key::Text(self.push_text(|t| t.write_str(&field.key)));
227 let slot = match field.as_field().value {
228 Value::Str(s) => Slot::Text(self.push_text(|t| t.write_str(s))),
229 Value::Bytes(b) => Slot::Text(self.push_text(|t| write_hex(t, b))),
230 Value::Null => Slot::Scalar(Value::Null),
231 Value::Bool(b) => Slot::Scalar(Value::Bool(b)),
232 Value::I64(n) => Slot::Scalar(Value::I64(n)),
233 Value::U64(n) => Slot::Scalar(Value::U64(n)),
234 Value::I128(n) => Slot::Scalar(Value::I128(n)),
235 Value::U128(n) => Slot::Scalar(Value::U128(n)),
236 Value::F64(n) => Slot::Scalar(Value::F64(n)),
237 Value::Char(c) => Slot::Scalar(Value::Char(c)),
238 other => Slot::Text(self.push_text(|t| write!(t, "{other}"))),
239 };
240 self.scratch.push((key, slot));
241 }
242}
243
244struct EventVisitor<'a>(&'a mut Arena);
246
247impl EventVisitor<'_> {
248 fn scalar(&mut self, field: &TracingField, value: Value<'static>) {
249 self.0
250 .event
251 .push((Key::Static(field.name()), Slot::Scalar(value)));
252 }
253
254 fn text(&mut self, field: &TracingField, f: impl FnOnce(&mut String) -> fmt::Result) {
255 let range = self.0.push_text(f);
256 if field.name() == "message" {
257 self.0.message = Some(range);
258 } else {
259 self.0
260 .event
261 .push((Key::Static(field.name()), Slot::Text(range)));
262 }
263 }
264}
265
266impl Visit for EventVisitor<'_> {
267 fn record_f64(&mut self, field: &TracingField, value: f64) {
268 self.scalar(field, Value::F64(value));
269 }
270 fn record_i64(&mut self, field: &TracingField, value: i64) {
271 self.scalar(field, Value::I64(value));
272 }
273 fn record_u64(&mut self, field: &TracingField, value: u64) {
274 self.scalar(field, Value::U64(value));
275 }
276 fn record_i128(&mut self, field: &TracingField, value: i128) {
277 self.scalar(field, Value::I128(value));
278 }
279 fn record_u128(&mut self, field: &TracingField, value: u128) {
280 self.scalar(field, Value::U128(value));
281 }
282 fn record_bool(&mut self, field: &TracingField, value: bool) {
283 self.scalar(field, Value::Bool(value));
284 }
285 fn record_str(&mut self, field: &TracingField, value: &str) {
286 self.text(field, |t| t.write_str(value));
287 }
288 fn record_bytes(&mut self, field: &TracingField, value: &[u8]) {
289 self.text(field, |t| write_hex(t, value));
290 }
291 fn record_error(&mut self, field: &TracingField, value: &(dyn std::error::Error + 'static)) {
292 self.text(field, |t| write_error_chain(t, value));
293 }
294 fn record_debug(&mut self, field: &TracingField, value: &dyn fmt::Debug) {
295 self.text(field, |t| write!(t, "{value:?}"));
296 }
297}
298
299thread_local! {
300 static ARENA: RefCell<Arena> = const { RefCell::new(Arena::new()) };
301}
302
303const MAX_RETAINED_TEXT: usize = 64 * 1024;
305
306impl<S> Layer<S> for TracingLayer
307where
308 S: Subscriber + for<'lookup> LookupSpan<'lookup>,
309{
310 fn on_new_span(&self, attrs: &Attributes<'_>, id: &Id, ctx: Context<'_, S>) {
311 let Some(span) = ctx.span(id) else {
312 return;
313 };
314 let mut fields = SpanFields::default();
315 attrs.record(&mut fields);
316 span.extensions_mut().insert(fields);
317 }
318
319 fn on_record(&self, id: &Id, values: &SpanRecord<'_>, ctx: Context<'_, S>) {
320 let Some(span) = ctx.span(id) else {
321 return;
322 };
323 let mut extensions = span.extensions_mut();
324 if let Some(fields) = extensions.get_mut::<SpanFields>() {
325 values.record(fields);
326 } else {
327 let mut fields = SpanFields::default();
328 values.record(&mut fields);
329 extensions.insert(fields);
330 }
331 }
332
333 fn on_event(&self, event: &Event<'_>, ctx: Context<'_, S>) {
334 let meta = event.metadata();
335 let level = from_tracing_level(*meta.level());
336 let target = meta.target();
337 if !self.logger.enabled(target, level) {
338 return;
339 }
340 let pooled = ARENA.try_with(|cell| {
341 let mut arena = cell.try_borrow_mut().ok()?;
342 self.emit(&mut arena, event, &ctx, level);
343 if arena.text.capacity() > MAX_RETAINED_TEXT {
344 arena.text = String::new();
345 }
346 Some(())
347 });
348 if !matches!(pooled, Ok(Some(()))) {
349 self.emit(&mut Arena::new(), event, &ctx, level);
351 }
352 }
353}
354
355impl TracingLayer {
356 fn emit<S>(&self, arena: &mut Arena, event: &Event<'_>, ctx: &Context<'_, S>, level: Level)
357 where
358 S: Subscriber + for<'lookup> LookupSpan<'lookup>,
359 {
360 arena.clear();
361 event.record(&mut EventVisitor(arena));
362 if let Some(scope) = ctx.event_scope(event) {
363 for span in scope {
364 let extensions = span.extensions();
365 if let Some(fields) = extensions.get::<SpanFields>() {
366 arena.blocks.push(arena.scratch.len());
367 for field in &fields.0 {
368 arena.add_span_field(field);
369 }
370 }
371 }
372 arena.order_spans_root_first();
373 }
374
375 let meta = event.metadata();
376 let mut metadata = Metadata::new(level, meta.target());
377 if let (Some(file), Some(line)) = (meta.file(), meta.line()) {
378 metadata = metadata.with_location(file, line);
379 }
380 let arena = &*arena;
381 let message = arena.message.as_ref().map_or("", |r| arena.text(r));
382 with_fields(arena, &arena.event, |fields| {
383 with_fields(arena, &arena.spans, |spans| {
384 let _reported = self.logger.emit_unchecked(metadata, message, fields, spans);
386 });
387 });
388 }
389}
390
391fn with_fields(arena: &Arena, items: &[(Key, Slot)], f: impl FnOnce(&[Field<'_>])) {
394 const EMPTY: Field<'static> = Field {
395 key: "",
396 value: Value::Null,
397 };
398 if items.len() <= 16 {
399 let mut buf = [EMPTY; 16];
400 for (slot, (key, value)) in buf.iter_mut().zip(items) {
401 *slot = Field {
402 key: arena.key(key),
403 value: arena.value(value),
404 };
405 }
406 f(&buf[..items.len()]);
407 } else {
408 let fields: Vec<Field<'_>> = items
409 .iter()
410 .map(|(key, value)| Field {
411 key: arena.key(key),
412 value: arena.value(value),
413 })
414 .collect();
415 f(&fields);
416 }
417}
418
419#[cfg(all(test, feature = "logfmt"))]
420mod tests {
421 use super::*;
422 use crate::format::LogfmtFormat;
423 use std::io::Write;
424 use std::sync::{Arc, Mutex};
425 use tracing_subscriber::layer::SubscriberExt;
426
427 #[derive(Default, Clone)]
428 struct Capture(Arc<Mutex<Vec<u8>>>);
429 impl Write for Capture {
430 fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
431 self.0.lock().unwrap().extend_from_slice(buf);
432 Ok(buf.len())
433 }
434 fn flush(&mut self) -> std::io::Result<()> {
435 Ok(())
436 }
437 }
438 impl Capture {
439 fn take(&self) -> String {
440 String::from_utf8(std::mem::take(&mut *self.0.lock().unwrap())).unwrap()
441 }
442 }
443
444 fn layer(level: Level) -> (TracingLayer, Capture) {
445 let out = Capture::default();
446 let logger = Logger::builder()
447 .level(level)
448 .no_timestamps()
449 .no_context()
450 .writer(out.clone(), LogfmtFormat::new())
451 .build();
452 (TracingLayer::new(logger), out)
453 }
454
455 #[test]
456 fn test_tracing_layer_event_fields_and_message() {
457 let (layer, out) = layer(Level::Trace);
458 let subscriber = tracing_subscriber::registry().with(layer);
459 tracing::subscriber::with_default(subscriber, || {
460 let err = std::io::Error::other("reset");
461 tracing::warn!(
462 target: "app::net",
463 status = 503_u16,
464 retry = true,
465 ratio = 0.25,
466 peer = %"10.0.0.1",
467 tags = ?["a", "b"],
468 big = u128::MAX,
469 error = &err as &(dyn std::error::Error + 'static),
470 "upstream {} failed", "db"
471 );
472 });
473 let text = out.take();
474 assert!(
475 text.starts_with("level=warn target=app::net message=\"upstream db failed\" file="),
476 "{text}"
477 );
478 assert!(
479 text.contains(" status=503 retry=true ratio=0.25 peer=10.0.0.1 "),
480 "{text}"
481 );
482 assert!(text.contains("tags=\"[\\\"a\\\", \\\"b\\\"]\""), "{text}");
483 assert!(text.contains(&format!("big={}", u128::MAX)), "{text}");
484 assert!(text.contains("error=reset"), "{text}");
485 }
486
487 #[test]
488 fn test_tracing_layer_attaches_span_fields_with_shadowing() {
489 let (layer, out) = layer(Level::Trace);
490 let subscriber = tracing_subscriber::registry().with(layer);
491 tracing::subscriber::with_default(subscriber, || {
492 let outer = tracing::info_span!(
493 "request",
494 id = 7_u64,
495 user = "alice",
496 tenant = tracing::field::Empty
497 );
498 let _o = outer.enter();
499 let _span = outer.record("tenant", "acme");
500 let inner = tracing::debug_span!("db", user = "svc", bytes = tracing::field::Empty);
501 let _i = inner.enter();
502 let _span = inner.record("bytes", &b"\x01\xff"[..]);
503 tracing::info!(user = "event", "query");
504 tracing::info!("no user field on the event");
505 });
506 let text = out.take();
507 let mut lines = text.lines();
508 let first = lines.next().unwrap_or_default();
509 let second = lines.next().unwrap_or_default();
510 assert!(
511 second.contains(" user=svc"),
512 "inner span shadows outer: {second}"
513 );
514 assert!(
515 second.find(" id=7").unwrap() < second.find(" user=svc").unwrap(),
516 "root span fields come first: {second}"
517 );
518 let text = first.to_owned();
519 assert!(text.contains(" id=7 "), "{text}");
520 assert!(text.contains(" tenant=acme "), "{text}");
521 assert!(text.contains(" bytes=01ff"), "{text}");
522 assert_eq!(text.matches("user=").count(), 1, "{text}");
523 assert!(text.contains(" user=event"), "{text}");
524 }
525
526 #[test]
527 fn test_tracing_layer_applies_logger_filter() {
528 let (layer, out) = layer(Level::Warn);
529 assert_eq!(layer.logger().min_level(), Level::Warn);
530 let subscriber = tracing_subscriber::registry().with(layer);
531 tracing::subscriber::with_default(subscriber, || {
532 tracing::info!("dropped");
533 tracing::error!("kept");
534 tracing::trace!("dropped too");
535 tracing::debug!("dropped as well");
536 });
537 let text = out.take();
538 assert!(!text.contains("dropped"), "{text}");
539 assert!(text.contains("message=kept"), "{text}");
540 }
541
542 #[test]
543 fn test_tracing_layer_many_fields_spill_to_heap() {
544 let (layer, out) = layer(Level::Trace);
545 let subscriber = tracing_subscriber::registry().with(layer);
546 tracing::subscriber::with_default(subscriber, || {
547 tracing::info!(
548 a0 = 0,
549 a1 = 1,
550 a2 = 2,
551 a3 = 3,
552 a4 = 4,
553 a5 = 5,
554 a6 = 6,
555 a7 = 7,
556 a8 = 8,
557 a9 = 9,
558 a10 = 10,
559 a11 = 11,
560 a12 = 12,
561 a13 = 13,
562 a14 = 14,
563 a15 = 15,
564 a16 = 16,
565 a17 = 17,
566 "wide"
567 );
568 });
569 let text = out.take();
570 for i in 0..18 {
571 assert!(text.contains(&format!(" a{i}={i}")), "a{i}: {text}");
572 }
573 }
574
575 #[test]
576 fn test_tracing_level_mapping() {
577 assert_eq!(from_tracing_level(tracing_core::Level::TRACE), Level::Trace);
578 assert_eq!(from_tracing_level(tracing_core::Level::DEBUG), Level::Debug);
579 assert_eq!(from_tracing_level(tracing_core::Level::INFO), Level::Info);
580 assert_eq!(from_tracing_level(tracing_core::Level::WARN), Level::Warn);
581 assert_eq!(from_tracing_level(tracing_core::Level::ERROR), Level::Error);
582 }
583}