1
//! Configure tracing subscribers for Arti
2

            
3
use anyhow::{Context, Result, anyhow};
4
use derive_deftly::Deftly;
5
use fs_mistrust::Mistrust;
6
use serde::{Deserialize, Serialize};
7
use std::io::IsTerminal as _;
8
use std::path::Path;
9
use std::str::FromStr;
10
use std::time::Duration;
11
use tor_basic_utils::PathExt as _;
12
use tor_config::ConfigBuildError;
13
use tor_config::derive::prelude::*;
14
use tor_config_path::{CfgPath, CfgPathResolver};
15
use tor_error::warn_report;
16
use tracing::{Subscriber, error};
17
use tracing_appender::non_blocking::WorkerGuard;
18
use tracing_subscriber::layer::SubscriberExt;
19
use tracing_subscriber::prelude::*;
20
use tracing_subscriber::{Layer, filter::Targets, fmt, registry};
21

            
22
mod fields;
23
#[cfg(feature = "opentelemetry")]
24
mod otlp_file_exporter;
25
mod time;
26

            
27
/// Structure to hold our logging configuration options
28
#[derive(Debug, Clone, Deftly, Eq, PartialEq)]
29
#[derive_deftly(TorConfig)]
30
#[cfg_attr(feature = "experimental-api", visibility::make(pub))]
31
#[cfg_attr(feature = "experimental-api", deftly(tor_config(vis = "pub")))]
32
pub(crate) struct LoggingConfig {
33
    /// Filtering directives that determine tracing levels as described at
34
    /// <https://docs.rs/tracing-subscriber/latest/tracing_subscriber/filter/targets/struct.Targets.html#impl-FromStr>
35
    ///
36
    /// You can override this setting with the -l, --log-level command line parameter.
37
    ///
38
    /// Example: "info,tor_proto::channel=trace"
39
    #[deftly(tor_config(default = "default_console_filter()"))]
40
    console: Option<String>,
41

            
42
    /// Filtering directives for the journald logger.
43
    ///
44
    /// Only takes effect if Arti is built with the `journald` filter.
45
    #[deftly(tor_config(
46
        build = r#"|this: &Self| tor_config::resolve_option(&this.journald, || None)"#
47
    ))]
48
    journald: Option<String>,
49

            
50
    /// Configuration for logging spans with OpenTelemetry.
51
    #[deftly(tor_config(
52
        sub_builder,
53
        cfg = r#" feature = "opentelemetry" "#,
54
        cfg_desc = "with opentelemetry support"
55
    ))]
56
    opentelemetry: OpentelemetryConfig,
57

            
58
    /// Configuration for passing information to tokio-console.
59
    #[deftly(tor_config(
60
        sub_builder,
61
        cfg = r#" feature = "tokio-console" "#,
62
        cfg_desc = "with tokio-console support"
63
    ))]
64
    tokio_console: TokioConsoleConfig,
65

            
66
    /// Configuration for one or more logfiles.
67
    ///
68
    /// The default is not to log to any files.
69
    #[deftly(tor_config(list(element(build), listtype = "LogfileList"), default = "vec![]"))]
70
    files: Vec<LogfileConfig>,
71

            
72
    /// If set to true, we disable safe logging on _all logs_, and store
73
    /// potentially sensitive information at level `info` or higher.
74
    ///
75
    /// This can be useful for debugging, but it increases the value of your
76
    /// logs to an attacker.  Do not turn this on in production unless you have
77
    /// a good log rotation mechanism.
78
    //
79
    // TODO: Eventually we might want to make this more complex, and add a
80
    // per-log mechanism to turn off unsafe logging. Alternatively, we might do
81
    // that by extending the filter syntax implemented by `tracing` to have an
82
    // "unsafe" flag on particular lines.
83
    #[deftly(tor_config(default))]
84
    log_sensitive_information: bool,
85

            
86
    /// An approximate granularity with which log times should be displayed.
87
    ///
88
    /// This value controls every log time that arti outputs; it doesn't have any
