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 }