snarkos_cli/helpers/
dynamic_format.rs

1// Copyright (c) 2019-2025 Provable Inc.
2// This file is part of the snarkOS 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
16use std::sync::{
17    Arc,
18    atomic::{AtomicBool, Ordering},
19};
20
21use time::{
22    OffsetDateTime,
23    format_description::{self, OwnedFormatItem},
24};
25use tracing::{Event, Subscriber};
26use 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.
32pub 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
39impl<S, N> FormatEvent<S, N> for DynamicFormatter
40where
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
53impl 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
61struct 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.
68impl 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
77impl<S, N> FormatEvent<S, N> for DimFormat
78where
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}