89
    /// effect on times written by other logging programs like `journald`.
90
    ///
91
    /// We may round this value up for convenience: For example, if you say
92
    /// "2.5s", we may treat it as if you had said "3s."
93
    ///
94
    /// The default is "1s", or one second.
95
    #[deftly(tor_config(default = "std::time::Duration::new(1,0)"))]
96
    time_granularity: std::time::Duration,
97
}
98

            
99
/// Return a default tracing filter value for `logging.console`.
100
#[allow(clippy::unnecessary_wraps)]
101
70
fn default_console_filter() -> Option<String> {
102
70
    Some("info".to_owned())
103
70
}
104

            
105
/// Configuration information for an (optionally rotating) logfile.
106
#[derive(Debug, Deftly, Clone, Eq, PartialEq)]
107
#[derive_deftly(TorConfig)]
108
#[deftly(tor_config(no_default_trait))]
109
#[cfg_attr(feature = "experimental-api", visibility::make(pub))]
110
#[cfg_attr(feature = "experimental-api", deftly(tor_config(vis = "pub")))]
111
pub(crate) struct LogfileConfig {
112
    /// How often to rotate the file?
113
    #[deftly(tor_config(default))]
114
    rotate: LogRotation,
115
    /// Where to write the files?
116
    #[deftly(tor_config(no_default))]
117
    path: CfgPath,
118
    /// Filter to apply before writing
119
    #[deftly(tor_config(no_default))]
120
    filter: String,
121
}
122

            
123
/// How often to rotate a log file
124
#[derive(Debug, Default, Clone, Serialize, Deserialize, Copy, Eq, PartialEq)]
125
#[non_exhaustive]
126
#[serde(rename_all = "lowercase")]
127
#[cfg_attr(feature = "experimental-api", visibility::make(pub))]
128
pub(crate) enum LogRotation {
129
    /// Rotate logs daily
130
    Daily,
131
    /// Rotate logs hourly
132
    Hourly,
133
    /// Never rotate the log
134
    #[default]
135
    Never,
136
}
137

            
138
/// Configuration for exporting spans with OpenTelemetry.
139
#[derive(Debug, Deftly, Clone, Eq, PartialEq, Serialize, Deserialize)]
140
#[derive_deftly(TorConfig)]
141
#[cfg_attr(feature = "experimental-api", visibility::make(pub))]
142
#[cfg_attr(feature = "experimental-api", deftly(tor_config(vis = "pub")))]
143
pub(crate) struct OpentelemetryConfig {
144
    /// Write spans to a file in OTLP JSON format.
145
    #[deftly(tor_config(default))]
146
    file: Option<OpentelemetryFileExporterConfig>,
147
    /// Export spans via HTTP.
148
    #[deftly(tor_config(default))]
149
    http: Option<OpentelemetryHttpExporterConfig>,
150
}
151

            
152
/// Configuration for the OpenTelemetry HTTP exporter.
153
#[derive(Debug, Deftly, Clone, Eq, PartialEq, Serialize, Deserialize)]
154
#[derive_deftly(TorConfig)]
155
#[deftly(tor_config(no_default_trait))]
156
#[cfg_attr(feature = "experimental-api", visibility::make(pub))]
157
#[cfg_attr(feature = "experimental-api", deftly(tor_config(vis = "pub")))]
158
pub(crate) struct OpentelemetryHttpExporterConfig {
159
    /// HTTP(S) endpoint to send spans to.
160
    ///
161
    /// For Jaeger, this should be something like: `http://localhost:4318/v1/traces`
162
    #[deftly(tor_config(no_default))]
163
    endpoint: String,
164
    /// Configuration for how to batch exports.
165
    #[deftly(tor_config(sub_builder))]
166
    batch: OpentelemetryBatchConfig,
167
    /// Timeout for sending data.
168
    ///
169
    /// If this is set to [`None`], it will be left at the OpenTelemetry default, which is
170
    /// currently 10 seconds unless overrided with a environment variable.
171
    //
172
    // NOTE: there is no way to actually override this with None, so we have to say
173
    // "no magic" to tell dd(TorConfig) not to worry about that.
174
    #[deftly(tor_config(no_magic, default))]
175
    timeout: Option<Duration>,
176
    // TODO: Once opentelemetry-otlp supports more than one protocol over HTTP, add a config option
177
    // to choose protocol here.
178
}
179

            
180
/// Configuration for the OpenTelemetry HTTP exporter.
181
#[derive(Debug, Deftly, Clone, Eq, PartialEq, Serialize, Deserialize)]
182
#[derive_deftly(TorConfig)]
183
#[deftly(tor_config(no_default_trait))]
184
#[cfg_attr(feature = "experimental-api", visibility::make(pub))]
185
#[cfg_attr(feature = "experimental-api", deftly(tor_config(vis = "pub")))]
186
pub(crate) struct OpentelemetryFileExporterConfig {
187
    /// The path to write the JSON file to.
188
    #[deftly(tor_config(no_default))]
189
    path: CfgPath,
190
    /// Configuration for how to batch writes.
191
    #[deftly(tor_config(sub_builder))]
192
    batch: OpentelemetryBatchConfig,
193
}
194

            
195
/// Configuration for the Opentelemetry batch exporting.
196
///
197
/// This is a copy of [`opentelemetry_sdk::trace::BatchConfig`].
198
#[derive(Debug, Deftly, Copy, Clone, Eq, PartialEq, Serialize, Deserialize)]
199
#[derive_deftly(TorConfig)]
200
#[cfg_attr(feature = "experimental-api", visibility::make(pub))]
201
#[cfg_attr(feature = "experimental-api", deftly(tor_config(vis = "pub")))]
202
pub(crate) struct OpentelemetryBatchConfig {
203
    /// Maximum queue size. See [`opentelemetry_sdk::trace::BatchConfig::max_queue_size`].
204
    #[deftly(tor_config(default))]
205
    max_queue_size: Option<usize>,
206
    /// Maximum export batch size. See [`opentelemetry_sdk::trace::BatchConfig::max_export_batch_size`].
207
    #[deftly(tor_config(default))]
208
    max_export_batch_size: Option<usize>,
209
    /// Scheduled delay. See [`opentelemetry_sdk::trace::BatchConfig::scheduled_delay`].
210
    #[deftly(tor_config(no_magic, default))]
211
    scheduled_delay: Option<Duration>,
212
}
213

            
214
#[cfg(feature = "opentelemetry")]
215
impl From<OpentelemetryBatchConfig> for opentelemetry_sdk::trace::BatchConfig {
216
    fn from(config: OpentelemetryBatchConfig) -> opentelemetry_sdk::trace::BatchConfig {
217
        let batch_config = opentelemetry_sdk::trace::BatchConfigBuilder::default();
218

            
219
        let batch_config = if let Some(max_queue_size) = config.max_queue_size {
220
            batch_config.with_max_queue_size(max_queue_size)
221
        } else {
222
            batch_config
223
        };
224

            
225
        let batch_config = if let Some(max_export_batch_size) = config.max_export_batch_size {
226
            batch_config.with_max_export_batch_size(max_export_batch_size)
227
        } else {
228
            batch_config
229
        };
230

            
231
        let batch_config = if let Some(scheduled_delay) = config.scheduled_delay {
232
            batch_config.with_scheduled_delay(scheduled_delay)
233
        } else {
234
            batch_config
235
        };
236

            
237
        batch_config.build()
238
    }
239
}
240

            
241
/// Configuration for logging to the tokio console.
242
#[derive(Debug, Deftly, Copy, Clone, Eq, PartialEq, Serialize, Deserialize)]
243
#[derive_deftly(TorConfig)]
244
#[cfg(feature = "tokio-console")]
245
#[cfg_attr(feature = "experimental-api", visibility::make(pub))]
246
#[cfg_attr(feature = "experimental-api", deftly(tor_config(vis = "pub")))]
247
pub(crate) struct TokioConsoleConfig {
248
    /// If true, the tokio console subscriber should be enabled.
249
    ///
250
    /// This requires that tokio (and hence arti) is built with `--cfg tokio_unstable`
251
    /// in RUSTFLAGS.
252
    #[deftly(tor_config(default))]
253
    enabled: bool,
254
}
255

            
256
/// Placeholder for unused tokio console config.
257
#[cfg(not(feature = "tokio-console"))]
258
type TokioConsoleConfig = ();
259

            
260
/// As [`Targets::from_str`], but wrapped in an [`anyhow::Result`].
261
//
262
// (Note that we have to use `Targets`, not `EnvFilter`: see comment in
263
// `setup_logging()`.)
264
306
fn filt_from_str_verbose(s: &str, source: &str) -> Result<Targets> {
265
306
    Targets::from_str(s).with_context(|| format!("in {}", source))
266
306
}
267

            
268
/// As filt_from_str_verbose, but treat an absent filter (or an empty string) as
269
/// None.
270
612
fn filt_from_opt_str(s: &Option<String>, source: &str) -> Result<Option<Targets>> {
271
306
    Ok(match s {
272
306
        Some(s) if !s.is_empty() => Some(filt_from_str_verbose(s, source)?),
273
306
        _ => None,
274
    })
275
612
}
276

            
277
/// Try to construct a tracing [`Layer`] for logging to stderr.
278
306
fn console_layer<S>(config: &LoggingConfig, cli: Option<&str>) -> Result<impl Layer<S> + use<S>>
279
306
where
280
306
    S: Subscriber + for<'span> tracing_subscriber::registry::LookupSpan<'span>,
