From 9e7c93e14901bb8bafe5be9ce3546d578cfc5743 Mon Sep 17 00:00:00 2001 From: rays Date: Tue, 29 Sep 2026 13:33:11 +0000 Subject: [PATCH] Name request traces by route, and no colour codes off a terminal (#87, #88) A request's trace was named by its path, so every item's GUID in POST /api/entries/{feed_id}/{guid}/flags made a trace name of its own and nothing grouped in Tempo. A route layer now renames it once routing has matched. It renames the OpenTelemetry span directly: tracing-opentelemetry drops a recorded otel.name once the span has been entered, and access_log enters it before routing runs. tracing-subscriber's fmt layer writes ANSI colour by default, so docker logs and Loki (through Alloy) carried escape codes on every line, which each query had to strip. Colour is now for a terminal only. Co-Authored-By: Claude Opus 5.5 --- CHANGELOG.md | 1 + src/main.rs | 3 +++ src/web.rs | 17 ++++++++++++++++- 3 files changed, 20 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4064b5c..e8960b9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -39,6 +39,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed +- The log has no terminal colour codes when it is not going to a terminal, as in `docker logs`. - A feed whose website names an icon that is missing shows the site's `/favicon.ico` instead of no artwork. - A website's icon standing in for a feed's artwork follows the site when it changes, and a diff --git a/src/main.rs b/src/main.rs index 236e9be..6511acb 100644 --- a/src/main.rs +++ b/src/main.rs @@ -191,6 +191,9 @@ async fn main() -> Result<()> { .with( tracing_subscriber::fmt::layer() .with_writer(std::io::stderr) + // Colour for a terminal only: in docker logs and Loki the escapes are noise + // every query has to strip (#88). + .with_ansi(std::io::IsTerminal::is_terminal(&std::io::stderr())) .with_filter(stderr_filter), ) .with(logbuf::RingLayer.with_filter(ui_filter)) diff --git a/src/web.rs b/src/web.rs index 4d0bd1b..b2729da 100644 --- a/src/web.rs +++ b/src/web.rs @@ -4,7 +4,7 @@ use anyhow::{Context, Result}; use axum::http::HeaderMap; use axum::{ Json, Router, - extract::{Path, Query, Request, State}, + extract::{MatchedPath, Path, Query, Request, State}, http::{StatusCode, header}, middleware::{self, Next}, response::{ @@ -79,6 +79,7 @@ pub fn router(state: WebState) -> Router { .route("/app.css", get(app_css)) .route("/login.js", get(login_js)) .route("/inter.woff2", get(inter)) + .route_layer(middleware::from_fn(name_span)) .layer(middleware::from_fn(access_log)) .with_state(state) } @@ -1930,6 +1931,19 @@ async fn logs(user: crate::db::User, Query(q): Query) -> Result Response { + use opentelemetry::trace::TraceContextExt; + use tracing_opentelemetry::OpenTelemetrySpanExt; + let span = tracing::Span::current(); + span.context().span().update_name(format!("{} {}", req.method(), route.as_str())); + span.record("http.route", route.as_str()); + next.run(req).await +} + /// One line per HTTP request, so the web side shows up in the same log as the daemon. /// /// The log view polls `/api/logs`, so logging that path would generate a line per poll @@ -1949,6 +1963,7 @@ async fn access_log(req: Request, next: Next) -> Response { otel.name = %format!("{method} {path}"), http.request.method = %method, url.path = %path, + http.route = tracing::field::Empty, http.response.status_code = tracing::field::Empty, ) };