greentic-telemetry 0.3.0

Thin telemetry facade for Greentic: tracing/logging/metrics with OTLP + WASM.
Documentation

greentic-telemetry

Tenant-aware telemetry utilities for Greentic services built on top of tracing and opentelemetry.

Highlights

  • TelemetryCtx: lightweight context carrying {tenant, session, flow, node, provider}.
  • layer_from_task_local: grab the context from a Tokio task-local without wiring closures.
  • CtxLayer (layer_with): legacy closure-based path kept for backwards compatibility.
  • init_otlp: install an OTLP pipeline (with optional fmt layer when GT_TELEMETRY_FMT=1).
  • Utilities for integration testing (testutil::span_recorder) and task-local helpers.

Quickstart

use greentic_telemetry::{
    init_otlp, layer_from_task_local, set_current_telemetry_ctx, with_task_local, OtlpConfig,
    TelemetryCtx,
};
use tracing::{info, span, Level};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    with_task_local(async {
        set_current_telemetry_ctx(
            TelemetryCtx::new("tenant-acme")
                .with_session("sess-123")
                .with_flow("flow-intake")
                .with_node("node-parse")
                .with_provider("runner"),
        );

        init_otlp(
            OtlpConfig {
                service_name: "greentic-runner".into(),
                endpoint: Some("http://localhost:4317".into()),
                sampling_rate: Some(1.0),
            },
            vec![Box::new(layer_from_task_local())],
        )?;

        let span = span!(
            Level::INFO,
            "node_execute",
            "gt.tenant" = tracing::field::Empty,
            "gt.session" = tracing::field::Empty,
            "gt.flow" = tracing::field::Empty,
            "gt.node" = tracing::field::Empty,
            "gt.provider" = tracing::field::Empty,
        );
        let _enter = span.enter();
        info!("executing node with injected telemetry context");

        Ok(())
    })
    .await
}

Spans automatically receive the Greentic attributes (as tracing fields and OTLP attributes), ensuring the collector exports {tenant, session, flow, node, provider} consistently via the task-local path.

OTLP wiring

init_otlp installs a tracing subscriber composed of:

  • tracing_subscriber::fmt layer (behind the fmt feature flag)
  • tracing_opentelemetry::layer connected to an OTLP gRPC exporter
  • service.name populated from OtlpConfig

The subscriber becomes the global default; use opentelemetry::global::shutdown_tracer_provider() during graceful shutdown to flush spans.

Testing utilities

testutil::span_recorder() returns a (CaptureLayer, Arc<Mutex<Vec<RecordedSpan>>>) pair for asserting that spans carry TelemetryCtx. See tests/context_propagation.rs for an end-to-end example exercising propagation across nested spans.

Dev Elastic bundle

A ready-to-run Elastic/Kibana/OpenTelemetry Collector stack lives in dev/elastic-compose/.

docker compose -f dev/elastic-compose/docker-compose.yml up -d
export OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4317

Then open Kibana at http://localhost:5601/. The default collector config writes spans/metrics to stdout for quick inspection—customise otel-config.yaml if you want to forward to Elastic APM.

The existing dev/docker-compose.elastic.yml + Filebeat setup remains available if you need the legacy log ingestion pipeline.

Verification

This crate must pass:

cargo fmt
cargo clippy --all-targets --all-features -- -D warnings
cargo test --all-features

Local CI checks

Run ci/local_check.sh before pushing to mirror the GitHub Actions matrix locally. The script is offline by default; opt in to extra checks via:

  • LOCAL_CHECK_ONLINE=1 — run networked steps (cargo publish dry-run, cloud telemetry loops, schema curls).
  • LOCAL_CHECK_STRICT=1 — treat skipped steps as failures and require every optional tool/env to be present.
  • LOCAL_CHECK_VERBOSE=1 — echo each command for easier debugging.

The generated .git/hooks/pre-push hook invokes the script automatically; remove or edit it if you prefer to run the checks manually.