Skip to main content

minco_plugin_observability/
lib.rs

1//! Default structured-observability plugin.
2#![forbid(unsafe_code)]
3
4use minco_core::{
5    CapabilityProvision, Plugin, PluginContext, PluginDescriptor, PluginError, PluginId,
6};
7use semver::Version;
8use serde::{Deserialize, Serialize};
9use std::sync::Arc;
10use tracing_subscriber::EnvFilter;
11
12#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
13pub struct ObservabilityConfig {
14    pub service_name: String,
15    pub json: bool,
16    pub default_filter: String,
17}
18
19#[derive(Debug, thiserror::Error)]
20pub enum ObservabilityError {
21    #[error("failed to initialize tracing subscriber: {0}")]
22    Initialization(String),
23}
24
25impl Default for ObservabilityConfig {
26    fn default() -> Self {
27        Self {
28            service_name: "minco-app".into(),
29            json: true,
30            default_filter: "info,tower_http=info".into(),
31        }
32    }
33}
34
35impl ObservabilityConfig {
36    pub fn init(&self) -> Result<(), ObservabilityError> {
37        let filter = EnvFilter::try_from_default_env()
38            .unwrap_or_else(|_| EnvFilter::new(&self.default_filter));
39        let result = if self.json {
40            tracing_subscriber::fmt()
41                .with_env_filter(filter)
42                .json()
43                .try_init()
44        } else {
45            tracing_subscriber::fmt().with_env_filter(filter).try_init()
46        };
47        result.map_err(|error| ObservabilityError::Initialization(error.to_string()))
48    }
49}
50
51#[derive(Debug, Clone)]
52pub struct ObservabilityPlugin {
53    config: ObservabilityConfig,
54}
55impl ObservabilityPlugin {
56    pub const fn new(config: ObservabilityConfig) -> Self {
57        Self { config }
58    }
59}
60impl Default for ObservabilityPlugin {
61    fn default() -> Self {
62        Self::new(ObservabilityConfig::default())
63    }
64}
65
66impl Plugin for ObservabilityPlugin {
67    fn descriptor(&self) -> PluginDescriptor {
68        let mut descriptor = PluginDescriptor::new(
69            PluginId::new("observability").expect("static id"),
70            Version::new(1, 0, 0),
71            "Structured tracing and CloudWatch-compatible JSON logging",
72        );
73        descriptor.default_enabled = true;
74        descriptor.provides.push(CapabilityProvision {
75            name: "observability.tracing".into(),
76            version: Version::new(1, 0, 0),
77        });
78        descriptor
79    }
80    fn install(&self, context: &mut PluginContext<'_>) -> Result<(), PluginError> {
81        context.services().insert(Arc::new(self.config.clone()))?;
82        Ok(())
83    }
84}