Files
clawmates/crates/cm-telemetry/src/lib.rs
T
Omar SobhandClaude Fable 5 add4f79fed Rebrand: TeamClaw -> Clawmates (clawmates.work)
Full-depth rename per the approved plan; the 'claw' product vocabulary
(claws, /claws routes, clawId, Claw Chat) stays — it is now the brand.

- Display brand: Clawmates (manifest, titles, hero, login/rail logo
  'clawmates'); default host app.clawmates.work; registry
  ghcr.io/clawmates
- Crates tc-* -> cm-* (16 crates + all imports); binaries
  clawmates-server/broker/bundler; images clawmates/*; env prefix
  CLAWMATES_* (+ CM_TEST_DATABASE_URL / CM_LIVE_LLM); config
  clawmates.toml; helm chart deploy/helm/clawmates with clawmates-*
  resources; db names clawmates*; sockets /run/clawmates; cookie
  cm_session; kind cluster clawmates-test; seccomp node profile
  clawmates-agent-profile.json
- All 9 Playwright brand assertions updated in lockstep; historical
  spec document left untouched as the only remaining 'TeamClaw'
- Local env migrated: dev pg clawmates-dev-pg/clawmates_dev, shared
  test server clawmates-test-pg, kind cluster recreated with image +
  profile, compose images rebuilt under clawmates/*

Verified end to end: 161 Rust + 68 frontend tests, 29 Playwright
journeys, 4 live kind tests, helm/install/LOC/placeholder gates, and
the clean-room install rehearsal serving the clawmates login page from
a signed bundle of the rebuilt images.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-06-10 12:31:25 -05:00

93 lines
3.3 KiB
Rust

//! Tracing setup (§16 NFR: observability). Always installs a fmt
//! subscriber honoring `RUST_LOG`; with an OTLP endpoint configured,
//! spans also export over OTLP/HTTP — to a collector shipped in the
//! air-gapped bundle or any cloud backend. No endpoint, no export, no
//! network: telemetry never phones home uninvited.
use opentelemetry::trace::TracerProvider as _;
use opentelemetry::KeyValue;
use opentelemetry_otlp::WithExportConfig;
use opentelemetry_sdk::trace::SdkTracerProvider;
use opentelemetry_sdk::Resource;
use tracing_subscriber::layer::SubscriberExt;
use tracing_subscriber::util::SubscriberInitExt;
use tracing_subscriber::{EnvFilter, Layer};
/// Keeps the exporter alive; dropping it flushes pending spans.
pub struct TelemetryGuard {
provider: Option<SdkTracerProvider>,
}
impl TelemetryGuard {
/// Forces all batched spans out — call before process exit (tests
/// call it to assert delivery).
pub fn flush(&self) {
if let Some(provider) = &self.provider {
let _ = provider.force_flush();
}
}
}
impl Drop for TelemetryGuard {
fn drop(&mut self) {
if let Some(provider) = self.provider.take() {
let _ = provider.force_flush();
let _ = provider.shutdown();
}
}
}
#[derive(Debug, thiserror::Error)]
pub enum TelemetryError {
#[error("otlp exporter: {0}")]
Exporter(String),
#[error("subscriber already installed")]
AlreadyInstalled,
}
/// Installs the global subscriber. `otlp_endpoint` is the collector's
/// OTLP/HTTP base (e.g. `http://otel-collector:4318`).
pub fn init(
service_name: &str,
otlp_endpoint: Option<&str>,
) -> Result<TelemetryGuard, TelemetryError> {
let fmt = tracing_subscriber::fmt::layer().with_target(false);
let filter = EnvFilter::try_from_default_env()
.unwrap_or_else(|_| EnvFilter::new("info,sqlx=warn,hyper=warn"));
match otlp_endpoint {
Some(endpoint) => {
let exporter = opentelemetry_otlp::SpanExporter::builder()
.with_http()
.with_endpoint(format!("{}/v1/traces", endpoint.trim_end_matches('/')))
.build()
.map_err(|e| TelemetryError::Exporter(e.to_string()))?;
let provider = SdkTracerProvider::builder()
.with_batch_exporter(exporter)
.with_resource(
Resource::builder()
.with_attributes([KeyValue::new("service.name", service_name.to_owned())])
.build(),
)
.build();
let tracer = provider.tracer(service_name.to_owned());
let otel = tracing_opentelemetry::layer().with_tracer(tracer);
tracing_subscriber::registry()
.with(fmt.with_filter(filter))
.with(otel)
.try_init()
.map_err(|_| TelemetryError::AlreadyInstalled)?;
Ok(TelemetryGuard {
provider: Some(provider),
})
}
None => {
tracing_subscriber::registry()
.with(fmt.with_filter(filter))
.try_init()
.map_err(|_| TelemetryError::AlreadyInstalled)?;
Ok(TelemetryGuard { provider: None })
}
}
}