281
{
282
306
    let timer = time::new_formatter(config.time_granularity);
283
306
    let filter = cli
284
306
        .map(|s| filt_from_str_verbose(s, "--log-level command line parameter"))
285
306
        .or_else(|| filt_from_opt_str(&config.console, "logging.console").transpose())
286
306
        .unwrap_or_else(|| Ok(Targets::from_str("debug").expect("bad default")))?;
287
306
    let use_color = std::io::stderr().is_terminal();
288
    // We used to suppress safe-logging on the console, but we removed that
289
    // feature: we cannot be certain that the console really is volatile. Even
290
    // if isatty() returns true on the console, we can't be sure that the
291
    // terminal isn't saving backlog to disk or something like that.
292
306
    Ok(fmt::Layer::default()
293
306
        // we apply custom field formatting so that error fields are listed last
294
306
        .fmt_fields(fields::ErrorsLastFieldFormatter)
295
306
        .with_ansi(use_color)
296
306
        .with_timer(timer)
297
306
        .with_writer(std::io::stderr) // we make this explicit, to match with use_color.
298
306
        .with_filter(filter))
299
306
}
300

            
301
/// Try to construct a tracing [`Layer`] for logging to journald, if one is
302
/// configured.
303
#[cfg(feature = "journald")]
304
306
fn journald_layer<S>(config: &LoggingConfig) -> Result<impl Layer<S>>
305
306
where
306
306
    S: Subscriber + for<'span> tracing_subscriber::registry::LookupSpan<'span>,
