Skip to main content

chaincodec_observability/
tracing_setup.rs

1//! Tracing / logging initialisation helpers.
2
3use serde::{Deserialize, Serialize};
4use std::collections::HashMap;
5use tracing_subscriber::{fmt, layer::SubscriberExt, util::SubscriberInitExt, EnvFilter};
6
7/// Log level per component.
8#[derive(Debug, Clone, Serialize, Deserialize)]
9pub struct LogConfig {
10    /// Global default level: "trace" | "debug" | "info" | "warn" | "error"
11    #[serde(default = "default_level")]
12    pub level: String,
13    /// Override per component: component_name → level
14    #[serde(default)]
15    pub components: HashMap<String, String>,
16    /// Emit JSON structured logs (true) or human-readable text (false)
17    #[serde(default)]
18    pub json: bool,
19}
20
21fn default_level() -> String {
22    "info".to_string()
23}
24
25impl Default for LogConfig {
26    fn default() -> Self {
27        Self {
28            level: "info".into(),
29            components: HashMap::new(),
30            json: false,
31        }
32    }
33}
34
35/// OpenTelemetry tracing configuration.
36#[derive(Debug, Clone, Serialize, Deserialize, Default)]
37pub struct TracingConfig {
38    /// OTLP exporter endpoint (e.g. "http://localhost:4317")
39    #[serde(skip_serializing_if = "Option::is_none")]
40    pub otlp_endpoint: Option<String>,
41    /// Service name for OTLP traces
42    #[serde(default = "default_service_name")]
43    pub service_name: String,
44}
45
46fn default_service_name() -> String {
47    "chaincodec".into()
48}
49
50/// Initialise tracing with the given log config.
51/// Should be called once at application startup.
52pub fn init_tracing(config: &LogConfig) {
53    // Build the directive string: "info,chaincodec_stream=debug" etc.
54    let mut directives = config.level.clone();
55    for (component, level) in &config.components {
56        directives.push_str(&format!(",{}={}", component.replace('-', "_"), level));
57    }
58
59    let filter = EnvFilter::try_new(&directives)
60        .unwrap_or_else(|_| EnvFilter::new("info"));
61
62    if config.json {
63        tracing_subscriber::registry()
64            .with(filter)
65            .with(fmt::layer().json())
66            .init();
67    } else {
68        tracing_subscriber::registry()
69            .with(filter)
70            .with(fmt::layer())
71            .init();
72    }
73}