Skip to main content

Module observability

Module observability 

Source
Expand description

Request ids, access logs and Prometheus metrics

use ullun::observability::{AccessLog, Metrics, RequestIdLayer};
use ullun::prelude::*;

let metrics = Metrics::new();
App::new()
    .middleware(RequestIdLayer::new())      // first: the others read the id
    .middleware(AccessLog::new())           // one line per request
    .middleware(metrics.clone())            // counters and latency histograms
    .get("/metrics", metrics.handler())     // Prometheus text format
    .get("/users/{id}", |req: Request| async move {
        // requests of this route are logged and counted as `/users/{id}`,
        // not once per id
        Ok(Response::text(req.request_id().unwrap_or("-").to_string()))
    })
    .run("127.0.0.1:3000")
    .await
    .unwrap();

Metric labels use Request::matched_path, the route pattern, so the number of series is bounded by your routes, not by the ids or by whatever paths scanners probe (all unmatched requests share route="unmatched"). Do not expose /metrics publicly without authentication or a network boundary.

Structsยง

AccessLog
One log line per request, written when the response is ready
LogEntry
One finished request, as handed to the log sink
Metrics
Request counters, latency histograms and an in-flight gauge, rendered in the Prometheus text format. Clone it: all clones share the numbers.
RequestId
The id of the current request, stored in Request::extensions
RequestIdLayer
Assigns every request an id (or keeps a sane one sent by the caller), exposes it as Request::request_id and echoes it in a response header