1use crate::opentelemetry::add_opentelemetry_layer;
2use crate::reload::{
3 set_default_otlp_level, set_log_layer_handle, set_otlp_layer_handle, LogLayer, SimpleLogLayer,
4};
5use crate::{log_counter, OpenTelemetryLevel};
6use std::path::PathBuf;
7use tracing::subscriber::DefaultGuard;
8use tracing_appender::non_blocking::NonBlocking;
9use tracing_subscriber::layer::SubscriberExt;
10use tracing_subscriber::registry::LookupSpan;
11use tracing_subscriber::{fmt, reload, EnvFilter, Layer};
12use unc_crypto::PublicKey;
13use unc_primitives_core::types::AccountId;
14
15pub struct DefaultSubscriberGuard<S> {
20 subscriber: Option<S>,
29 local_subscriber_guard: Option<DefaultGuard>,
30 #[allow(dead_code)] writer_guard: Option<tracing_appender::non_blocking::WorkerGuard>,
32 #[allow(dead_code)] io_trace_guard: Option<tracing_appender::non_blocking::WorkerGuard>,
34}
35
36#[derive(Debug, Default, clap::Parser)]
38pub struct Options {
39 #[clap(long, value_enum, default_value = "off")]
41 opentelemetry: OpenTelemetryLevel,
42
43 #[clap(long, value_enum, default_value = "auto")]
45 color: ColorOutput,
46
47 #[clap(long)]
50 log_span_events: bool,
51
52 #[clap(long)]
54 record_io_trace: Option<PathBuf>,
55}
56
57impl<S: tracing::Subscriber + Send + Sync> DefaultSubscriberGuard<S> {
58 pub fn global(mut self) -> Self {
62 if let Some(subscriber) = self.subscriber.take() {
63 tracing::subscriber::set_global_default(subscriber)
64 .expect("could not set a global subscriber");
65 } else {
66 panic!("trying to set a default subscriber that has been already taken")
67 }
68 self
69 }
70
71 pub fn local(mut self) -> Self {
75 if let Some(subscriber) = self.subscriber.take() {
76 self.local_subscriber_guard = Some(tracing::subscriber::set_default(subscriber));
77 } else {
78 panic!("trying to set a default subscriber that has been already taken")
79 }
80 self
81 }
82}
83
84#[derive(clap::ValueEnum, Debug, Clone, Default)]
88pub enum ColorOutput {
89 #[default]
90 Always,
91 Never,
92 Auto,
93}
94
95fn is_terminal() -> bool {
96 use std::io::IsTerminal;
97 std::io::stderr().is_terminal()
98}
99
100fn add_simple_log_layer<S, W>(
101 filter: EnvFilter,
102 writer: W,
103 ansi: bool,
104 with_span_events: bool,
105 subscriber: S,
106) -> SimpleLogLayer<S, W>
107where
108 S: tracing::Subscriber + for<'span> LookupSpan<'span> + Send + Sync,
109 W: for<'writer> fmt::MakeWriter<'writer> + 'static,
110{
111 let layer = fmt::layer()
112 .with_ansi(ansi)
113 .with_span_events(get_fmt_span(with_span_events))
114 .with_writer(writer)
115 .with_filter(filter);
116
117 subscriber.with(layer)
118}
119
120fn get_fmt_span(with_span_events: bool) -> fmt::format::FmtSpan {
121 if with_span_events {
122 fmt::format::FmtSpan::ENTER | fmt::format::FmtSpan::CLOSE
123 } else {
124 fmt::format::FmtSpan::NONE
125 }
126}
127
128fn add_non_blocking_log_layer<S>(
129 filter: EnvFilter,
130 writer: NonBlocking,
131 ansi: bool,
132 with_span_events: bool,
133 subscriber: S,
134) -> (LogLayer<S>, reload::Handle<EnvFilter, S>)
135where
136 S: tracing::Subscriber + for<'span> LookupSpan<'span> + Send + Sync,
137{
138 let (filter, handle) = reload::Layer::<EnvFilter, S>::new(filter);
139
140 let layer = fmt::layer()
141 .with_ansi(ansi)
142 .with_span_events(get_fmt_span(with_span_events))
143 .with_writer(writer)
144 .with_filter(filter);
145
146 (subscriber.with(layer), handle)
147}
148
149#[cfg(feature = "io_trace")]
155pub fn make_io_tracing_layer<S>(
156 file: std::fs::File,
157) -> (
158 tracing_subscriber::filter::Filtered<crate::io_tracer::IoTraceLayer, EnvFilter, S>,
159 tracing_appender::non_blocking::WorkerGuard,
160)
161where
162 S: tracing::Subscriber + for<'span> LookupSpan<'span>,
163{
164 use std::io::BufWriter;
165 let (base_io_layer, guard) = crate::io_tracer::IoTraceLayer::new(BufWriter::new(file));
166 let io_layer = base_io_layer.with_filter(EnvFilter::new(
167 "store=trace,vm_logic=trace,host-function=trace,runtime=debug,crate::io_tracer=trace,io_tracer_count=trace",
168 ));
169 (io_layer, guard)
170}
171
172fn use_color_output(options: &Options) -> bool {
173 match options.color {
174 ColorOutput::Always => true,
175 ColorOutput::Never => false,
176 ColorOutput::Auto => use_color_auto(),
177 }
178}
179
180pub(crate) fn use_color_auto() -> bool {
181 std::env::var_os("NO_COLOR").is_none() && is_terminal()
182}
183
184pub fn default_subscriber(
195 env_filter: EnvFilter,
196 options: &Options,
197) -> DefaultSubscriberGuard<impl tracing::Subscriber + for<'span> LookupSpan<'span> + Send + Sync> {
198 let color_output = use_color_output(options);
199
200 let make_writer = || {
201 let stderr = std::io::stderr();
202 std::io::LineWriter::new(stderr)
203 };
204
205 let subscriber = tracing_subscriber::registry();
206 let subscriber = subscriber.with(log_counter::LogCounter::default());
207 let subscriber = add_simple_log_layer(
208 env_filter,
209 make_writer,
210 color_output,
211 options.log_span_events,
212 subscriber,
213 );
214
215 #[allow(unused_mut)]
216 let mut io_trace_guard = None;
217 #[cfg(feature = "io_trace")]
218 let subscriber = subscriber.with(options.record_io_trace.as_ref().map(|output_path| {
219 let (sub, guard) = make_io_tracing_layer(
220 std::fs::File::create(output_path)
221 .expect("unable to create or truncate IO trace output file"),
222 );
223 io_trace_guard = Some(guard);
224 sub
225 }));
226
227 DefaultSubscriberGuard {
228 subscriber: Some(subscriber),
229 local_subscriber_guard: None,
230 writer_guard: None,
231 io_trace_guard,
232 }
233}
234
235pub async fn default_subscriber_with_opentelemetry(
240 env_filter: EnvFilter,
241 options: &Options,
242 chain_id: String,
243 node_public_key: PublicKey,
244 account_id: Option<AccountId>,
245) -> DefaultSubscriberGuard<impl tracing::Subscriber + Send + Sync> {
246 let color_output = use_color_output(options);
247
248 let stderr = std::io::stderr();
250 let lined_stderr = std::io::LineWriter::new(stderr);
251 let (writer, writer_guard) = tracing_appender::non_blocking(lined_stderr);
252
253 let subscriber = tracing_subscriber::registry();
254 let subscriber = subscriber.with(log_counter::LogCounter::default());
256
257 set_default_otlp_level(options.opentelemetry);
258
259 let (subscriber, handle) = add_non_blocking_log_layer(
260 env_filter,
261 writer,
262 color_output,
263 options.log_span_events,
264 subscriber,
265 );
266 set_log_layer_handle(handle);
267
268 let (subscriber, handle) = add_opentelemetry_layer(
269 options.opentelemetry,
270 chain_id,
271 node_public_key,
272 account_id,
273 subscriber,
274 )
275 .await;
276 set_otlp_layer_handle(handle);
277
278 #[allow(unused_mut)]
279 let mut io_trace_guard = None;
280 #[cfg(feature = "io_trace")]
281 let subscriber = subscriber.with(options.record_io_trace.as_ref().map(|output_path| {
282 let (sub, guard) = make_io_tracing_layer(
283 std::fs::File::create(output_path)
284 .expect("unable to create or truncate IO trace output file"),
285 );
286 io_trace_guard = Some(guard);
287 sub
288 }));
289
290 DefaultSubscriberGuard {
291 subscriber: Some(subscriber),
292 local_subscriber_guard: None,
293 writer_guard: Some(writer_guard),
294 io_trace_guard,
295 }
296}