307
{
308
306
    if let Some(filter) = filt_from_opt_str(&config.journald, "logging.journald")? {
309
        Ok(Some(tracing_journald::layer()?.with_filter(filter)))
310
    } else {
311
        // Fortunately, Option<Layer> implements Layer, so we can just return None here.
312
306
        Ok(None)
313
    }
314
306
}
315

            
316
/// Try to construct a tracing [`Layer`] for exporting spans via OpenTelemetry.
317
///
318
/// This doesn't allow for filtering, since most of our spans are exported at the trace level
319
/// anyways, and filtering can easily be done when viewing the data.
320
#[cfg(feature = "opentelemetry")]
321
306
fn otel_layer<S>(config: &LoggingConfig, path_resolver: &CfgPathResolver) -> Result<impl Layer<S>>
322
306
where
323
306
    S: Subscriber + for<'span> tracing_subscriber::registry::LookupSpan<'span>,
324
{
325
    use opentelemetry::trace::TracerProvider;
326
    use opentelemetry_otlp::WithExportConfig;
327

            
328
306
    if config.opentelemetry.file.is_some() && config.opentelemetry.http.is_some() {
329
        return Err(ConfigBuildError::Invalid {
330
            field: "logging.opentelemetry".into(),
331
            problem: "Only one OpenTelemetry exporter can be enabled at once.".into(),
332
        }
333
        .into());
334
306
    }
335

            
336
306
    let resource = opentelemetry_sdk::Resource::builder()
337
306
        .with_service_name("arti")
338
306
        .build();
339

            
340
306
    let span_processor = if let Some(otel_file_config) = &config.opentelemetry.file {
341
        let file = std::fs::File::options()
342
            .create(true)
343
            .append(true)
344
            .open(otel_file_config.path.path(path_resolver)?)?;
345

            
346
        let exporter = otlp_file_exporter::FileExporter::new(file, resource.clone());
347

            
348
        opentelemetry_sdk::trace::BatchSpanProcessor::builder(exporter)
349
            .with_batch_config(otel_file_config.batch.into())
350
            .build()
351
306
    } else if let Some(otel_http_config) = &config.opentelemetry.http {
352
        if otel_http_config.endpoint.starts_with("http://")
353
            && !(otel_http_config.endpoint.starts_with("http://localhost")
354
                || otel_http_config.endpoint.starts_with("http://127.0.0.1"))
355
        {
356
            return Err(ConfigBuildError::Invalid {
357
                field: "logging.opentelemetry.http.endpoint".into(),
358
                problem: "OpenTelemetry endpoint is set to HTTP on a non-localhost address! For security reasons, this is not supported.".into(),
359
            }
360
            .into());
361
        }
362
        let exporter = opentelemetry_otlp::SpanExporter::builder()
363
            .with_http()
364
            .with_endpoint(otel_http_config.endpoint.clone());
365

            
366
        let exporter = if let Some(timeout) = otel_http_config.timeout {
367
            exporter.with_timeout(timeout)
368
        } else {
369
            exporter
370
        };
371

            
372
        let exporter = exporter.build()?;
373

            
374
        opentelemetry_sdk::trace::BatchSpanProcessor::builder(exporter)
375
            .with_batch_config(otel_http_config.batch.into())
376
            .build()
377
    } else {
378
306
        return Ok(None);
379
    };
380

            
381
    let tracer_provider = opentelemetry_sdk::trace::SdkTracerProvider::builder()
382
        .with_resource(resource.clone())
383
        .with_span_processor(span_processor)
384
        .build();
385

            
386
    let tracer = tracer_provider.tracer("otel_file_tracer");
387

            
388
    Ok(Some(tracing_opentelemetry::layer().with_tracer(tracer)))
389
306
}
390

            
391
/// Try to construct a non-blocking tracing [`Layer`] for writing data to an
392
/// optionally rotating logfile.
393
///
394
/// On success, return that layer, along with a WorkerGuard that needs to be
395
/// dropped when the program exits, to flush buffered messages.
396
fn logfile_layer<S>(
397
    config: &LogfileConfig,
398
    granularity: std::time::Duration,
399
    mistrust: &Mistrust,
400
    path_resolver: &CfgPathResolver,
401
) -> Result<(impl Layer<S> + Send + Sync + Sized + use<S>, WorkerGuard)>
402
where
403
    S: Subscriber + for<'span> tracing_subscriber::registry::LookupSpan<'span> + Send + Sync,
