referrerpolicy=no-referrer-when-downgrade

sc_tracing/logging/
event_format.rs

1// This file is part of Substrate.
2
3// Copyright (C) Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
5
6// This program is free software: you can redistribute it and/or modify
7// it under the terms of the GNU General Public License as published by
8// the Free Software Foundation, either version 3 of the License, or
9// (at your option) any later version.
10
11// This program is distributed in the hope that it will be useful,
12// but WITHOUT ANY WARRANTY; without even the implied warranty of
13// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14// GNU General Public License for more details.
15
16// You should have received a copy of the GNU General Public License
17// along with this program. If not, see <https://www.gnu.org/licenses/>.
18
19use crate::logging::fast_local_time::FastLocalTime;
20use console::style;
21use std::fmt;
22use tracing::{Event, Level, Subscriber};
23use tracing_log::NormalizeEvent;
24use tracing_subscriber::{
25	fmt::{format, time::FormatTime, FmtContext, FormatEvent, FormatFields},
26	registry::LookupSpan,
27};
28
29/// A pre-configured event formatter.
30pub struct EventFormat<T = FastLocalTime> {
31	/// Use the given timer for log message timestamps.
32	pub timer: T,
33	/// Sets whether or not an event's target is displayed.
34	pub display_target: bool,
35	/// Sets whether or not an event's level is displayed.
36	pub display_level: bool,
37	/// Sets whether or not the name of the current thread is displayed when formatting events.
38	pub display_thread_name: bool,
39	/// Duplicate INFO, WARN and ERROR messages to stdout.
40	pub dup_to_stdout: bool,
41}
42
43impl<T> EventFormat<T>
44where
45	T: FormatTime,
46{
47	// NOTE: the following code took inspiration from tracing-subscriber
48	//
49	//       https://github.com/tokio-rs/tracing/blob/2f59b32/tracing-subscriber/src/fmt/format/mod.rs#L449
50	pub(crate) fn format_event_custom<'b, 'w, S, N>(
51		&self,
52		ctx: &FmtContext<'b, S, N>,
53		mut writer: format::Writer<'w>,
54		event: &Event,
55	) -> fmt::Result
56	where
57		S: Subscriber + for<'a> LookupSpan<'a>,
58		N: for<'a> FormatFields<'a> + 'static,
59	{
60		let normalized_meta = event.normalized_metadata();
61		let meta = normalized_meta.as_ref().unwrap_or_else(|| event.metadata());
62		time::write(&self.timer, &mut format::Writer::new(&mut writer))?;
63
64		if self.display_level {
65			let fmt_level = FmtLevel::new(meta.level());
66			write!(writer, "{} ", fmt_level)?;
67		}
68
69		if self.display_thread_name {
70			let current_thread = std::thread::current();
71			match current_thread.name() {
72				Some(name) => {
73					write!(&mut writer, "{} ", FmtThreadName::new(name))?;
74				},
75				// fall-back to thread id when name is absent and ids are not enabled
76				None => {
77					write!(&mut writer, "{:0>2?} ", current_thread.id())?;
78				},
79			}
80		}
81
82		if self.display_target {
83			write!(&mut writer, "{}: ", meta.target())?;
84		}
85
86		// Custom code to display node name
87		if let Some(span) = ctx.lookup_current() {
88			for span in span.scope() {
89				let exts = span.extensions();
90				if let Some(prefix) = exts.get::<super::layers::Prefix>() {
91					write!(&mut writer, "{}", prefix.as_str())?;
92					break
93				}
94			}
95		}
96
97		ctx.format_fields(format::Writer::new(&mut writer), event)?;
98		writeln!(&mut writer)?;
99
100		Ok(())
101	}
102}
103
104// NOTE: the following code took inspiration from tracing-subscriber
105//
106//       https://github.com/tokio-rs/tracing/blob/2f59b32/tracing-subscriber/src/fmt/format/mod.rs#L449
107impl<S, N, T> FormatEvent<S, N> for EventFormat<T>
108where
109	S: Subscriber + for<'a> LookupSpan<'a>,
110	N: for<'a> FormatFields<'a> + 'static,
111	T: FormatTime,
112{
113	fn format_event(
114		&self,
115		ctx: &FmtContext<S, N>,
116		mut writer: format::Writer<'_>,
117		event: &Event,
118	) -> fmt::Result {
119		if self.dup_to_stdout &&
120			(event.metadata().level() == &Level::INFO ||
121				event.metadata().level() == &Level::WARN ||
122				event.metadata().level() == &Level::ERROR)
123		{
124			let mut out = String::new();
125			let buf_writer = format::Writer::new(&mut out);
126			self.format_event_custom(ctx, buf_writer, event)?;
127			writer.write_str(&out)?;
128			print!("{}", out);
129			Ok(())
130		} else {
131			self.format_event_custom(ctx, writer, event)
132		}
133	}
134}
135
136struct FmtLevel<'a> {
137	level: &'a Level,
138}
139
140impl<'a> FmtLevel<'a> {
141	pub(crate) fn new(level: &'a Level) -> Self {
142		Self { level }
143	}
144}
145
146const TRACE_STR: &str = "TRACE";
147const DEBUG_STR: &str = "DEBUG";
148const INFO_STR: &str = " INFO";
149const WARN_STR: &str = " WARN";
150const ERROR_STR: &str = "ERROR";
151
152impl<'a> fmt::Display for FmtLevel<'a> {
153	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
154		match *self.level {
155			Level::TRACE => write!(f, "{}", style(TRACE_STR).magenta()),
156			Level::DEBUG => write!(f, "{}", style(DEBUG_STR).blue()),
157			Level::INFO => write!(f, "{}", style(INFO_STR).green()),
158			Level::WARN => write!(f, "{}", style(WARN_STR).yellow()),
159			Level::ERROR => write!(f, "{}", style(ERROR_STR).red()),
160		}
161	}
162}
163
164struct FmtThreadName<'a> {
165	name: &'a str,
166}
167
168impl<'a> FmtThreadName<'a> {
169	pub(crate) fn new(name: &'a str) -> Self {
170		Self { name }
171	}
172}
173
174// NOTE: the following code has been duplicated from tracing-subscriber
175//
176//       https://github.com/tokio-rs/tracing/blob/2f59b32/tracing-subscriber/src/fmt/format/mod.rs#L845
177impl<'a> fmt::Display for FmtThreadName<'a> {
178	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
179		use std::sync::atomic::{
180			AtomicUsize,
181			Ordering::{AcqRel, Acquire, Relaxed},
182		};
183
184		// Track the longest thread name length we've seen so far in an atomic,
185		// so that it can be updated by any thread.
186		static MAX_LEN: AtomicUsize = AtomicUsize::new(0);
187		let len = self.name.len();
188		// Snapshot the current max thread name length.
189		let mut max_len = MAX_LEN.load(Relaxed);
190
191		while len > max_len {
192			// Try to set a new max length, if it is still the value we took a
193			// snapshot of.
194			match MAX_LEN.compare_exchange(max_len, len, AcqRel, Acquire) {
195				// We successfully set the new max value
196				Ok(_) => break,
197				// Another thread set a new max value since we last observed
198				// it! It's possible that the new length is actually longer than
199				// ours, so we'll loop again and check whether our length is
200				// still the longest. If not, we'll just use the newer value.
201				Err(actual) => max_len = actual,
202			}
203		}
204
205		// pad thread name using `max_len`
206		write!(f, "{:>width$}", self.name, width = max_len)
207	}
208}
209
210// NOTE: the following code has been duplicated from tracing-subscriber
211//
212//       https://github.com/tokio-rs/tracing/blob/2f59b32/tracing-subscriber/src/fmt/time/mod.rs#L252
213mod time {
214	use std::fmt;
215	use tracing_subscriber::fmt::{format, time::FormatTime};
216
217	pub(crate) fn write<T>(timer: T, writer: &mut format::Writer<'_>) -> fmt::Result
218	where
219		T: FormatTime,
220	{
221		if console::colors_enabled() {
222			write!(writer, "\x1B[2m")?;
223			timer.format_time(writer)?;
224			write!(writer, "\x1B[0m")?;
225		} else {
226			timer.format_time(writer)?;
227		}
228
229		writer.write_char(' ')?;
230		Ok(())
231	}
232}