feat(14.2-01): opt-in http.access_log request log in surf
- one Info record "http request" per request with method, matched route pattern, status and duration_ms; path without the query only when no route matched - the matched route records its pattern through a request-scoped entry, so locale prefix rewrites do not hide it - logs through the published *slog.Logger or slog.Default; off by default - surf README, logging and configuration docs describe the key
This commit is contained in:
@@ -45,6 +45,27 @@ A plugin resolves a `*slog.Logger` from the application with `backpack.Lookup` a
|
||||
|
||||
Redaction is a safety net for mistakes, not a way to log secrets on purpose. Log identifiers and outcomes: the record id, the vendor, the status code, how long it took. Never log request arguments, tokens, credentials or request and response bodies, which can carry personal data the patterns do not recognise.
|
||||
|
||||
## Request log
|
||||
|
||||
The router can log every request. It is off by default; turn it on with the `http.access_log` key (environment `SUMMER_HTTP__ACCESS_LOG=true`):
|
||||
|
||||
```yaml
|
||||
# config/http.yaml
|
||||
access_log: true
|
||||
```
|
||||
|
||||
Each request then writes one Info record with the message `http request` once it has been served:
|
||||
|
||||
| Attribute | Value |
|
||||
|-----------|-------|
|
||||
| `method` | The request method. |
|
||||
| `pattern` | The matched route, method and path pattern, for example `GET /posts/{id}`. Empty when no route matched. |
|
||||
| `status` | The response status; a handler that writes without setting one counts as 200. |
|
||||
| `duration_ms` | Time spent serving the request, in whole milliseconds. |
|
||||
| `path` | Only when no route matched (a 404 or 405 from the router, or a CORS preflight answered before routing): the URL path without the query string. |
|
||||
|
||||
The record never carries the query string, request or response headers, the client address or the raw path of a matched route, because tokens often travel in paths and query strings. It goes to the published `*slog.Logger` or `slog.Default`, so the redacting handler still applies. Grep for the empty `pattern` to find requests that reached no route.
|
||||
|
||||
## Panics
|
||||
|
||||
The router recovers a panicking handler and answers with an opaque 500: the JSON groups send `{"error":true,"message":"Internal server error"}` and the raw groups an empty body. The panic value never reaches the client. See [Routing](routing.md).
|
||||
|
||||
@@ -53,6 +53,7 @@ These are the keys most applications set. Each module's reference page lists all
|
||||
| `app.locale`, `app.fallback_locale` | Default and fallback translation locales. | [phrasebook](../../modules/phrasebook/README.md) |
|
||||
| `http.body_limits.default_bytes`, `http.body_limits.upload_bytes` | Request body limits. Required by `serve` and `route:list`. | [surf](../../modules/surf/README.md) |
|
||||
| `http.cors.*`, `http.trusted_proxies` | CORS and the proxies whose forwarded client IP is trusted. | [surf](../../modules/surf/README.md) |
|
||||
| `http.access_log` | Logs one record per request with method, matched route pattern, status and duration. Defaults to `false`. | [surf](../../modules/surf/README.md) |
|
||||
| `storage.uploads.bucket_url` | Uploads bucket, `file://` or `mem://`. Required by `serve`. | [lagoon](../../modules/lagoon/README.md) |
|
||||
| `storage.uploads.serve` | Mounts `attach.StaticHandlerPublic` under `storage.uploads.public_path_prefix` with `X-Content-Type-Options: nosniff`. Defaults to `false`. | [surf](../../modules/surf/README.md) |
|
||||
| `admin.jwt.secret` | Secret for admin tokens. Required as soon as a plugin registers an admin controller. | [cabana](../../modules/cabana/README.md) |
|
||||
|
||||
Reference in New Issue
Block a user