404
{
405
    use tracing_appender::{
406
        non_blocking,
407
        rolling::{RollingFileAppender, Rotation},
408
    };
409
    let timer = time::new_formatter(granularity);
410

            
411
    let filter = filt_from_str_verbose(&config.filter, "logging.files.filter")?;
412
    let rotation = match config.rotate {
413
        LogRotation::Daily => Rotation::DAILY,
414
        LogRotation::Hourly => Rotation::HOURLY,
415
        _ => Rotation::NEVER,
416
    };
417
    let path = config.path.path(path_resolver)?;
418

            
419
    let directory = match path.parent() {
420
        None => {
421
            return Err(anyhow!(
422
                "Logfile path \"{}\" did not have a parent directory",
423
                path.display_lossy()
424
            ));
425
        }
426
        Some(p) if p == Path::new("") => Path::new("."),
427
        Some(d) => d,
428
    };
429
    mistrust.make_directory(directory).with_context(|| {
430
        format!(
431
            "Unable to create parent directory for logfile \"{}\"",
432
            path.display_lossy()
433
        )
434
    })?;
435
    let fname = path
436
        .file_name()
437
        .ok_or_else(|| anyhow!("No path for log file"))
438
        .map(Path::new)?;
439

            
440
    let appender = RollingFileAppender::new(rotation, directory, fname);
441
    let (nonblocking, guard) = non_blocking(appender);
442
    let layer = fmt::layer()
443
        // we apply custom field formatting so that error fields are listed last
444
        .fmt_fields(fields::ErrorsLastFieldFormatter)
445
        .with_ansi(false)
446
        .with_writer(nonblocking)
447
        .with_timer(timer)
448
        .with_filter(filter);
449
    Ok((layer, guard))
450
}
451

            
452
/// Try to construct a tracing [`Layer`] for all of the configured logfiles.
453
///
454
/// On success, return that layer along with a list of [`WorkerGuard`]s that
455
/// need to be dropped when the program exits.
456
306
fn logfile_layers<S>(
457
306
    config: &LoggingConfig,
458
306
    mistrust: &Mistrust,
459
306
    path_resolver: &CfgPathResolver,
460
306
) -> Result<(impl Layer<S> + use<S>, Vec<WorkerGuard>)>
461
306
where
462
306
    S: Subscriber + for<'span> tracing_subscriber::registry::LookupSpan<'span> + Send + Sync,
