Performance Monitoring
Log every API request's method, path, status, duration, and cache outcome via a Hono timing middleware, gated by config.performanceMonitor.
A Hono middleware (packages/api/src/middleware/performance.ts) times every request and logs one line through the @repo/runtime logger, gated by config.performanceMonitor.enabled in packages/config/src/index.ts. When enabled it wraps the whole app as the first middleware; when off it is never registered, so requests are not logged at the API layer.
What you get
- One log line per request:
METHOD /path STATUS durationMs <cache-tag>. - Wall-clock duration measured with
performance.now()(rounded to ms). - A cache tag showing how the response was served:
| Tag | Meaning |
|---|---|
| (empty) | Served fresh (handler ran). |
cached | Returned from the Redis response cache (c.set("cached", true)). |
etag | 304 Not Modified - client revalidated, no body sent. |
Configuration
config.performanceMonitor is { enabled: boolean }. The middleware registers when the value is not false, so a missing key still logs.
performanceMonitor: {
enabled: true
}| Key | Type | Default | Description |
|---|---|---|---|
enabled | boolean | true | When false, the middleware is not mounted and the API logs no request lines. |
The fullstack target mounts the API inside a host framework that already logs requests natively, so the CLI writes enabled: false there to avoid double-logging. Standalone backend targets default to true.
How it works
The middleware is the first app.use in packages/api/src/index.ts, registered only when the flag is on:
if (config.performanceMonitor?.enabled !== false) {
app.use(performanceMonitor);
}It records start before await next(), computes the duration after, then logs. Because it wraps everything, the duration includes all downstream middleware (CORS, rate limiting, cache, route handler).
Reading the logs
Output goes to the @repo/runtime logger (consola), which writes to stdout - your local terminal in dev, your host's log stream in production. There are no environment variables for this feature; verbosity follows the logger's level, which is higher in development (NODE_ENV).
GET /api/posts 200 12ms cached
POST /api/posts 201 84ms
GET /api/posts 304 3ms etagFrequently asked questions
Does this add an /metrics endpoint or a dashboard?
No. It is a structured log line per request, not a metrics scrape target or UI. Pipe stdout to your platform's log aggregator.
Why is the cache tag empty on most requests?
The tag only appears when the response was a cache hit (cached) or a revalidation (etag). Fresh handler responses log no tag. See Caching.
Will it log requests the rate limiter rejects?
Yes - it wraps the entire app, so a 429 is timed and logged like any other response.