greentic-pack-dev 1.1.26387674049

Greentic pack builder CLI
Documentation
use anyhow::Result;
use greentic_config_types::{TelemetryConfig, TelemetryExporterKind};
pub use greentic_telemetry::with_task_local;
use greentic_telemetry::{
    TelemetryConfig as ServiceTelemetryConfig, TelemetryCtx,
    export::{ExportConfig, ExportMode, Sampling},
    init_telemetry_auto, init_telemetry_from_config, set_current_telemetry_ctx,
};
use greentic_types::TenantCtx;

/// Install the default Greentic telemetry stack for the given service.
pub fn install(service_name: &str) -> Result<()> {
    init_telemetry_auto(ServiceTelemetryConfig {
        service_name: service_name.to_string(),
    })
}

/// Install telemetry honoring greentic-config telemetry settings.
pub fn install_with_config(service_name: &str, cfg: &TelemetryConfig) -> Result<()> {
    if !cfg.enabled || matches!(cfg.exporter, TelemetryExporterKind::None) {
        return Ok(());
    }

    let export = match cfg.exporter {
        TelemetryExporterKind::Otlp => export_config(ExportMode::OtlpGrpc, cfg),
        TelemetryExporterKind::Stdout => export_config(ExportMode::JsonStdout, cfg),
        TelemetryExporterKind::Gcp => export_config(ExportMode::GcpCloudTrace, cfg),
        TelemetryExporterKind::Azure => export_config(ExportMode::AzureAppInsights, cfg),
        TelemetryExporterKind::Aws => export_config(ExportMode::AwsXRay, cfg),
        TelemetryExporterKind::None => unreachable!("handled above"),
    };

    init_telemetry_from_config(
        ServiceTelemetryConfig {
            service_name: service_name.to_string(),
        },
        export,
    )
}

fn export_config(mode: ExportMode, cfg: &TelemetryConfig) -> ExportConfig {
    let mut export = ExportConfig::default();
    export.mode = mode;
    export.endpoint = cfg.endpoint.clone();
    export.sampling = Sampling::TraceIdRatio(cfg.sampling as f64);
    export.compression = None;
    export
}

/// Map the provided tenant context into the task-local telemetry slot.
pub fn set_current_tenant_ctx(ctx: &TenantCtx) {
    use greentic_types::telemetry::attr_keys;

    let mut telemetry = TelemetryCtx::new(ctx.tenant_id.as_ref()).with_env(ctx.env.as_str());

    if let Some(session) = ctx.session_id() {
        telemetry = telemetry.with_session(session);
    }
    if let Some(flow) = ctx.flow_id() {
        telemetry = telemetry.with_flow(flow);
    }
    if let Some(node) = ctx.node_id() {
        telemetry = telemetry.with_node(node);
    }
    if let Some(provider) = ctx.provider_id() {
        telemetry = telemetry.with_provider(provider);
    }
    // B11 rollout identifiers ride the free-form attributes map under the same
    // canonical keys the greentic-types bridge uses; mirror that projection so
    // packc telemetry carries env + revision/bundle/customer attribution too.
    if let Some(v) = ctx.attributes.get(attr_keys::CUSTOMER_ID) {
        telemetry = telemetry.with_customer_id(v);
    }
    if let Some(v) = ctx.attributes.get(attr_keys::DEPLOYMENT_ID) {
        telemetry = telemetry.with_deployment_id(v);
    }
    if let Some(v) = ctx.attributes.get(attr_keys::BUNDLE_ID) {
        telemetry = telemetry.with_bundle_id(v);
    }
    if let Some(v) = ctx.attributes.get(attr_keys::REVISION_ID) {
        telemetry = telemetry.with_revision_id(v);
    }

    set_current_telemetry_ctx(telemetry);
}

#[cfg(test)]
mod tests {
    use super::*;
    use greentic_config_types::TelemetryConfig;
    use greentic_types::{EnvId, TenantCtx, TenantId};
    use std::str::FromStr;

    #[test]
    fn install_with_config_is_noop_when_disabled() {
        let cfg = TelemetryConfig {
            enabled: false,
            exporter: TelemetryExporterKind::Otlp,
            endpoint: Some("http://localhost:4317".to_string()),
            sampling: 1.0,
        };

        install_with_config("packc-test", &cfg).expect("disabled config should be a no-op");
    }

    #[test]
    fn install_with_config_is_noop_for_none_exporter() {
        let cfg = TelemetryConfig {
            enabled: true,
            exporter: TelemetryExporterKind::None,
            endpoint: None,
            sampling: 0.25,
        };

        install_with_config("packc-test", &cfg).expect("none exporter should be a no-op");
    }

    #[test]
    fn set_current_tenant_ctx_accepts_full_context() {
        let tenant = TenantId::from_str("tenant-a").expect("tenant");
        let env = EnvId::from_str("dev").expect("env");
        let mut ctx = TenantCtx::new(env, tenant)
            .with_session("sess-123")
            .with_flow("flow.main")
            .with_node("node-1")
            .with_provider("provider-1");
        // Exercise the B11 rollout-ID projection branches.
        ctx.attributes.insert(
            greentic_types::telemetry::attr_keys::BUNDLE_ID.to_string(),
            "customer.support".to_string(),
        );

        set_current_tenant_ctx(&ctx);
    }
}