463
{
464
306
    let mut guards = Vec::new();
465
306
    if config.files.is_empty() {
466
        // As above, we have Option<Layer> implements Layer, so we can return
467
        // None in this case.
468
306
        return Ok((None, guards));
469
    }
470

            
471
    let (layer, guard) = logfile_layer(
472
        &config.files[0],
473
        config.time_granularity,
474
        mistrust,
475
        path_resolver,
476
    )?;
477
    guards.push(guard);
478

            
479
    // We have to use a dyn pointer here so we can build up linked list of
480
    // arbitrary depth.
481
    let mut layer: Box<dyn Layer<S> + Send + Sync + 'static> = Box::new(layer);
482

            
483
    for logfile in &config.files[1..] {
484
        let (new_layer, guard) =
485
            logfile_layer(logfile, config.time_granularity, mistrust, path_resolver)?;
486
        layer = Box::new(layer.and_then(new_layer));
487
        guards.push(guard);
488
    }
489

            
490
    Ok((Some(layer), guards))
491
306
}
492

            
493
/// Configure a panic handler to send everything to tracing, in addition to our
494
/// default panic behavior.
495
306
fn install_panic_handler() {
496
    // TODO library support: There's a library called `tracing-panic` that
497
    // provides a hook we could use instead, but that doesn't have backtrace
498
    // support.  We should consider using it if it gets backtrace support in the
499
    // future.  We should also keep an eye on `tracing` to see if it learns how
500
    // to do this for us.
501
306
    let default_handler = std::panic::take_hook();
502
306
    std::panic::set_hook(Box::new(move |panic_info| {
503
        // Note that if we were ever to _not_ call this handler,
504
        // we would want to abort on nested panics and !can_unwind cases.
505
        default_handler(panic_info);
506

            
507
        // This statement is copied from stdlib.
508
        let msg = match panic_info.payload().downcast_ref::<&'static str>() {
509
            Some(s) => *s,
510
            None => match panic_info.payload().downcast_ref::<String>() {
511
                Some(s) => &s[..],
512
                None => "Box<dyn Any>",
513
            },
514
        };
515

            
516
        let backtrace = std::backtrace::Backtrace::force_capture();
517
        match panic_info.location() {
518
            Some(location) => error!("Panic at {}: {}\n{}", location, msg, backtrace),
519
            None => error!("Panic at ???: {}\n{}", msg, backtrace),
520
        };
521
    }));
