sc_tracing_proc_macro/
lib.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 proc_macro::TokenStream;
20use proc_macro2::Span;
21use proc_macro_crate::{crate_name, FoundCrate};
22use quote::quote;
23use syn::{Error, Expr, Ident, ItemFn};
24
25/// Add a log prefix to the function.
26///
27/// This prefixes all the log lines with `[<name>]` (after the timestamp). It works by making a
28/// tracing's span that is propagated to all the child calls and child tasks (futures) if they are
29/// spawned properly with the `SpawnHandle` (see `TaskManager` in sc-cli) or if the futures use
30/// `.in_current_span()` (see tracing-futures).
31///
32/// See Tokio's [tracing documentation](https://docs.rs/tracing-core/) and
33/// [tracing-futures documentation](https://docs.rs/tracing-futures/) for more details.
34///
35/// # Implementation notes
36///
37/// If there are multiple spans with a log prefix, only the latest will be shown.
38///
39/// # Example with a literal
40///
41/// ```ignore
42/// Builds a new service for a light client.
43/// #[sc_cli::prefix_logs_with("light")]
44/// pub fn new_light(config: Configuration) -> Result<TaskManager, ServiceError> {
45///     let (client, backend, keystore, mut task_manager, on_demand) =
46///         sc_service::new_light_parts::<Block, RuntimeApi, Executor>(&config)?;
47///
48///        ...
49/// }
50/// ```
51///
52/// Will produce logs that look like this:
53///
54/// ```text
55/// 2020-10-16 08:03:14  Substrate Node
56/// 2020-10-16 08:03:14  ✌️  version 2.0.0-47f7d3f2e-x86_64-linux-gnu
57/// 2020-10-16 08:03:14  ❤️  by Anonymous, 2017-2020
58/// 2020-10-16 08:03:14  📋 Chain specification: Local Testnet
59/// 2020-10-16 08:03:14  🏷  Node name: nice-glove-1401
60/// 2020-10-16 08:03:14  👤 Role: LIGHT
61/// 2020-10-16 08:03:14  💾 Database: RocksDb at /tmp/substrate95w2Dk/chains/local_testnet/db
62/// 2020-10-16 08:03:14  ⛓  Native runtime: node-template-1 (node-template-1.tx1.au1)
63/// 2020-10-16 08:03:14  [light] 🔨 Initializing Genesis block/state (state: 0x121d…8e36, header-hash: 0x24ef…8ff6)
64/// 2020-10-16 08:03:14  [light] Loading GRANDPA authorities from genesis on what appears to be first startup.
65/// 2020-10-16 08:03:15  [light] ⏱  Loaded block-time = 6000 milliseconds from genesis on first-launch
66/// 2020-10-16 08:03:15  [light] Using default protocol ID "sup" because none is configured in the chain specs
67/// 2020-10-16 08:03:15  [light] 🏷  Local node identity is: 12D3KooWHX4rkWT6a6N55Km7ZnvenGdShSKPkzJ3yj9DU5nqDtWR
68/// 2020-10-16 08:03:15  [light] 📦 Highest known block at #0
69/// 2020-10-16 08:03:15  [light] 〽️ Prometheus server started at 127.0.0.1:9615
70/// 2020-10-16 08:03:15  [light] Listening for new connections on 127.0.0.1:9944.
71/// ```
72///
73/// # Example using the actual node name
74///
75/// ```ignore
76/// Builds a new service for a light client.
77/// #[sc_cli::prefix_logs_with(config.network.node_name.as_str())]
78/// pub fn new_light(config: Configuration) -> Result<TaskManager, ServiceError> {
79///     let (client, backend, keystore, mut task_manager, on_demand) =
80///         sc_service::new_light_parts::<Block, RuntimeApi, Executor>(&config)?;
81///
82///        ...
83/// }
84/// ```
85///
86/// Will produce logs that look like this:
87///
88/// ```text
89/// 2020-10-16 08:12:57  Substrate Node
90/// 2020-10-16 08:12:57  ✌️  version 2.0.0-efb9b822a-x86_64-linux-gnu
91/// 2020-10-16 08:12:57  ❤️  by Anonymous, 2017-2020
92/// 2020-10-16 08:12:57  📋 Chain specification: Local Testnet
93/// 2020-10-16 08:12:57  🏷  Node name: open-harbor-1619
94/// 2020-10-16 08:12:57  👤 Role: LIGHT
95/// 2020-10-16 08:12:57  💾 Database: RocksDb at /tmp/substrate9T9Mtb/chains/local_testnet/db
96/// 2020-10-16 08:12:57  ⛓  Native runtime: node-template-1 (node-template-1.tx1.au1)
97/// 2020-10-16 08:12:58  [open-harbor-1619] 🔨 Initializing Genesis block/state (state: 0x121d…8e36, header-hash: 0x24ef…8ff6)
98/// 2020-10-16 08:12:58  [open-harbor-1619] Loading GRANDPA authorities from genesis on what appears to be first startup.
99/// 2020-10-16 08:12:58  [open-harbor-1619] ⏱  Loaded block-time = 6000 milliseconds from genesis on first-launch
100/// 2020-10-16 08:12:58  [open-harbor-1619] Using default protocol ID "sup" because none is configured in the chain specs
101/// 2020-10-16 08:12:58  [open-harbor-1619] 🏷  Local node identity is: 12D3KooWRzmYC8QTK1Pm8Cfvid3skTS4Hn54jc4AUtje8Rqbfgtp
102/// 2020-10-16 08:12:58  [open-harbor-1619] 📦 Highest known block at #0
103/// 2020-10-16 08:12:58  [open-harbor-1619] 〽️ Prometheus server started at 127.0.0.1:9615
104/// 2020-10-16 08:12:58  [open-harbor-1619] Listening for new connections on 127.0.0.1:9944.
105/// ```
106#[proc_macro_attribute]
107pub fn prefix_logs_with(arg: TokenStream, item: TokenStream) -> TokenStream {
108	let item_fn = syn::parse_macro_input!(item as ItemFn);
109
110	if arg.is_empty() {
111		return Error::new(
112			Span::call_site(),
113			"missing argument: name of the node. Example: sc_cli::prefix_logs_with(<expr>)",
114		)
115		.to_compile_error()
116		.into()
117	}
118
119	let name = syn::parse_macro_input!(arg as Expr);
120
121	let crate_name = match crate_name("sc-tracing") {
122		Ok(FoundCrate::Itself) => Ident::new("sc_tracing", Span::call_site()),
123		Ok(FoundCrate::Name(crate_name)) => Ident::new(&crate_name, Span::call_site()),
124		Err(e) => return Error::new(Span::call_site(), e).to_compile_error().into(),
125	};
126
127	let ItemFn { attrs, vis, sig, block } = item_fn;
128
129	(quote! {
130		#(#attrs)*
131		#vis #sig {
132			let span = #crate_name::tracing::info_span!(
133				#crate_name::logging::PREFIX_LOG_SPAN,
134				name = #name,
135			);
136			let _enter = span.enter();
137
138			#block
139		}
140	})
141	.into()
142}