/ cli / src / helpers / dynamic_format.rs
dynamic_format.rs
  1  // Copyright (c) 2025 ADnet Contributors
  2  // This file is part of the AlphaOS library.
  3  
  4  // Licensed under the Apache License, Version 2.0 (the "License");
  5  // you may not use this file except in compliance with the License.
  6  // You may obtain a copy of the License at:
  7  
  8  // http://www.apache.org/licenses/LICENSE-2.0
  9  
 10  // Unless required by applicable law or agreed to in writing, software
 11  // distributed under the License is distributed on an "AS IS" BASIS,
 12  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 13  // See the License for the specific language governing permissions and
 14  // limitations under the License.
 15  
 16  use std::sync::{
 17      Arc,
 18      atomic::{AtomicBool, Ordering},
 19  };
 20  
 21  use time::{
 22      OffsetDateTime,
 23      format_description::{self, OwnedFormatItem},
 24  };
 25  use tracing::{Event, Subscriber};
 26  use tracing_subscriber::{
 27      fmt::{FmtContext, FormatEvent, FormatFields, format::Writer},
 28      registry::LookupSpan,
 29  };
 30  
 31  /// A formatter that can switch between the default formatter and the DIM style.
 32  pub struct DynamicFormatter {
 33      dim_format: DimFormat,
 34      default_format: tracing_subscriber::fmt::format::Format,
 35      // This is the shutdown flag. When set to true, switch to the DIM format.
 36      dim: Arc<AtomicBool>,
 37  }
 38  
 39  impl<S, N> FormatEvent<S, N> for DynamicFormatter
 40  where
 41      S: Subscriber + for<'a> LookupSpan<'a>,
 42      N: for<'a> FormatFields<'a> + 'static,
 43  {
 44      fn format_event(&self, ctx: &FmtContext<'_, S, N>, writer: Writer<'_>, event: &Event<'_>) -> std::fmt::Result {
 45          if self.dim.load(Ordering::Relaxed) {
 46              self.dim_format.format_event(ctx, writer, event)
 47          } else {
 48              self.default_format.format_event(ctx, writer, event)
 49          }
 50      }
 51  }
 52  
 53  impl DynamicFormatter {
 54      pub fn new(dim: Arc<AtomicBool>) -> Self {
 55          let dim_format = DimFormat::new();
 56          let default_format = tracing_subscriber::fmt::format::Format::default();
 57          Self { dim_format, default_format, dim }
 58      }
 59  }
 60  
 61  struct DimFormat {
 62      fmt: OwnedFormatItem,
 63  }
 64  
 65  /// A custom format for the DIM style.
 66  /// This formatter is quite basic and does not support all the features of the default formatter.
 67  /// It does support all the default fields of the default formatter.
 68  impl DimFormat {
 69      fn new() -> Self {
 70          let format =
 71              format_description::parse_owned::<2>("[year]-[month]-[day]T[hour]:[minute]:[second].[subsecond digits:6]Z")
 72                  .expect("failed to set timestampt format");
 73          Self { fmt: format }
 74      }
 75  }
 76  
 77  impl<S, N> FormatEvent<S, N> for DimFormat
 78  where
 79      S: Subscriber + for<'a> LookupSpan<'a>,
 80      N: for<'a> FormatFields<'a> + 'static,
 81  {
 82      /// Format like the `Full` format, but using the DIM tty style.
 83      fn format_event(&self, ctx: &FmtContext<'_, S, N>, mut writer: Writer<'_>, event: &Event<'_>) -> std::fmt::Result {
 84          // set the DIM style if we are in TTY mode
 85          if writer.has_ansi_escapes() {
 86              write!(writer, "\x1b[2m")?;
 87          }
 88  
 89          let date_time = OffsetDateTime::now_utc();
 90          write!(writer, "{}  ", date_time.format(&self.fmt).map_err(|_| std::fmt::Error)?)?;
 91  
 92          let meta = event.metadata();
 93          let fmt_level = match *meta.level() {
 94              tracing::Level::ERROR => "ERROR",
 95              tracing::Level::WARN => "WARN ",
 96              tracing::Level::INFO => "INFO ",
 97              tracing::Level::DEBUG => "DEBUG",
 98              tracing::Level::TRACE => "TRACE",
 99          };
100          write!(writer, "{fmt_level}")?;
101  
102          write!(writer, "{}: ", meta.target())?;
103  
104          ctx.format_fields(writer.by_ref(), event)?;
105  
106          // reset the style
107          if writer.has_ansi_escapes() {
108              write!(writer, "\x1b[0m")?;
109          }
110          writeln!(writer)
111      }
112  }