522
306
}
523

            
524
/// Opaque structure that gets dropped when the program is shutting down,
525
/// after logs are no longer needed.  The `Drop` impl flushes buffered messages.
526
#[cfg_attr(feature = "experimental-api", visibility::make(pub))]
527
pub(crate) struct LogGuards {
528
    /// The actual list of guards we're returning.
529
    #[allow(unused)]
530
    guards: Vec<WorkerGuard>,
531

            
532
    /// A safelog guard, for use if we have decided to disable safe logging.
533
    #[allow(unused)]
534
    safelog_guard: Option<safelog::Guard>,
535
}
536

            
537
/// Set up logging.
538
///
539
/// Note that the returned LogGuard must be dropped precisely when the program
540
/// quits; they're used to ensure that all the log messages are flushed.
541
306
#[cfg_attr(feature = "experimental-api", visibility::make(pub))]
542
306
#[cfg_attr(docsrs, doc(cfg(feature = "experimental-api")))]
543
306
pub(crate) fn setup_logging(
544
306
    config: &LoggingConfig,
545
306
    mistrust: &Mistrust,
546
306
    path_resolver: &CfgPathResolver,
547
306
    cli: Option<&str>,
548
306
) -> Result<LogGuards> {
549
    // Important: We have to make sure that the individual layers we add here
550
    // are not filters themselves.  That means, for example, that we can't add
551
    // an `EnvFilter` layer unless we want it to apply globally to _all_ layers.
552
    //
553
    // For a bit of discussion on the difference between per-layer filters and filters
554
    // that apply to the entire registry, see
555
    // https://docs.rs/tracing-subscriber/0.3.5/tracing_subscriber/layer/index.html#global-filtering
556

            
557
306
    let registry = registry().with(console_layer(config, cli)?);
558

            
559
    #[cfg(feature = "journald")]
560
306
    let registry = registry.with(journald_layer(config)?);
561

            
562
    #[cfg(feature = "opentelemetry")]
563
306
    let registry = registry.with(otel_layer(config, path_resolver)?);
564

            
565
    #[cfg(feature = "tokio-console")]
566
306
    let registry = {
567
        // Note 1: We can't enable console_subscriber unconditionally when the `tokio-console`
568
        // feature is enabled, since it panics unless tokio is built with  `--cfg tokio_unstable`,
569
        // but we want arti to work with --all-features without any special --cfg.
570
        //
571
        // Note 2: We have to use an `Option` here, since the type of the registry changes
572
        // with whatever you add to it.
573
306
        let tokio_layer = if config.tokio_console.enabled {
574
            Some(console_subscriber::spawn())
575
        } else {
576
306
            None
577
        };
578
306
        registry.with(tokio_layer)
579
    };
580

            
581
306
    let (layer, guards) = logfile_layers(config, mistrust, path_resolver)?;
582
306
    let registry = registry.with(layer);
583

            
584
306
    registry.init();
585

            
586
306
    let safelog_guard = if config.log_sensitive_information {
587
        match safelog::disable_safe_logging() {
588
            Ok(guard) => Some(guard),
589
            Err(e) => {
590
                // We don't need to propagate this error; it isn't the end of
591
                // the world if we were unable to disable safe logging.
592
                warn_report!(e, "Unable to disable safe logging");
593
                None
594
            }
595
        }
596
    } else {
597
306
        None
598
    };
599

            
600
306
    install_panic_handler();
601

            
602
306
    Ok(LogGuards {
603
306
        guards,
604
306
        safelog_guard,
605
306
    })
606
306
}