fregate/observability/
tracing.rs

1//! Tools initialise logging and tracing
2
3mod event_formatter;
4pub mod floor_char_boundary;
5mod log_layer;
6mod otlp_layer;
7mod tracing_fields;
8
9pub use event_formatter::*;
10pub use log_layer::*;
11pub use otlp_layer::*;
12pub use tracing_fields::*;
13
14use crate::error::Result;
15use crate::observability::{HeadersFilter, HEADERS_FILTER};
16use opentelemetry::global::set_error_handler;
17use std::sync::OnceLock;
18use tracing_appender::non_blocking::WorkerGuard;
19use tracing_subscriber::layer::Layered;
20use tracing_subscriber::util::SubscriberInitExt;
21use tracing_subscriber::{
22    filter::EnvFilter, layer::SubscriberExt, registry, reload::Handle, Layer, Registry,
23};
24
25/// Global value to be used everywhere.
26pub const SANITIZED_VALUE: &str = "*****";
27
28/// This by default uninitialised unless you call [`crate::bootstrap()`] or [`init_tracing`] functions.
29/// Used to change log level filter
30/// See in [`example`](https://github.com/elefant-dev/fregate-rs/tree/main/examples/log-level-change) how it might be used.
31pub static LOG_LAYER_HANDLE: OnceLock<LogLayerHandle> = OnceLock::new();
32
33/// This by default uninitialised unless you call [`crate::bootstrap()`] or [`init_tracing`] functions.
34/// Used to change trace level filter
35/// See in [`example`](https://github.com/elefant-dev/fregate-rs/tree/main/examples/log-level-change) how it might be used.
36pub static OTLP_LAYER_HANDLE: OnceLock<TraceLayerHandle> = OnceLock::new();
37
38/// Alias for [`Handle<EnvFilter, Layered<Option<Box<dyn Layer<Registry> + Send + Sync>>, Registry>>`]
39pub type LogLayerHandle =
40    Handle<EnvFilter, Layered<Option<Box<dyn Layer<Registry> + Send + Sync>>, Registry>>;
41
42/// Alias for [`Handle<EnvFilter, Registry>`]
43pub type TraceLayerHandle = Handle<EnvFilter, Registry>;
44
45/// Set-up:\
46/// 1. [`log_layer()`] with custom event formatter [`EventFormatter`].\
47/// 2. [`otlp_layer()`].\
48/// 3. Reload filters for both layers: [`OTLP_LAYER_HANDLE`] and [`LOG_LAYER_HANDLE`].\
49/// 4. [`HEADERS_FILTER`] to be used in [`crate::extensions::HeaderFilterExt`].\
50/// 5. Sets panic hook.\
51/// Uses [`tracing_appender`] crate to do non blocking writes to stdout, so returns [`WorkerGuard`]. Read more here: [`https://docs.rs/tracing-appender/latest/tracing_appender/non_blocking/struct.WorkerGuard.html`]
52#[allow(clippy::too_many_arguments)]
53pub fn init_tracing(
54    log_level: &str,
55    trace_level: &str,
56    version: &str,
57    service_name: &str,
58    component_name: &str,
59    traces_endpoint: Option<&str>,
60    log_msg_length: Option<usize>,
61    buffered_lines_limit: Option<usize>,
62    headers_filter: Option<HeadersFilter>,
63) -> Result<WorkerGuard> {
64    let (log_layer, log_reload, worker) = log_layer(
65        log_level,
66        version,
67        service_name,
68        component_name,
69        log_msg_length,
70        buffered_lines_limit,
71    )?;
72    let (otlp_layer, otlp_reload) = otlp_layer(trace_level, component_name, traces_endpoint)?;
73    registry().with(otlp_layer).with(log_layer).try_init()?;
74
75    let _ = LOG_LAYER_HANDLE.get_or_init(|| log_reload);
76    if let Some(otlp_reload) = otlp_reload {
77        let _ = OTLP_LAYER_HANDLE.get_or_init(|| otlp_reload);
78    }
79    if let Some(headers_filter) = headers_filter {
80        let _ = HEADERS_FILTER.get_or_init(|| headers_filter);
81    }
82
83    set_error_handler(|err| {
84        tracing::error!("{err}");
85    })?;
86    set_panic_hook();
87
88    Ok(worker)
89}
90
91fn set_panic_hook() {
92    // Capture the span context in which the program panicked
93    std::panic::set_hook(Box::new(|panic| {
94        // If the panic has a source location, record it as structured fields.
95        if let Some(location) = panic.location() {
96            tracing::error!(
97                message = %panic,
98                panic.file = location.file(),
99                panic.line = location.line(),
100                panic.column = location.column(),
101            );
102        } else {
103            tracing::error!(message = %panic);
104        }
105    }));
106}