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
76impl<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> S: MakeWriter<'a> + 'static,
99 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 {
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 self.next_id.store(0, Ordering::Release);
165 }
166
167 if self.display.enabled(meta) {
168 id |= VGA_BIT;
170 }
171
172 if self.serial.enabled(meta) {
173 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 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 }
199
200 fn record_follows_from(&self, _span: &span::Id, _follows: &span::Id) {
201 }
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 tracing_core::span::Current::none()
233 }
234}
235
236impl<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
286impl<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 (|| {
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
372impl<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 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 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 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 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}