tc_tracing/logging/
directives.rs

1// Copyright 2021 Parity Technologies (UK) Ltd.
2// This file is part of Tetcore.
3
4// Tetcore is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8
9// Tetcore is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12// GNU General Public License for more details.
13
14// You should have received a copy of the GNU General Public License
15// along with Tetcore.  If not, see <http://www.gnu.org/licenses/>.
16
17use once_cell::sync::OnceCell;
18use parking_lot::Mutex;
19use tracing_subscriber::{
20	filter::Directive, fmt as tracing_fmt, fmt::time::ChronoLocal, layer, reload::Handle,
21	EnvFilter, Registry,
22};
23
24// Handle to reload the tracing log filter
25static FILTER_RELOAD_HANDLE: OnceCell<Handle<EnvFilter, SCSubscriber>> = OnceCell::new();
26// Directives that are defaulted to when resetting the log filter
27static DEFAULT_DIRECTIVES: OnceCell<Mutex<Vec<String>>> = OnceCell::new();
28// Current state of log filter
29static CURRENT_DIRECTIVES: OnceCell<Mutex<Vec<String>>> = OnceCell::new();
30
31/// Add log filter directive(s) to the defaults
32///
33/// The syntax is identical to the CLI `<target>=<level>`:
34///
35/// `sync=debug,state=trace`
36pub(crate) fn add_default_directives(directives: &str) {
37	DEFAULT_DIRECTIVES
38		.get_or_init(|| Mutex::new(Vec::new()))
39		.lock()
40		.push(directives.to_owned());
41	add_directives(directives);
42}
43
44/// Add directives to current directives
45pub fn add_directives(directives: &str) {
46	CURRENT_DIRECTIVES
47		.get_or_init(|| Mutex::new(Vec::new()))
48		.lock()
49		.push(directives.to_owned());
50}
51
52/// Parse `Directive` and add to default directives if successful.
53///
54/// Ensures the supplied directive will be restored when resetting the log filter.
55pub(crate) fn parse_default_directive(directive: &str) -> super::Result<Directive> {
56	let dir = directive.parse()?;
57	add_default_directives(directive);
58	Ok(dir)
59}
60
61/// Reload the logging filter with the supplied directives added to the existing directives
62pub fn reload_filter() -> Result<(), String> {
63	let mut env_filter = EnvFilter::default();
64	if let Some(current_directives) = CURRENT_DIRECTIVES.get() {
65		// Use join and then split in case any directives added together
66		for directive in current_directives
67			.lock()
68			.join(",")
69			.split(',')
70			.map(|d| d.parse())
71		{
72			match directive {
73				Ok(dir) => env_filter = env_filter.add_directive(dir),
74				Err(invalid_directive) => {
75					log::warn!(
76						target: "tracing",
77						"Unable to parse directive while setting log filter: {:?}",
78						invalid_directive,
79					);
80				}
81			}
82		}
83	}
84	env_filter = env_filter.add_directive(
85		"tc_tracing=trace"
86			.parse()
87			.expect("provided directive is valid"),
88	);
89	log::debug!(target: "tracing", "Reloading log filter with: {}", env_filter);
90	FILTER_RELOAD_HANDLE
91		.get()
92		.ok_or("No reload handle present".to_string())?
93		.reload(env_filter)
94		.map_err(|e| format!("{}", e))
95}
96
97/// Resets the log filter back to the original state when the node was started.
98///
99/// Includes tetcore defaults and CLI supplied directives.
100pub fn reset_log_filter() -> Result<(), String> {
101	let directive = DEFAULT_DIRECTIVES
102		.get_or_init(|| Mutex::new(Vec::new()))
103		.lock()
104		.clone();
105
106	*CURRENT_DIRECTIVES
107		.get_or_init(|| Mutex::new(Vec::new()))
108		.lock() = directive;
109	reload_filter()
110}
111
112/// Initialize FILTER_RELOAD_HANDLE, only possible once
113pub(crate) fn set_reload_handle(handle: Handle<EnvFilter, SCSubscriber>) {
114	let _ = FILTER_RELOAD_HANDLE.set(handle);
115}
116
117// The layered Subscriber as built up in `LoggerBuilder::init()`.
118// Used in the reload `Handle`.
119type SCSubscriber<
120	N = tracing_fmt::format::DefaultFields,
121	E = crate::logging::EventFormat<ChronoLocal>,
122	W = fn() -> std::io::Stderr,
123> = layer::Layered<tracing_fmt::Layer<Registry, N, E, W>, Registry>;