You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
synctv/synctv-api-http/src/http/metrics_middleware.rs

65 lines
1.9 KiB
Rust

//! Axum middleware for collecting HTTP request metrics.
feat(logging): add structured transport access logs (#436) ## Summary - add structured HTTP and gRPC access logs with request correlation, status-aware levels, response bytes, and complete body lifecycle timing - route diagnostic and access events through independently configurable text or JSON outputs with non-blocking writers and dropped-line accounting - honor the configured IANA timezone across text, JSON, diagnostic, and access logs with DST-aware RFC 3339 offsets - keep HTTP metric labels bounded while logging concrete unmatched paths without query strings - reduce local development noise by defaulting global logs to info while retaining component debug logs ## Behavior - HTTP 2xx and 3xx complete at info, 4xx at warn, and 5xx or body failures at error - slow handlers are raised to warn; ordinary client response cancellation remains debug - gRPC completion waits for final trailers and records the canonical gRPC status - request IDs are validated, propagated in responses, and included in mapped gRPC errors - header latency and full response lifecycle latency are reported separately - log timestamps use the configured time.timezone and include a numeric UTC offset ## Testing - cargo test -p synctv-api-common transport_access_log --lib - cargo test -p synctv-core logging::tests --lib - cargo test -p synctv resource_options::tests --lib - cargo clippy for affected crates and all targets with warnings denied - cargo check --workspace --all-targets - cargo fmt --all -- --check - git diff --check - local server smoke tests for request completion, request ID propagation, byte counts, query omission, and Asia/Shanghai timestamps in diagnostic and access logs
1 month ago
use axum::{
extract::{MatchedPath, Request},
middleware::Next,
response::Response,
};
use std::time::Instant;
use synctv_api_common::observability::metrics;
/// Middleware that records HTTP request count, duration, and in-flight gauge.
pub async fn metrics_layer(request: Request, next: Next) -> Response {
let method = request.method().to_string();
feat(logging): add structured transport access logs (#436) ## Summary - add structured HTTP and gRPC access logs with request correlation, status-aware levels, response bytes, and complete body lifecycle timing - route diagnostic and access events through independently configurable text or JSON outputs with non-blocking writers and dropped-line accounting - honor the configured IANA timezone across text, JSON, diagnostic, and access logs with DST-aware RFC 3339 offsets - keep HTTP metric labels bounded while logging concrete unmatched paths without query strings - reduce local development noise by defaulting global logs to info while retaining component debug logs ## Behavior - HTTP 2xx and 3xx complete at info, 4xx at warn, and 5xx or body failures at error - slow handlers are raised to warn; ordinary client response cancellation remains debug - gRPC completion waits for final trailers and records the canonical gRPC status - request IDs are validated, propagated in responses, and included in mapped gRPC errors - header latency and full response lifecycle latency are reported separately - log timestamps use the configured time.timezone and include a numeric UTC offset ## Testing - cargo test -p synctv-api-common transport_access_log --lib - cargo test -p synctv-core logging::tests --lib - cargo test -p synctv resource_options::tests --lib - cargo clippy for affected crates and all targets with warnings denied - cargo check --workspace --all-targets - cargo fmt --all -- --check - git diff --check - local server smoke tests for request completion, request ID propagation, byte counts, query omission, and Asia/Shanghai timestamps in diagnostic and access logs
1 month ago
let path = request.extensions().get::<MatchedPath>().map_or_else(
|| "<unmatched>".to_string(),
|path| path.as_str().to_string(),
);
refactor(metrics): centralize registry and lifecycle tracking (#446) ## Summary - split Prometheus definitions into domain-owned modules backed by one registry - eagerly initialize every metric family and fail startup on invalid or duplicate definitions - add reusable RAII guards for gauges and relay durations across cancellation, panic, and retry paths - centralize HTTP, WebSocket, gRPC, and relay recording behind bounded-label helpers - enforce the 65-metric bilingual catalog from registered descriptors - persist completed explicit-message deletion and user-ban moderation steps - update the pinned Rust toolchain and CI jobs to `nightly-2026-08-25` - make the CI Clippy job fail on every Rust warning with `-D warnings` - keep Helm configuration-validation builds visible to avoid silent-run termination - separate Rust caches by artifact type and let Helm reuse codegen artifacts from the Build job - work around the nightly global next-solver memory regression and remove obsolete rustc recursion-limit overrides - preserve workspace Rust flags in Docker builds while applying linker flags to the final binary ## Compatibility - preserve all existing metric names, HELP text, label order, and histogram buckets - keep route-template and bounded status/error labels - return HTTP 500 when Prometheus exposition encoding fails - keep the latest nightly while restoring the previous coherence-only trait-solver behavior ## Nightly memory regression The failed CI jobs were terminated by the runner while compiling `synctv-api-http` (SIGTERM, exit 143). Local peak-RSS measurements for that crate were: | Configuration | Peak RSS | | --- | ---: | | `nightly-2026-08-21` | 6.62 GB | | `nightly-2026-08-25`, global next solver | 10.76 GB | | `nightly-2026-08-25`, `-Znext-solver=coherence` | 6.64 GB | Rust enabled `-Znext-solver=globally` by default on nightly in [rust-lang/rust#160619](https://github.com/rust-lang/rust/pull/160619). The same memory blow-up is tracked in [rust-lang/rust#161748](https://github.com/rust-lang/rust/issues/161748), and the official tracking issue documents `-Znext-solver=coherence` as the temporary opt-out. The workspace now applies that option in `.cargo/config.toml`. The Dockerfile previously set `RUSTFLAGS` for linker options, which overrode the workspace configuration and re-enabled the global solver inside image builds. It now uses `cargo rustc` to pass linker flags only to the final binary, preserving the workspace solver setting for every crate. All rustc `#![recursion_limit = "256"]` attributes were removed. The default limit passes with the coherence-only solver, confirming that a higher recursion limit was unrelated to the CI termination. ## Validation - `make build-workspace` - `make clippy-check` (`--workspace --all-targets -- -D warnings`) - `cargo check --locked -p synctv-api-http` - `cargo check --locked -p synctv-api-common -p synctv-api-grpc` - `cargo test -p synctv-api-common --lib` (655 passed, 165 Docker tests ignored) - `cargo test -p synctv-core metrics --lib` (9 passed) - `make fmt-check` - `docker build --check .` - `make validate-helm` - `actionlint .github/workflows/ci.yml .github/workflows/helm-ci.yml` - `npm run validate` in `docs` (124 pages, 353 links, Astro 0 errors/warnings)
4 weeks ago
let _in_flight = metrics::start_request();
let start = Instant::now();
let response = next.run(request).await;
refactor(metrics): centralize registry and lifecycle tracking (#446) ## Summary - split Prometheus definitions into domain-owned modules backed by one registry - eagerly initialize every metric family and fail startup on invalid or duplicate definitions - add reusable RAII guards for gauges and relay durations across cancellation, panic, and retry paths - centralize HTTP, WebSocket, gRPC, and relay recording behind bounded-label helpers - enforce the 65-metric bilingual catalog from registered descriptors - persist completed explicit-message deletion and user-ban moderation steps - update the pinned Rust toolchain and CI jobs to `nightly-2026-08-25` - make the CI Clippy job fail on every Rust warning with `-D warnings` - keep Helm configuration-validation builds visible to avoid silent-run termination - separate Rust caches by artifact type and let Helm reuse codegen artifacts from the Build job - work around the nightly global next-solver memory regression and remove obsolete rustc recursion-limit overrides - preserve workspace Rust flags in Docker builds while applying linker flags to the final binary ## Compatibility - preserve all existing metric names, HELP text, label order, and histogram buckets - keep route-template and bounded status/error labels - return HTTP 500 when Prometheus exposition encoding fails - keep the latest nightly while restoring the previous coherence-only trait-solver behavior ## Nightly memory regression The failed CI jobs were terminated by the runner while compiling `synctv-api-http` (SIGTERM, exit 143). Local peak-RSS measurements for that crate were: | Configuration | Peak RSS | | --- | ---: | | `nightly-2026-08-21` | 6.62 GB | | `nightly-2026-08-25`, global next solver | 10.76 GB | | `nightly-2026-08-25`, `-Znext-solver=coherence` | 6.64 GB | Rust enabled `-Znext-solver=globally` by default on nightly in [rust-lang/rust#160619](https://github.com/rust-lang/rust/pull/160619). The same memory blow-up is tracked in [rust-lang/rust#161748](https://github.com/rust-lang/rust/issues/161748), and the official tracking issue documents `-Znext-solver=coherence` as the temporary opt-out. The workspace now applies that option in `.cargo/config.toml`. The Dockerfile previously set `RUSTFLAGS` for linker options, which overrode the workspace configuration and re-enabled the global solver inside image builds. It now uses `cargo rustc` to pass linker flags only to the final binary, preserving the workspace solver setting for every crate. All rustc `#![recursion_limit = "256"]` attributes were removed. The default limit passes with the coherence-only solver, confirming that a higher recursion limit was unrelated to the CI termination. ## Validation - `make build-workspace` - `make clippy-check` (`--workspace --all-targets -- -D warnings`) - `cargo check --locked -p synctv-api-http` - `cargo check --locked -p synctv-api-common -p synctv-api-grpc` - `cargo test -p synctv-api-common --lib` (655 passed, 165 Docker tests ignored) - `cargo test -p synctv-core metrics --lib` (9 passed) - `make fmt-check` - `docker build --check .` - `make validate-helm` - `actionlint .github/workflows/ci.yml .github/workflows/helm-ci.yml` - `npm run validate` in `docs` (124 pages, 353 links, Astro 0 errors/warnings)
4 weeks ago
metrics::record_request(&method, &path, response.status().as_u16(), start.elapsed());
response
}
feat(logging): add structured transport access logs (#436) ## Summary - add structured HTTP and gRPC access logs with request correlation, status-aware levels, response bytes, and complete body lifecycle timing - route diagnostic and access events through independently configurable text or JSON outputs with non-blocking writers and dropped-line accounting - honor the configured IANA timezone across text, JSON, diagnostic, and access logs with DST-aware RFC 3339 offsets - keep HTTP metric labels bounded while logging concrete unmatched paths without query strings - reduce local development noise by defaulting global logs to info while retaining component debug logs ## Behavior - HTTP 2xx and 3xx complete at info, 4xx at warn, and 5xx or body failures at error - slow handlers are raised to warn; ordinary client response cancellation remains debug - gRPC completion waits for final trailers and records the canonical gRPC status - request IDs are validated, propagated in responses, and included in mapped gRPC errors - header latency and full response lifecycle latency are reported separately - log timestamps use the configured time.timezone and include a numeric UTC offset ## Testing - cargo test -p synctv-api-common transport_access_log --lib - cargo test -p synctv-core logging::tests --lib - cargo test -p synctv resource_options::tests --lib - cargo clippy for affected crates and all targets with warnings denied - cargo check --workspace --all-targets - cargo fmt --all -- --check - git diff --check - local server smoke tests for request completion, request ID propagation, byte counts, query omission, and Asia/Shanghai timestamps in diagnostic and access logs
1 month ago
#[cfg(test)]
mod tests {
use axum::{
body::Body,
http::{Request, StatusCode},
routing::get,
Router,
};
use tower::ServiceExt;
#[tokio::test]
async fn metrics_use_matched_route_without_resource_ids() {
let app = Router::new()
.route("/items/{item_id}", get(|| async { StatusCode::OK }))
.layer(axum::middleware::from_fn(super::metrics_layer));
let response = app
.oneshot(
Request::builder()
.uri("/items/private-resource-id")
.body(Body::empty())
.expect("request should build"),
)
.await
.expect("request should complete");
assert_eq!(response.status(), StatusCode::OK);
refactor(metrics): centralize registry and lifecycle tracking (#446) ## Summary - split Prometheus definitions into domain-owned modules backed by one registry - eagerly initialize every metric family and fail startup on invalid or duplicate definitions - add reusable RAII guards for gauges and relay durations across cancellation, panic, and retry paths - centralize HTTP, WebSocket, gRPC, and relay recording behind bounded-label helpers - enforce the 65-metric bilingual catalog from registered descriptors - persist completed explicit-message deletion and user-ban moderation steps - update the pinned Rust toolchain and CI jobs to `nightly-2026-08-25` - make the CI Clippy job fail on every Rust warning with `-D warnings` - keep Helm configuration-validation builds visible to avoid silent-run termination - separate Rust caches by artifact type and let Helm reuse codegen artifacts from the Build job - work around the nightly global next-solver memory regression and remove obsolete rustc recursion-limit overrides - preserve workspace Rust flags in Docker builds while applying linker flags to the final binary ## Compatibility - preserve all existing metric names, HELP text, label order, and histogram buckets - keep route-template and bounded status/error labels - return HTTP 500 when Prometheus exposition encoding fails - keep the latest nightly while restoring the previous coherence-only trait-solver behavior ## Nightly memory regression The failed CI jobs were terminated by the runner while compiling `synctv-api-http` (SIGTERM, exit 143). Local peak-RSS measurements for that crate were: | Configuration | Peak RSS | | --- | ---: | | `nightly-2026-08-21` | 6.62 GB | | `nightly-2026-08-25`, global next solver | 10.76 GB | | `nightly-2026-08-25`, `-Znext-solver=coherence` | 6.64 GB | Rust enabled `-Znext-solver=globally` by default on nightly in [rust-lang/rust#160619](https://github.com/rust-lang/rust/pull/160619). The same memory blow-up is tracked in [rust-lang/rust#161748](https://github.com/rust-lang/rust/issues/161748), and the official tracking issue documents `-Znext-solver=coherence` as the temporary opt-out. The workspace now applies that option in `.cargo/config.toml`. The Dockerfile previously set `RUSTFLAGS` for linker options, which overrode the workspace configuration and re-enabled the global solver inside image builds. It now uses `cargo rustc` to pass linker flags only to the final binary, preserving the workspace solver setting for every crate. All rustc `#![recursion_limit = "256"]` attributes were removed. The default limit passes with the coherence-only solver, confirming that a higher recursion limit was unrelated to the CI termination. ## Validation - `make build-workspace` - `make clippy-check` (`--workspace --all-targets -- -D warnings`) - `cargo check --locked -p synctv-api-http` - `cargo check --locked -p synctv-api-common -p synctv-api-grpc` - `cargo test -p synctv-api-common --lib` (655 passed, 165 Docker tests ignored) - `cargo test -p synctv-core metrics --lib` (9 passed) - `make fmt-check` - `docker build --check .` - `make validate-helm` - `actionlint .github/workflows/ci.yml .github/workflows/helm-ci.yml` - `npm run validate` in `docs` (124 pages, 353 links, Astro 0 errors/warnings)
4 weeks ago
let output = synctv_api_common::observability::metrics::gather_metrics()
.expect("metrics should encode");
feat(logging): add structured transport access logs (#436) ## Summary - add structured HTTP and gRPC access logs with request correlation, status-aware levels, response bytes, and complete body lifecycle timing - route diagnostic and access events through independently configurable text or JSON outputs with non-blocking writers and dropped-line accounting - honor the configured IANA timezone across text, JSON, diagnostic, and access logs with DST-aware RFC 3339 offsets - keep HTTP metric labels bounded while logging concrete unmatched paths without query strings - reduce local development noise by defaulting global logs to info while retaining component debug logs ## Behavior - HTTP 2xx and 3xx complete at info, 4xx at warn, and 5xx or body failures at error - slow handlers are raised to warn; ordinary client response cancellation remains debug - gRPC completion waits for final trailers and records the canonical gRPC status - request IDs are validated, propagated in responses, and included in mapped gRPC errors - header latency and full response lifecycle latency are reported separately - log timestamps use the configured time.timezone and include a numeric UTC offset ## Testing - cargo test -p synctv-api-common transport_access_log --lib - cargo test -p synctv-core logging::tests --lib - cargo test -p synctv resource_options::tests --lib - cargo clippy for affected crates and all targets with warnings denied - cargo check --workspace --all-targets - cargo fmt --all -- --check - git diff --check - local server smoke tests for request completion, request ID propagation, byte counts, query omission, and Asia/Shanghai timestamps in diagnostic and access logs
1 month ago
assert!(output.contains(
"http_requests_total{method=\"GET\",path=\"/items/{item_id}\",status=\"200\"}"
));
assert!(!output.contains("private-resource-id"));
}
}