mod event_formatter;
pub mod floor_char_boundary;
mod log_layer;
mod otlp_layer;
mod tracing_fields;
pub use event_formatter::*;
pub use log_layer::*;
pub use otlp_layer::*;
pub use tracing_fields::*;
use crate::error::Result;
use crate::observability::{HeadersFilter, HEADERS_FILTER};
use opentelemetry::global::set_error_handler;
use std::sync::OnceLock;
use tracing_appender::non_blocking::WorkerGuard;
use tracing_subscriber::layer::Layered;
use tracing_subscriber::util::SubscriberInitExt;
use tracing_subscriber::{
    filter::EnvFilter, layer::SubscriberExt, registry, reload::Handle, Layer, Registry,
};
pub const SANITIZED_VALUE: &str = "*****";
pub static LOG_LAYER_HANDLE: OnceLock<LogLayerHandle> = OnceLock::new();
pub static OTLP_LAYER_HANDLE: OnceLock<TraceLayerHandle> = OnceLock::new();
pub type LogLayerHandle =
    Handle<EnvFilter, Layered<Option<Box<dyn Layer<Registry> + Send + Sync>>, Registry>>;
pub type TraceLayerHandle = Handle<EnvFilter, Registry>;
#[allow(clippy::too_many_arguments)]
pub fn init_tracing(
    log_level: &str,
    trace_level: &str,
    version: &str,
    service_name: &str,
    component_name: &str,
    traces_endpoint: Option<&str>,
    log_msg_length: Option<usize>,
    buffered_lines_limit: Option<usize>,
    headers_filter: Option<HeadersFilter>,
) -> Result<WorkerGuard> {
    let (log_layer, log_reload, worker) = log_layer(
        log_level,
        version,
        service_name,
        component_name,
        log_msg_length,
        buffered_lines_limit,
    )?;
    let (otlp_layer, otlp_reload) = otlp_layer(trace_level, component_name, traces_endpoint)?;
    registry().with(otlp_layer).with(log_layer).try_init()?;
    let _ = LOG_LAYER_HANDLE.get_or_init(|| log_reload);
    if let Some(otlp_reload) = otlp_reload {
        let _ = OTLP_LAYER_HANDLE.get_or_init(|| otlp_reload);
    }
    if let Some(headers_filter) = headers_filter {
        let _ = HEADERS_FILTER.get_or_init(|| headers_filter);
    }
    set_error_handler(|err| {
        tracing::error!("{err}");
    })?;
    set_panic_hook();
    Ok(worker)
}
fn set_panic_hook() {
    std::panic::set_hook(Box::new(|panic| {
        if let Some(location) = panic.location() {
            tracing::error!(
                message = %panic,
                panic.file = location.file(),
                panic.line = location.line(),
                panic.column = location.column(),
            );
        } else {
            tracing::error!(message = %panic);
        }
    }));
}