Skip to content

DocsReferenceObservability

On this page

Observability

Three edge-safe primitives, built on the existing middleware + hook system (no dependency):

  1. Trace id per request (traceId()) — propagated or generated, readable in loaders/actions.
  2. Structured request logs (requestLogging()) — method, path, status, duration, trace id.
  3. Metrics (createMetrics()) — counters/latencies from app hooks, exposed as JSON.

Wiring

ts
// src/app.ts
import { createMetrics, createMetricsRoute, requestLogging, traceId } from "@ramonyx/server";
import { mergeHooks } from "ramonyx";

const metrics = createMetrics();

const handler = createHandler({
  routes: [
    ...apiRoutes,
    createMetricsRoute(metrics),   // GET /metrics
    ...createPageRoutes({ pages, renderer, hooks: mergeHooks(appHooks, metrics.hooks) }),
  ],
  middleware: [traceId(), requestLogging()],
  hooks: mergeHooks(appHooks, metrics.hooks),
});

Order matters: traceId() must run before requestLogging() so the log line carries the id.

Trace id

  • Uses the incoming x-request-id header when present (proxy/client propagation), otherwise generates one (UUID, with a getRandomValues fallback).
  • Rewrites the request with the header set, so loaders/actions can read it; sets the same header on the response.
ts
// inside a loader
const id = request.headers.get("x-request-id");

Custom header name: traceId({ header: "x-trace-id" }).

Structured logs

ts
requestLogging();                    // message "request"
requestLogging({ message: "http" }); // custom message

requestLogging() emits one info line per request through the app logger:

json
{"level":"info","message":"request","method":"GET","path":"/","status":200,"ms":12,"traceId":"…"}

Ship it to any collector (Fluent Bit, Vector, OTLP log exporter). Secrets/PII must not be logged — the framework only emits method/path/status/duration/trace id.

Metrics

createMetrics() returns { hooks, snapshot() }:

json
{
  "requests": { "total": 120, "byStatusClass": { "2xx": 118, "5xx": 2 },
                "durationMs": { "sum": 1840, "count": 120, "max": 210 } },
  "loaders": { "count": 80, "cached": 42, "durationMs": { "sum": 620, "max": 90 } },
  "renders": { "count": 118, "streaming": 110, "durationMs": { "sum": 980, "max": 140 } },
  "invalidations": 3
}
  • createMetricsRoute(metrics, { path: "/metrics" }) serves the snapshot as JSON.
  • Schedule metrics.snapshot() exports to OTLP/Prometheus from your platform, or pull /metrics.
  • Counters are cumulative since process start (per instance).

Notes & limitations

  • traceId is a correlation id, not a full W3C traceparent; native OpenTelemetry spans are not built in (candidate if there is demand).
  • Metrics are process-local: multi-instance deployments should scrape each instance or aggregate in your platform.
  • The hooks are zero-cost when unused; the helpers are edge-safe (no node:*).
Edit this page on GitHub ↗