mycelium_trace/
lib.rs

1#![cfg_attr(not(test), no_std)]
2#![feature(doc_cfg, doc_auto_cfg)]
3
4#[cfg(feature = "alloc")]
5extern crate alloc;
6#[cfg(feature = "alloc")]
7pub mod buf;
8pub mod color;
9#[cfg(feature = "embedded-graphics")]
10pub mod embedded_graphics;
11pub mod writer;
12
13use crate::{
14    color::{Color, SetColor},
15    writer::MakeWriter,
16};
17use core::sync::atomic::{AtomicU64, Ordering};
18use mycelium_util::fmt::{self, Write};
19use tracing_core::{field, span, Event, Level, Metadata};
20
21#[derive(Debug)]
22pub struct Subscriber<D, S = Option<writer::NoWriter>> {
23    display: Output<D, VGA_BIT>,
24    serial: Output<S, SERIAL_BIT>,
25    next_id: AtomicU64,
26}
27
28#[derive(Debug)]
29struct Output<W, const BIT: u64> {
30    make_writer: W,
31    cfg: OutputCfg,
32}
33
34#[derive(Debug)]
35struct OutputCfg {
36    line_len: usize,
37    indent: AtomicU64,
38    indent_cfg: IndentCfg,
39}
40
41#[derive(Debug, Copy, Clone)]
42struct IndentCfg {
43    event: &'static str,
44    indent: &'static str,
45    new_span: &'static str,
46}
47
48#[derive(Debug, Copy, Clone, Eq, PartialEq)]
49enum IndentKind {
50    Event,
51    NewSpan,
52    Indent,
53}
54
55#[derive(Debug)]
56struct Writer<'a, W: Write> {
57    cfg: &'a OutputCfg,
58    current_line: usize,
59    writer: W,
60}
61
62#[derive(Debug)]
63struct WriterPair<'a, D: Write, S: Write> {
64    display: Option<Writer<'a, D>>,
65    serial: Option<Writer<'a, S>>,
66}
67
68struct Visitor<'writer, W> {
69    writer: &'writer mut W,
70    seen: bool,
71    newline: bool,
72    comma: bool,
73    altmode: bool,
74}
75
76// === impl Subscriber ===
77
78impl<D> Default for Subscriber<D>
79where
80    for<'a> D: MakeWriter<'a> + 'static,
81    for<'a> <D as MakeWriter<'a>>::Writer: SetColor,
82    D: Default,
83{
84    fn default() -> Self {
85        Self::display_only(D::default())
86    }
87}
88
89const SERIAL_BIT: u64 = 1 << 0;
90const VGA_BIT: u64 = 1 << 1;
91const _ACTUAL_ID_BITS: u64 = !(SERIAL_BIT | VGA_BIT);
92
93impl<D, S> Subscriber<D, S> {
94    pub fn display_only(display: D) -> Self
95    where
96        for<'a> D: MakeWriter<'a> + 'static,
97        // for<'a> <D as MakeWriter<'a>>::Writer: SetColor,
98        for<'a> S: MakeWriter<'a> + 'static,
99        // for<'a> <S as MakeWriter<'a>>::Writer: SetColor,
100        S: Default,
101    {
102        Self {
103            display: Output::new(display, Self::DISPLAY_INDENT_CFG),
104            serial: Output::new(S::default(), Self::SERIAL_INDENT_CFG),
105            next_id: AtomicU64::new(0),
106        }
107    }
108
109    pub fn with_serial<S2>(self, port: S2) -> Subscriber<D, S2>
110    where
111        for<'a> S2: MakeWriter<'a> + 'static,
112        // for<'a> <S2 as MakeWriter<'a>>::Writer: SetColor,
113    {
114        Subscriber {
115            serial: Output::new(port, Self::SERIAL_INDENT_CFG),
116            display: self.display,
117            next_id: self.next_id,
118        }
119    }
120
121    const SERIAL_INDENT_CFG: IndentCfg = IndentCfg {
122        indent: "│",
123        new_span: "┌",
124        event: "├",
125    };
126    const DISPLAY_INDENT_CFG: IndentCfg = IndentCfg {
127        indent: "",
128        new_span: "> ",
129        event: " ",
130    };
131}
132
133impl<D, S> Subscriber<D, S> {
134    fn writer<'a>(&'a self, meta: &Metadata<'_>) -> WriterPair<'a, D::Writer, S::Writer>
135    where
136        D: MakeWriter<'a>,
137        S: MakeWriter<'a>,
138    {
139        WriterPair {
140            display: self.display.writer(meta),
141            serial: self.serial.writer(meta),
142        }
143    }
144}
145
146impl<D, S> tracing_core::Collect for Subscriber<D, S>
147where
148    for<'a> D: MakeWriter<'a> + 'static,
149    for<'a> <D as MakeWriter<'a>>::Writer: SetColor,
150    for<'a> S: MakeWriter<'a> + 'static,
151    for<'a> <S as MakeWriter<'a>>::Writer: SetColor,
152{
153    fn enabled(&self, meta: &Metadata) -> bool {
154        self.display.enabled(meta) || self.serial.enabled(meta)
155    }
156
157    fn new_span(&self, span: &span::Attributes) -> span::Id {
158        let meta = span.metadata();
159        let id = {
160            let mut id = self.next_id.fetch_add(1, Ordering::Acquire);
161            if id & SERIAL_BIT != 0 {
162                // we have used a _lot_ of span IDs...presumably the low-numbered
163                // spans are gone by now.
164                self.next_id.store(0, Ordering::Release);
165            }
166
167            if self.display.enabled(meta) {
168                // mark that this span should be written to the VGA buffer.
169                id |= VGA_BIT;
170            }
171
172            if self.serial.enabled(meta) {
173                // mark that this span should be written to the serial port buffer.
174                id |= SERIAL_BIT;
175            }
176            span::Id::from_u64(id)
177        };
178
179        let mut writer = self.writer(meta);
180        let _ = write_timestamp(&mut writer);
181        let _ = write_level(&mut writer, meta.level());
182        let _ = writer.indent_initial(IndentKind::NewSpan);
183        let _ = writer.with_bold().write_str(meta.name());
184        let _ = writer.with_fg_color(Color::BrightBlack).write_str(": ");
185
186        // ensure the span's fields are nicely indented if they wrap by
187        // "entering" and then "exiting"`````findent`
188        // the span.
189        self.enter(&id);
190        span.record(&mut Visitor::new(&mut writer, false));
191        self.exit(&id);
192
193        id
194    }
195
196    fn record(&self, _span: &span::Id, _values: &span::Record) {
197        // nop for now
198    }
199
200    fn record_follows_from(&self, _span: &span::Id, _follows: &span::Id) {
201        // nop for now
202    }
203
204    fn event(&self, event: &Event) {
205        let meta = event.metadata();
206        let mut writer = self.writer(meta);
207        let _ = write_timestamp(&mut writer);
208        let _ = write_level(&mut writer, meta.level());
209        let _ = writer.indent_initial(IndentKind::Event);
210        let _ = write!(
211            writer.with_fg_color(Color::BrightBlack),
212            "{}: ",
213            meta.target()
214        );
215        event.record(&mut Visitor::new(&mut writer, true));
216    }
217
218    fn enter(&self, span: &span::Id) {
219        let bits = span.into_u64();
220        self.display.enter(bits);
221        self.serial.enter(bits);
222    }
223
224    fn exit(&self, span: &span::Id) {
225        let bits = span.into_u64();
226        self.display.exit(bits);
227        self.serial.exit(bits);
228    }
229
230    fn current_span(&self) -> tracing_core::span::Current {
231        // TODO(eliza): fix
232        tracing_core::span::Current::none()
233    }
234}
235
236// === impl Output ===
237
238impl<W, const BIT: u64> Output<W, BIT> {
239    fn new<'a>(make_writer: W, indent_cfg: IndentCfg) -> Self
240    where
241        W: MakeWriter<'a>,
242    {
243        let cfg = OutputCfg {
244            line_len: make_writer.line_len() - 16,
245            indent: AtomicU64::new(0),
246            indent_cfg,
247        };
248        Self { make_writer, cfg }
249    }
250
251    #[inline]
252    fn enabled<'a>(&'a self, metadata: &tracing_core::Metadata<'_>) -> bool
253    where
254        W: MakeWriter<'a>,
255    {
256        self.make_writer.enabled(metadata)
257    }
258
259    #[inline]
260    fn enter(&self, id: u64) {
261        if id & BIT != 0 {
262            self.cfg.indent.fetch_add(1, Ordering::Release);
263        }
264    }
265
266    #[inline]
267    fn exit(&self, id: u64) {
268        if id & BIT != 0 {
269            self.cfg.indent.fetch_sub(1, Ordering::Release);
270        }
271    }
272
273    fn writer<'a>(&'a self, meta: &Metadata<'_>) -> Option<Writer<'a, W::Writer>>
274    where
275        W: MakeWriter<'a>,
276    {
277        let writer = self.make_writer.make_writer_for(meta)?;
278        Some(Writer {
279            current_line: 0,
280            writer,
281            cfg: &self.cfg,
282        })
283    }
284}
285
286// === impl WriterPair ===
287
288impl<D, S> SetColor for WriterPair<'_, D, S>
289where
290    D: Write + SetColor,
291    S: Write + SetColor,
292{
293    fn set_fg_color(&mut self, color: Color) {
294        if let Some(ref mut w) = self.display {
295            w.set_fg_color(color)
296        }
297        if let Some(ref mut w) = self.serial {
298            w.set_fg_color(color)
299        };
300    }
301
302    fn fg_color(&self) -> Color {
303        self.display
304            .as_ref()
305            .map(SetColor::fg_color)
306            .or_else(|| self.serial.as_ref().map(SetColor::fg_color))
307            .unwrap_or(Color::Default)
308    }
309
310    fn set_bold(&mut self, bold: bool) {
311        if let Some(ref mut w) = self.display {
312            w.set_bold(bold)
313        }
314        if let Some(ref mut w) = self.serial {
315            w.set_bold(bold)
316        };
317    }
318}
319
320impl<D, S> Write for WriterPair<'_, D, S>
321where
322    D: Write,
323    S: Write,
324{
325    fn write_str(&mut self, s: &str) -> fmt::Result {
326        let err = if let Some(ref mut display) = self.display {
327            display.write_str(s)
328        } else {
329            Ok(())
330        };
331        if let Some(ref mut serial) = self.serial {
332            serial.write_str(s)?;
333        }
334        err
335    }
336
337    fn write_fmt(&mut self, args: fmt::Arguments<'_>) -> fmt::Result {
338        let err = if let Some(ref mut display) = self.display {
339            display.write_fmt(args)
340        } else {
341            Ok(())
342        };
343        if let Some(ref mut serial) = self.serial {
344            serial.write_fmt(args)?;
345        }
346        err
347    }
348}
349
350impl<D, S> WriterPair<'_, D, S>
351where
352    D: Write,
353    S: Write,
354{
355    fn indent_initial(&mut self, kind: IndentKind) -> fmt::Result {
356        let err = if let Some(ref mut display) = self.display {
357            // "rust has try-catch syntax lol"
358            (|| {
359                display.indent(kind)?;
360                Ok(())
361            })()
362        } else {
363            Ok(())
364        };
365        if let Some(ref mut serial) = self.serial {
366            serial.indent(kind)?;
367        }
368        err
369    }
370}
371
372// ==== impl Writer ===
373
374impl<W: Write> Writer<'_, W> {
375    fn indent(&mut self, kind: IndentKind) -> fmt::Result {
376        let indent = self.cfg.indent.load(Ordering::Acquire);
377        self.write_indent(" ")?;
378
379        for i in 1..=indent {
380            let indent_str = match (i, kind) {
381                (i, IndentKind::Event) if i == indent => self.cfg.indent_cfg.event,
382                _ => self.cfg.indent_cfg.indent,
383            };
384            self.write_indent(indent_str)?;
385        }
386
387        if kind == IndentKind::NewSpan {
388            self.write_indent(self.cfg.indent_cfg.new_span)?;
389        }
390
391        Ok(())
392    }
393
394    fn write_indent(&mut self, chars: &'static str) -> fmt::Result {
395        self.writer.write_str(chars)?;
396        self.current_line += chars.len();
397        Ok(())
398    }
399
400    fn write_newline(&mut self) -> fmt::Result {
401        // including width of the 16-character timestamp bit
402        self.writer.write_str("                  ")?;
403        self.current_line = 3;
404        self.indent(IndentKind::Indent)
405    }
406
407    fn finish(&mut self) -> fmt::Result {
408        self.writer.write_char('\n')
409    }
410}
411
412impl<W> Write for Writer<'_, W>
413where
414    W: Write,
415{
416    fn write_str(&mut self, s: &str) -> fmt::Result {
417        let lines = s.split_inclusive('\n');
418        for line in lines {
419            let mut line = line;
420            while self.current_line + line.len() >= self.cfg.line_len {
421                let offset = if let Some(last_ws) = line[..self.cfg.line_len - self.current_line]
422                    .chars()
423                    .rev()
424                    .position(|c| c.is_whitespace())
425                {
426                    // found a nice whitespace to break on!
427                    self.writer.write_str(&line[..last_ws])?;
428                    last_ws
429                } else {
430                    0
431                };
432                self.writer.write_char('\n')?;
433                self.write_newline()?;
434                self.writer.write_str(" ")?;
435                self.current_line += 1;
436                line = &line[offset..];
437            }
438            self.writer.write_str(line)?;
439            if line.ends_with('\n') {
440                self.write_newline()?;
441                self.writer.write_char(' ')?;
442            }
443            self.current_line += line.len();
444        }
445
446        Ok(())
447    }
448
449    fn write_char(&mut self, ch: char) -> fmt::Result {
450        self.writer.write_char(ch)?;
451        if ch == '\n' {
452            self.write_newline()
453        } else {
454            Ok(())
455        }
456    }
457}
458
459impl<W> SetColor for Writer<'_, W>
460where
461    W: Write + SetColor,
462{
463    fn fg_color(&self) -> Color {
464        self.writer.fg_color()
465    }
466
467    fn set_fg_color(&mut self, color: Color) {
468        self.writer.set_fg_color(color);
469    }
470
471    fn set_bold(&mut self, bold: bool) {
472        self.writer.set_bold(bold)
473    }
474}
475
476impl<W: Write> Drop for Writer<'_, W> {
477    fn drop(&mut self) {
478        let _ = self.finish();
479    }
480}
481
482#[inline]
483fn write_level<W>(w: &mut W, level: &Level) -> fmt::Result
484where
485    W: fmt::Write + SetColor,
486{
487    w.write_char('[')?;
488    match *level {
489        Level::TRACE => w.with_fg_color(Color::BrightBlue).write_char('*'),
490        Level::DEBUG => w.with_fg_color(Color::BrightCyan).write_char('?'),
491        Level::INFO => w.with_fg_color(Color::BrightGreen).write_char('i'),
492        Level::WARN => w.with_fg_color(Color::BrightYellow).write_char('!'),
493        Level::ERROR => w.with_fg_color(Color::BrightRed).write_char('x'),
494    }?;
495    w.write_char(']')
496}
497
498#[inline]
499fn write_timestamp<W>(w: &mut W) -> fmt::Result
500where
501    W: fmt::Write + SetColor,
502{
503    w.write_char('[')?;
504    if let Ok(now) = maitake::time::Instant::try_now() {
505        let now = now.elapsed();
506        write!(
507            w.with_fg_color(Color::BrightBlack),
508            "{:>6}.{:06}",
509            now.as_secs(),
510            now.subsec_micros()
511        )?;
512    } else {
513        write!(w.with_fg_color(Color::BrightBlack), "     ?.??????")?;
514    }
515    w.write_char(']')?;
516    Ok(())
517}
518
519impl<'writer, W> Visitor<'writer, W>
520where
521    W: fmt::Write,
522    &'writer mut W: SetColor,
523{
524    fn new(writer: &'writer mut W, altmode: bool) -> Self {
525        Self {
526            writer,
527            seen: false,
528            comma: false,
529            newline: false,
530            altmode,
531        }
532    }
533
534    fn record_inner(&mut self, field: &field::Field, val: &dyn fmt::Debug) {
535        // XXX(eliza): sad and gross hack
536        struct HasWrittenNewline<'a, W> {
537            writer: &'a mut W,
538            has_written_newline: bool,
539            has_written_punct: bool,
540        }
541
542        impl<W: fmt::Write> fmt::Write for HasWrittenNewline<'_, W> {
543            #[inline]
544            fn write_str(&mut self, s: &str) -> fmt::Result {
545                self.has_written_punct = s.ends_with(|ch: char| ch.is_ascii_punctuation());
546                if s.contains('\n') {
547                    self.has_written_newline = true;
548                }
549                self.writer.write_str(s)
550            }
551        }
552
553        impl<W: fmt::Write> SetColor for HasWrittenNewline<'_, W>
554        where
555            W: SetColor,
556        {
557            fn fg_color(&self) -> Color {
558                self.writer.fg_color()
559            }
560
561            fn set_fg_color(&mut self, color: Color) {
562                self.writer.set_fg_color(color);
563            }
564
565            fn set_bold(&mut self, bold: bool) {
566                self.writer.set_bold(bold)
567            }
568        }
569
570        let mut writer = HasWrittenNewline {
571            writer: &mut self.writer,
572            has_written_newline: false,
573            has_written_punct: false,
574        };
575        let nl = if self.newline { '\n' } else { ' ' };
576
577        if field.name() == "message" {
578            if self.seen {
579                let _ = write!(writer.with_bold(), "{nl}{val:?}");
580            } else {
581                let _ = write!(writer.with_bold(), "{val:?}");
582                self.comma = !writer.has_written_punct;
583            }
584            self.seen = true;
585            return;
586        }
587
588        if self.comma {
589            let _ = writer.with_fg_color(Color::BrightBlack).write_char(',');
590        }
591
592        if self.seen {
593            let _ = writer.write_char(nl);
594        }
595
596        if !self.comma {
597            self.seen = true;
598            self.comma = true;
599        }
600
601        // pretty-print the name with dots in the punctuation color
602        let mut name_pieces = field.name().split('.');
603        if let Some(piece) = name_pieces.next() {
604            let _ = writer.write_str(piece);
605            for piece in name_pieces {
606                let _ = writer.with_fg_color(Color::BrightBlack).write_char('.');
607                let _ = writer.write_str(piece);
608            }
609        }
610
611        let _ = writer.with_fg_color(Color::BrightBlack).write_char('=');
612        let _ = write!(writer, "{val:?}");
613        self.newline |= writer.has_written_newline;
614    }
615}
616
617impl<'writer, W> field::Visit for Visitor<'writer, W>
618where
619    W: fmt::Write,
620    &'writer mut W: SetColor,
621{
622    #[inline]
623    fn record_u64(&mut self, field: &field::Field, val: u64) {
624        self.record_inner(field, &val)
625    }
626
627    #[inline]
628    fn record_i64(&mut self, field: &field::Field, val: i64) {
629        self.record_inner(field, &val)
630    }
631
632    #[inline]
633    fn record_bool(&mut self, field: &field::Field, val: bool) {
634        self.record_inner(field, &val)
635    }
636
637    #[inline]
638    fn record_str(&mut self, field: &field::Field, val: &str) {
639        if val.len() >= 70 {
640            self.newline = true;
641        }
642        self.record_inner(field, &val)
643    }
644
645    fn record_debug(&mut self, field: &field::Field, val: &dyn fmt::Debug) {
646        if self.altmode {
647            self.record_inner(field, &fmt::alt(val))
648        } else {
649            self.record_inner(field, val)
650        }
651    }
652}