diff --git a/.env.example b/.env.example index d9704d9..3ec0662 100644 --- a/.env.example +++ b/.env.example @@ -28,7 +28,9 @@ HLS_CONCURRENCY=2 HLS_TIMEOUT_SECONDS=900 PLAYLIST_CLEANUP_INTERVAL_SECONDS=86400 -# ── Telemetry (release builds only) ───────────────────────────────────── +# ── Telemetry ──────────────────────────────────────────────────────────── +# Span export over OTLP in any build when set (unset = spans stay local). +# RUST_LOG also gates span export: keep it at info or debug. # OTLP_OTLS_ENDPOINT=http://localhost:4317 # ── AI Insights — Ollama (local LLM) ──────────────────────────────────── diff --git a/.specify/memory/constitution.md b/.specify/memory/constitution.md index 20926cc..c7d78ad 100644 --- a/.specify/memory/constitution.md +++ b/.specify/memory/constitution.md @@ -1,20 +1,31 @@ @@ -34,7 +45,8 @@ All features MUST follow the established layered architecture: contracts. Every DAO MUST be defined as a trait to enable mock implementations for testing. - **Database Layer** (Diesel ORM, `schema.rs`): Concrete `Sqlite*Dao` - implementations. All queries traced with OpenTelemetry. + implementations. Every query runs inside a `tracing` span via + `otel::trace_db_call`. New features MUST NOT bypass layers (e.g., HTTP handlers MUST NOT execute raw SQL). Actix actors are permitted for long-running async @@ -84,17 +96,35 @@ prohibited. ### V. Observability -All database operations and HTTP handlers MUST be instrumented -with OpenTelemetry spans via the `trace_db_call` helper or -equivalent tracing macros. +All database operations and HTTP handlers MUST be instrumented with +`tracing` spans, exported through `tracing-opentelemetry`: DAO bodies via +`otel::trace_db_call`, and handlers / feature functions via +`#[tracing::instrument]`. The per-request root span comes from the +`tracing-actix-web` middleware, which also honors an incoming W3C +`traceparent`. -- Release builds export traces to the configured OTLP endpoint. -- Debug builds use the basic logger. +- Spans are produced in debug and release builds alike. OTLP export is + enabled by `OTLP_OTLS_ENDPOINT`, not by build profile. +- Code MUST NOT thread an OpenTelemetry `Context` through call chains. + Spans parent on the ambient span. +- Every `tokio::spawn` or cross-thread fan-out (rayon, per-pass runtimes) + MUST carry its parent span: `.instrument(span)` or + `info_span!(parent: &span, ...)`. Tracing context is thread-local, and + an uncarried spawn orphans its spans onto a fresh trace. +- `RUST_LOG` gates span export as well as console output (one `EnvFilter`). + Deployments MUST run at `info` or `debug`. +- Logs are a separate pipeline: `env_logger` in debug, OTel log export in + release. - Prometheus metrics (`imageserver_image_total`, `imageserver_video_total`) MUST be maintained for key counters. - Errors MUST be logged at `error!` level with sufficient context for debugging without reproducing the issue. +**Rationale:** span parenting that relies on hand-threaded contexts drifted +into orphaned traces (the pre-migration baseline, #137). Ambient +parenting, plus an explicit carry at each spawn boundary, keeps a request or +job in one trace by construction. + ## Technology Stack & Constraints - **Language**: Rust (stable toolchain, Cargo build system) @@ -105,8 +135,9 @@ equivalent tracing macros. - **Video Processing**: ffmpeg/ffprobe (CLI, must be on PATH) - **Image Processing**: `image` crate for thumbnails, `kamadak-exif` for EXIF extraction -- **Tracing**: OpenTelemetry with OTLP export (release), - basic logger (debug) +- **Tracing**: `tracing` + `tracing-opentelemetry` (every build), OTLP + export when `OTLP_OTLS_ENDPOINT` is set; logs via `env_logger` (debug) / + OTel log export (release) - **Testing**: `cargo test`, `actix_web::test`, in-memory SQLite External dependencies (ffmpeg, Ollama) are optional runtime @@ -146,4 +177,4 @@ MUST comply with these principles. these principles. The CLAUDE.md file provides runtime development guidance and MUST remain consistent with this constitution. -**Version**: 1.0.0 | **Ratified**: 2026-02-26 | **Last Amended**: 2026-02-26 +**Version**: 1.1.0 | **Ratified**: 2026-02-26 | **Last Amended**: 2026-09-29 diff --git a/CLAUDE.md b/CLAUDE.md index 1c2bb30..c163221 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -696,7 +696,7 @@ Optional: WATCH_QUICK_INTERVAL_SECONDS=60 # Quick scan interval WATCH_FULL_INTERVAL_SECONDS=3600 # Full scan interval DATE_BACKFILL_MAX_PER_TICK=500 # Cap on canonical-date drain per watcher tick -OTLP_OTLS_ENDPOINT=http://... # OpenTelemetry collector (release builds) +OTLP_OTLS_ENDPOINT=http://... # OTLP collector for span export (any build; unset = no export) # AI Insights Configuration OLLAMA_PRIMARY_URL=http://desktop:11434 # Primary Ollama server (e.g., desktop) @@ -956,7 +956,7 @@ side if exposing beyond a trusted network. - **image**: Thumbnail generation - **walkdir**: Directory traversal - **rayon**: Parallel processing -- **opentelemetry**: Distributed tracing +- **tracing** + **tracing-opentelemetry** + **tracing-actix-web**: spans (exported via `opentelemetry-otlp`) - **bcrypt**: Password hashing - **infer**: Magic number file type detection diff --git a/README.md b/README.md index 8a6421b..693d977 100644 --- a/README.md +++ b/README.md @@ -72,7 +72,8 @@ They should be defined where the binary is located or above it in an `.env` file - `GIFS_DIRECTORY` is a path where generated video GIF thumbnails should be stored - `BIND_URL` is the url and port to bind to (typically your own IP address) - `SECRET_KEY` is the *hopefully* random string to sign Tokens with -- `RUST_LOG` is one of `off, error, warn, info, debug, trace`, from least to most noisy [error is default] +- `RUST_LOG` is one of `off, error, warn, info, debug, trace`, from least to most noisy [default: `info` for spans, `error` for dev console logs]. It also gates tracing-span export, so keep it at `info` or `debug` +- `OTLP_OTLS_ENDPOINT` (optional) is an OTLP/gRPC collector URL (e.g. SigNoz, Jaeger) for span export, in any build [default: unset, no export] - `EXCLUDED_DIRS` is a comma separated list of directories to exclude from the Memories API - `PREVIEW_CLIPS_DIRECTORY` (optional) is a path where generated video preview clips should be stored [default: `preview_clips`] - `WATCH_QUICK_INTERVAL_SECONDS` (optional) is the interval in seconds for quick file scans [default: 60]