tracing_proc_macros_ink/
lib.rs

1#![doc = include_str!("../README.md")]
2#![feature(proc_macro_diagnostic)]
3#![feature(proc_macro_tracked_env)]
4
5extern crate proc_macro;
6
7use std::{io, sync::Once};
8
9use proc_macro::Diagnostic;
10use tracing::Level;
11use tracing_subscriber::{
12  fmt::{self, MakeWriter, time},
13  prelude::*,
14};
15
16/// Converts a `tracing` log level to a `proc_macro` log level.
17const fn convert_level(level: Level) -> proc_macro::Level {
18  match level {
19    Level::ERROR => proc_macro::Level::Error,
20    Level::WARN => proc_macro::Level::Warning,
21    Level::INFO => proc_macro::Level::Note,
22    Level::DEBUG | Level::TRACE => proc_macro::Level::Help,
23  }
24}
25
26/// A [`io::Write`] implementation that writes `tracing` logs to `proc_macro` diagnostics.
27/// Users should not use this directly, but instead pass an instance of [`RustcDiagnosticsMakeWriter`] to [`fmt::Layer::with_writer`].
28pub struct RustcDiagnosticsWriter {
29  level: Level,
30}
31
32impl RustcDiagnosticsWriter {
33  const fn new(level: Level) -> Self {
34    Self { level }
35  }
36}
37
38impl io::Write for RustcDiagnosticsWriter {
39  fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
40    let message = String::from_utf8_lossy(buf);
41    Diagnostic::new(convert_level(self.level), message).emit();
42    Ok(buf.len())
43  }
44
45  fn flush(&mut self) -> io::Result<()> {
46    Ok(())
47  }
48}
49
50/// A [`MakeWriter`] implementation that writes `tracing` logs to `proc_macro` diagnostics.
51pub struct RustcDiagnosticsMakeWriter;
52
53impl<'a> MakeWriter<'a> for RustcDiagnosticsMakeWriter {
54  type Writer = RustcDiagnosticsWriter;
55
56  fn make_writer(&'a self) -> Self::Writer {
57    RustcDiagnosticsWriter::new(Level::INFO)
58  }
59
60  fn make_writer_for(&'a self, meta: &tracing::Metadata<'_>) -> Self::Writer {
61    let level = *meta.level();
62    RustcDiagnosticsWriter::new(level)
63  }
64}
65
66/// Sets up a default logging arrangement for the `proc_macro`.
67/// This function should be called once in every top level function of your `proc_macro` crate.
68/// To use a custom logging setup take a look at the source code of this function.
69///
70/// When writing your own setup function, make sure to use a `static SETUP_LOGGER: Once = Once::new();` to ensure that the setup is only done once.
71#[cfg(feature = "default-setup")]
72pub fn proc_macro_logger_default_setup() {
73  static SETUP_LOGGER: Once = Once::new();
74  SETUP_LOGGER.call_once(|| {
75    const FILE_INFO: bool = true;
76    tracing_subscriber::registry()
77      .with(
78        fmt::Layer::default()
79          .with_ansi(false)
80          .with_file(FILE_INFO)
81          .with_line_number(FILE_INFO)
82          .with_timer(time::OffsetTime::local_rfc_3339().expect("Could not get local offset!"))
83          .with_writer(RustcDiagnosticsMakeWriter),
84      )
85      .with(
86        tracing_subscriber::EnvFilter::builder()
87          .parse_lossy(proc_macro::tracked_env::var("RUST_LOG").unwrap_or_default()),
88      )
89      .init();
90    #[cfg(feature = "tracing-panic")]
91    std::panic::set_hook(Box::new(tracing_panic::panic_hook));
92  });
93}