greentic-telemetry 0.1.3

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`], [`opentelemetry`], and the shared [`greentic-types`] domain crate.

[`tracing`]: https://github.com/tokio-rs/tracing
[`opentelemetry`]: https://opentelemetry.io/
[`greentic-types`]: https://github.com/greentic-ai/greentic-types

## Highlights

- `TelemetryCtx`: structured context carrying `{tenant, team, user, session, flow, node, provider}`.
- `CtxLayer`: a `tracing` layer that injects `TelemetryCtx` fields into spans and OTLP attributes.
- `init_otlp`: opinionated OTLP pipeline wiring with fmt logging + OpenTelemetry exporter.
- Helpers for integration tests (`testutil::span_recorder`) and Elastic/Kibana developer bundle.
- Existing `init_telemetry` bootstrap preserved for legacy callers.

## Quickstart

```rust
use greentic_telemetry::{CtxLayer, TelemetryCtx, init_otlp, OtlpConfig};
use tracing::{info, span, Level};
use tracing_subscriber::{layer::SubscriberExt, Registry};

fn telemetry_ctx() -> TelemetryCtx {
    TelemetryCtx::default()
        .with_tenant("tenant-acme")
        .with_session("sess-123")
        .with_flow("flow-intake")
        .with_provider("runner")
        .with_node("node-parse")
}

fn main() -> anyhow::Result<()> {
    // Wire OTLP + fmt logging once at startup (Tokio runtime assumed for batches).
    init_otlp(OtlpConfig {
        endpoint: "http://localhost:4317".into(),
        service_name: "greentic-runner".into(),
        insecure: true,
    })?;

    let ctx_layer = CtxLayer::new(telemetry_ctx);
    let subscriber = Registry::default().with(ctx_layer);
    let _guard = tracing::subscriber::set_default(subscriber);

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

    Ok(())
}
```

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

### Bridging `greentic-types`

`TelemetryCtx` implements `From<&greentic_types::TenantCtx>`, `From<&InvocationEnvelope>`, and `From<&telemetry::SpanContext>`, so existing domain payloads can be mapped without manual string conversions:

```rust
use greentic_telemetry::TelemetryCtx;
use greentic_types::{InvocationEnvelope, TenantCtx};

fn span_ctx(env: &InvocationEnvelope) -> TelemetryCtx {
    TelemetryCtx::from(env)
}

fn tenant_ctx(ctx: &TenantCtx) -> TelemetryCtx {
    TelemetryCtx::from(ctx)
}
```

## OTLP wiring

`init_otlp` produces a `tracing` `Dispatch` with:

- `tracing_subscriber::fmt` layer (target + thread info disabled by default)
- `tracing_opentelemetry::layer` connected to an OTLP gRPC exporter
- Resource set with `service.name` from `OtlpConfig`

It installs the `Dispatch` as the global default and returns a clone so callers can reinstall or inspect it. Use `otlp::shutdown()` (or the legacy `shutdown()` re-export) on graceful shutdown to flush spans.

### Legacy bootstrap

`init_telemetry` / `TelemetryConfig` continue to provide the previous logging preset (stdout/file appenders + OTLP when `OTEL_EXPORTER_OTLP_ENDPOINT` is set). New services should prefer `init_otlp` for explicit configuration.

## 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/`.

```bash
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:

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

The new context propagation integration test (`tests/context_propagation.rs`) asserts that `CtxLayer` injects the Greentic attributes across spans.