Logs were a separate pipeline: env_logger in debug (never exported) and opentelemetry-appender-log in release (exported without trace context, so SigNoz could not link logs to traces). - tracing-subscriber `tracing-log` feature: try_init installs LogTracer, so log:: records become tracing events: one console format, and RUST_LOG per-module directives still apply to them. - opentelemetry-appender-tracing exports events as OTel log records when OTLP_OTLS_ENDPOINT is set (any build), stamped with the enclosing span's trace_id/span_id. A per-layer filter drops the exporter's own crates. - The OTel span layer keeps only WARN/ERROR events as span events; the info/debug copies duplicated the correlated logs. - Remove env_logger init in the server, init_logs, and the unused opentelemetry-appender-log / opentelemetry-stdout deps. - tests/otel_log_correlation.rs pins correlation (native + bridged), the span-event filter, and per-module filtering of bridged records. - Constitution 1.2.0 (Principle V logging rule), CLAUDE.md, README, .env.example. Closes #147 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
182 lines
8.0 KiB
Markdown
182 lines
8.0 KiB
Markdown
<!--
|
|
Sync Impact Report
|
|
==================
|
|
Version change: 1.1.0 -> 1.2.0
|
|
Bump rationale: MINOR. Principle V's logging rule is materially changed:
|
|
logs are no longer a separate, build-profile-dependent pipeline; they
|
|
share the tracing subscriber and export correlated with traces in any
|
|
build (#147). No principle is removed or redefined in intent.
|
|
Modified principles:
|
|
- V. Observability: logging bullet rewritten; span events limited to
|
|
WARN/ERROR
|
|
Also updated:
|
|
- Technology Stack & Constraints: Tracing line
|
|
Added sections: N/A
|
|
Removed sections: N/A
|
|
Templates requiring updates:
|
|
- .specify/templates/plan-template.md — ✅ no changes needed (generic Constitution Check)
|
|
- .specify/templates/spec-template.md — ✅ no changes needed
|
|
- .specify/templates/tasks-template.md — ✅ no changes needed
|
|
- .specify/templates/checklist-template.md — ✅ no changes needed
|
|
- .specify/templates/agent-file-template.md — ✅ no changes needed
|
|
Runtime guidance:
|
|
- CLAUDE.md "Tracing" — ✅ logs bullet added; dependency list updated
|
|
- README.md — ✅ RUST_LOG default + OTLP_OTLS_ENDPOINT now cover logs
|
|
- .env.example — ✅ telemetry header covers logs
|
|
Follow-up TODOs: migrate `log::` macros to `tracing::` (bridged records
|
|
export with target "log"; module survives only in code.namespace)
|
|
-->
|
|
|
|
# ImageApi Constitution
|
|
|
|
## Core Principles
|
|
|
|
### I. Layered Architecture
|
|
|
|
All features MUST follow the established layered architecture:
|
|
|
|
- **HTTP Layer** (`main.rs`, feature modules): Route handlers, request
|
|
parsing, response formatting. No direct database access.
|
|
- **Service Layer** (`files.rs`, `exif.rs`, `memories.rs`, etc.): Business
|
|
logic. No HTTP-specific types.
|
|
- **DAO Layer** (`database/` trait definitions): Trait-based data access
|
|
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. 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
|
|
work (video processing, file watching) but MUST interact with the
|
|
DAO layer through the established trait interfaces.
|
|
|
|
### II. Path Safety (NON-NEGOTIABLE)
|
|
|
|
All user-supplied file paths MUST be validated against `BASE_PATH`
|
|
using `is_valid_full_path()` before any filesystem operation. This
|
|
prevents directory traversal attacks.
|
|
|
|
- Paths stored in the database MUST be relative to `BASE_PATH`.
|
|
- Paths passed to external tools (ffmpeg, image processing) MUST be
|
|
fully resolved absolute paths.
|
|
- Extension detection MUST use the centralized helpers in
|
|
`file_types.rs` (case-insensitive). Manual string matching on
|
|
extensions is prohibited.
|
|
|
|
### III. Trait-Based Testability
|
|
|
|
All data access MUST go through trait-based DAOs so that every
|
|
handler and service can be tested with mock implementations.
|
|
|
|
- Each DAO trait MUST be defined in `src/database/` and require
|
|
`Sync + Send`.
|
|
- Mock DAOs for testing MUST live in `src/testhelpers.rs`.
|
|
- Integration tests against real SQLite MUST use in-memory databases
|
|
via `in_memory_db_connection()` from `database::test`.
|
|
- Handler tests MUST use `actix_web::test` utilities with JWT token
|
|
injection (using `Claims::valid_user()` and the `test_key` secret).
|
|
- New DAO implementations MUST include a `#[cfg(test)]` constructor
|
|
(e.g., `from_connection`) accepting an injected connection.
|
|
|
|
### IV. Environment-Driven Configuration
|
|
|
|
Server behavior MUST be controlled through environment variables
|
|
loaded from `.env` files. Hard-coded paths, URLs, or secrets are
|
|
prohibited.
|
|
|
|
- Required variables MUST call `.expect()` with a clear message at
|
|
startup so misconfiguration fails fast.
|
|
- Optional variables MUST use `.unwrap_or_else()` with sensible
|
|
defaults and be documented in `README.md`.
|
|
- Any new environment variable MUST be added to the README
|
|
environment section before the feature is considered complete.
|
|
|
|
### V. Observability
|
|
|
|
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`.
|
|
|
|
- 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 share the tracing subscriber: `log::` records are bridged into
|
|
`tracing`, so both print in one console format. With
|
|
`OTLP_OTLS_ENDPOINT` set (any build), events export as OTel log records
|
|
carrying the enclosing span's trace_id / span_id. Only WARN and ERROR
|
|
events are also recorded on spans as span events.
|
|
- 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)
|
|
- **HTTP Framework**: Actix-web 4
|
|
- **ORM**: Diesel 2.2 with SQLite backend
|
|
- **Auth**: JWT (HS256) via `jsonwebtoken` crate, bcrypt password
|
|
hashing
|
|
- **Video Processing**: ffmpeg/ffprobe (CLI, must be on PATH)
|
|
- **Image Processing**: `image` crate for thumbnails, `kamadak-exif`
|
|
for EXIF extraction
|
|
- **Tracing**: `tracing` + `tracing-opentelemetry` (every build), OTLP
|
|
export when `OTLP_OTLS_ENDPOINT` is set; logs through the same
|
|
subscriber via `opentelemetry-appender-tracing` (`log::` bridged)
|
|
- **Testing**: `cargo test`, `actix_web::test`, in-memory SQLite
|
|
|
|
External dependencies (ffmpeg, Ollama) are optional runtime
|
|
requirements. The server MUST start and serve core functionality
|
|
(images, thumbnails, tags) without them. Features that depend on
|
|
optional services MUST degrade gracefully with logged warnings,
|
|
not panics.
|
|
|
|
## Development Workflow
|
|
|
|
- `cargo fmt` MUST pass before committing.
|
|
- `cargo clippy` warnings MUST be resolved or explicitly suppressed
|
|
with a justification comment.
|
|
- `cargo test` MUST pass with all tests green before merging to
|
|
master.
|
|
- Database schema changes MUST use Diesel migrations
|
|
(`diesel migration generate`), with hand-written SQL in `up.sql`
|
|
and `down.sql`, followed by `diesel print-schema` to regenerate
|
|
`schema.rs`.
|
|
- Features MUST be developed on named branches
|
|
(`###-feature-name`) and merged to master via pull request.
|
|
- File uploads MUST preserve existing files (append timestamp on
|
|
conflict, never overwrite).
|
|
|
|
## Governance
|
|
|
|
This constitution defines the non-negotiable architectural and
|
|
development standards for the ImageApi project. All code changes
|
|
MUST comply with these principles.
|
|
|
|
- **Amendments**: Any change to this constitution MUST be documented
|
|
with a version bump, rationale, and updated Sync Impact Report.
|
|
- **Versioning**: MAJOR for principle removals/redefinitions, MINOR
|
|
for new principles or material expansions, PATCH for wording
|
|
clarifications.
|
|
- **Compliance**: Pull request reviews SHOULD verify adherence to
|
|
these principles. The CLAUDE.md file provides runtime development
|
|
guidance and MUST remain consistent with this constitution.
|
|
|
|
**Version**: 1.2.0 | **Ratified**: 2026-02-26 | **Last Amended**: 2026-09-29
|