Skip to content
Docs

Command palette

Jump to a page or view, or find a todo by name.

Changelog

Observability and tracing

Read structured errors, durable audit records and Cloudflare spans as separate signals.

Markdown

Application errors and logs

The web middleware integrates evlog. Shared error codes and localized messages come from packages/errors; its server handler logs unexpected failures while the client resolver returns a safe user message. During development, inspect .evlog/logs/ when available. Do not copy credentials, prompts or private payloads into logs.

Each RPC request has a request ID: cf-ray when Cloudflare set a well-formed one, otherwise a UUID. It is context.requestId on the server, the x-request-id header on every /api/rpc response (including the 500 when building the context fails), and a field of the error log. The MCP context derives its own requestId for the logs of its document tools; MCP responses do not carry the header. It serves only to correlate logs, never to decide anything: outside Cloudflare (local wrangler dev, Node) a client can send a well-formed cf-ray and it is used as is, and Cloudflare's header reference does not say whether a client-sent value is replaced. The error handler writes expected 4xx failures at debug level and unexpected ones (5xx, unknown codes, non-oRPC errors) as errors, both with the request ID, so a user's quoted header finds the matching line (error handling).

An Audit Event is a durable domain record, not a log line. The Audit Log is the admin table; an Activity Timeline is one User's security history. Release notes belong to the public changelog.

Workers Logs and sampling

Workers Logs keeps what the Worker writes for seven days: one invocation event per request plus one event per console line (for example the evlog error handler's). observability.logs.head_sampling_rate in apps/web/wrangler.jsonc decides which share of requests is kept, and it is set explicitly to 1 (every request).

Sampling is decided per request before the Worker runs. An unsampled request keeps none of its lines, errors included: at 0.1, nine of ten failures leave no trace, and a failure that happens once a day may never show up. Lower it only when the log bill matters more than that, and watch errors through the Worker's metrics (requests and errors per status in the dashboard), which this setting does not sample. Traces have their own rate (observability.traces.head_sampling_rate, default 1); changing the logs rate does not change it.

What the logs cost on Workers Paid until 30 November 2026 (20 million events a month included, then US$ 0.60 per million), with one event per request and 15 ms of CPU per server render (an estimate from local workerd; every extra log line per request adds the logs column again). Prices read on 2026-11-05; the comparison is in the 2026-11-06 measurement.

SSR requests / monthWorkers total (base + requests + CPU + logs)Logs at rate 1Logs at rate 0.1
10 MUS$ 7US$ 0US$ 0
50 MUS$ 49US$ 18US$ 0
100 MUS$ 109US$ 48US$ 0
500 MUS$ 589US$ 288US$ 18

From 1 December 2026 Workers Logs moves to Cloudflare Observability pricing: on Workers Paid, 50 GB of ingestion and 10 GB-month of storage per billing cycle are included, then US$ 0.25 per GB ingested and US$ 0.10 per GB-month stored. Sampling reduces both. Measure the bytes per event in the dashboard before choosing a rate under that model.

Web Vitals

Real visits report their Core Web Vitals (LCP, INP, CLS, FCP, TTFB) when the visitor allows analytics in the cookie banner; without that consent nothing is observed or sent. useWebVitals (apps/web/src/platform/analytics/web-vitals.ts) starts after hydration, so it adds nothing before the first paint (W15), and sends one navigator.sendBeacon when the page is hidden or left. POST /api/events/web-vitals validates it with Zod (web-vital-event.ts), refuses bodies over 2 KB, asks the WEB_VITALS_RATE_LIMITER binding (30 requests a minute per client address and Cloudflare location; previews have no binding and do not limit) and writes one line to Workers Logs:

{
  "event": "web_vital",
  "route": "/docs/$",
  "locale": "en",
  "navigationType": "navigate",
  "effectiveType": "4g",
  "LCP": 1180,
  "FCP": 640,
  "TTFB": 210,
  "CLS": 0.01,
  "INP": 72
}

route is the route pattern the document loaded on (/{-$locale}/, /docs/$, /todos), never the URL, so no id or query string is kept. Nothing identifies the visitor: no cookie, session, user, organization or address is logged (the address is the rate limiter's key and is not written anywhere). A metric that is not known yet when the page is hidden (INP without an interaction) is left out.

In the dashboard's Query Builder, filter event = web_vital and compute the 75th percentile of LCP, INP or CLS grouped by route; compare TTFB by effectiveType to see what the network adds. The endpoint is the first event of the planned analytics relay (POST /api/events): the relay takes webVitalEvent as its web_vital event and adds provider adapters beside the log line.

Worker traces

Wrangler enables observability and traces. The scheduled handler passes ctx.tracing into runScheduledJobs; workersTracer adapts Effect spans to Cloudflare spans. It records scalar attributes, failures and interruption.

The native API cannot assign an explicit parent to each Effect span. Those spans appear as siblings under jobs.scheduled, alongside platform fetch and Hyperdrive spans. Node execution runs without this tracer. Local successful jobs do not prove that production custom spans are visible in the Cloudflare dashboard.

Diagnose a failure

Trace the entry point, transport result and durable row together. For an import, inspect its state and persisted failure reason before assuming the queue lost it. For a webhook, inspect the delivery lease and next attempt. For a client error, keep the stable error code and correlation context rather than exposing an exception message.

Bundle and fault experiments are recorded in docs/measurements. Their local timings are not production latency or billing measurements. Add a dated report when changing runtime ownership, deadlines or a heavy dependency.

Key files

Edit on GitHubDocumentation is in English.
Cookie settings
We use essential cookies to make our app work. With your consent, we also use optional cookies to improve your experience.

Read our Cookie Policy and Privacy Policy.