pikadick/
logger.rs

1mod delay_writer;
2
3pub use self::delay_writer::DelayWriter;
4use crate::config::Config;
5use anyhow::Context;
6use opentelemetry_otlp::WithExportConfig;
7use tonic::metadata::{
8    MetadataKey,
9    MetadataMap,
10};
11use tracing_appender::non_blocking::WorkerGuard;
12use tracing_log::LogTracer;
13use tracing_subscriber::{
14    filter::EnvFilter,
15    layer::SubscriberExt,
16};
17
18/// Try to setup a logger.
19///
20/// Must be called from a tokio runtime.
21pub fn setup(config: &Config) -> anyhow::Result<WorkerGuard> {
22    let file_writer = tracing_appender::rolling::hourly(config.log_file_dir(), "log.txt");
23    let (nonblocking_file_writer, guard) = tracing_appender::non_blocking(file_writer);
24
25    let mut env_filter = EnvFilter::default();
26    // If the user provides logging directives, use them
27    for directive in config.log.directives.iter() {
28        env_filter = env_filter.add_directive(
29            directive
30                .parse()
31                .context("failed to parse logging directive")?,
32        );
33    }
34
35    let stderr_formatting_layer = tracing_subscriber::fmt::layer().with_writer(std::io::stderr);
36    let file_formatting_layer = tracing_subscriber::fmt::layer()
37        .with_ansi(false)
38        .with_writer(nonblocking_file_writer);
39
40    let opentelemetry_layer = if config.log.opentelemetry {
41        eprintln!("setting up opentelemetry...");
42
43        opentelemetry::global::set_error_handler(|error| {
44            // Print to stderr.
45            // There was an error logging something, so we avoid using the logging system.
46            eprintln!("opentelemetry error: {:?}", anyhow::Error::from(error));
47        })
48        .context("failed to set opentelemetry error handler")?;
49
50        let mut map = MetadataMap::with_capacity(config.log.headers.len());
51        for (k, v) in config.log.headers.iter() {
52            let k = MetadataKey::from_bytes(k.as_bytes()).context("invalid header name")?;
53            map.insert(k, v.parse().context("invalid header value")?);
54        }
55
56        let exporter = {
57            let mut exporter = opentelemetry_otlp::new_exporter()
58                .tonic()
59                .with_metadata(map)
60                .with_tls_config(Default::default());
61
62            if let Some(endpoint) = config.log.endpoint.as_ref() {
63                exporter = exporter.with_endpoint(endpoint);
64            }
65
66            exporter
67        };
68
69        let tracer = opentelemetry_otlp::new_pipeline()
70            .tracing()
71            .with_exporter(exporter)
72            .install_batch(opentelemetry_sdk::runtime::Tokio)
73            .context("failed to install otlp opentelemetry exporter")?;
74
75        Some(tracing_opentelemetry::layer().with_tracer(tracer))
76    } else {
77        None
78    };
79
80    let subscriber = tracing_subscriber::Registry::default()
81        .with(env_filter)
82        .with(file_formatting_layer)
83        .with(stderr_formatting_layer);
84
85    if let Some(opentelemetry_layer) = opentelemetry_layer {
86        let subscriber = subscriber.with(opentelemetry_layer);
87
88        tracing::subscriber::set_global_default(subscriber).context("failed to set subscriber")?;
89    } else {
90        tracing::subscriber::set_global_default(subscriber).context("failed to set subscriber")?;
91    }
92
93    LogTracer::init().context("failed to init log tracer")?;
94
95    Ok(guard)
96}