|
| 1 | +# Server Logging Conventions |
| 2 | + |
| 3 | +## Quick rule |
| 4 | + |
| 5 | +- **New production diagnostics in `server/routes/`** → use `logger.event()` via `getRequestLogger` |
| 6 | +- **Ad-hoc debugging anywhere** → `logger.debug()` is fine |
| 7 | +- **Legacy code outside converted routes** → `logger.warn/error` remain; migrate opportunistically |
| 8 | + |
| 9 | +--- |
| 10 | + |
| 11 | +## Why typed events? |
| 12 | + |
| 13 | +`logger.warn("something failed", { ... })` produces unstructured Axiom rows that are hard to query. |
| 14 | +`logger.event("tunnel.created", ...)` produces a row you can filter by `event`, `workspaceId`, `orgId`, `route`, etc. without grepping prose. |
| 15 | + |
| 16 | +--- |
| 17 | + |
| 18 | +## How to emit a typed event from a route handler |
| 19 | + |
| 20 | +```ts |
| 21 | +import { getRequestLogger } from "../../utils/request-logger.js"; |
| 22 | +import { classifyError } from "../../utils/error-classify.js"; |
| 23 | + |
| 24 | +// inside a Hono handler that has `c: Context` |
| 25 | +getRequestLogger(c, "routes.web.tools").event("mcp.tool.execution.failed", { |
| 26 | + toolName: body.toolName, |
| 27 | + serverId: body.serverId, |
| 28 | + errorCode: classifyError(error), |
| 29 | +}); |
| 30 | +``` |
| 31 | + |
| 32 | +The request context (`workspaceId`, `orgId`, `workspaceRole`, etc.) is automatically |
| 33 | +picked up from `c.var.requestLogContext`, which is populated by: |
| 34 | +1. `requestLogContextMiddleware` at request start (sets `requestId`, `route`, `method`) |
| 35 | +2. `authorizeServer` / `createAuthorizedManager` after Convex auth succeeds (sets workspace + user fields) |
| 36 | + |
| 37 | +--- |
| 38 | + |
| 39 | +## Event catalog |
| 40 | + |
| 41 | +| Event | Emitted from | Key payload fields | |
| 42 | +|---|---|---| |
| 43 | +| `http.request.completed` | middleware | `statusCode` | |
| 44 | +| `http.request.failed` | middleware | `statusCode`, `errorCode` | |
| 45 | +| `mcp.oauth.proxy.failed` | `routes/mcp/oauth.ts`, `routes/web/oauth.ts` | `targetUrlHost`, `oauthPhase`, `errorCode`, `statusCode?` | |
| 46 | +| `mcp.tool.execution.failed` | `routes/web/tools.ts` | `toolName`, `serverId?`, `errorCode` | |
| 47 | +| `tunnel.created` | `routes/mcp/tunnels.ts` | `tunnelKind`, `tunnelDomain`, `existed`, `credentialIdPresent?` | |
| 48 | +| `tunnel.creation_failed` | `routes/mcp/tunnels.ts` | `tunnelKind`, `errorCode` | |
| 49 | +| `tunnel.record_failed` | `routes/mcp/tunnels.ts` | `tunnelKind`, `tunnelDomain?`, `errorCode` | |
| 50 | +| `chat.session.persist.failed` | `utils/chat-ingestion.ts` | `failureKind`, `statusCode?`, `sourceType?` | |
| 51 | +| `widget.resource.served` | `routes/apps/mcp-apps/index.ts` | `widgetType`, `resourceUri`, `cspMode`, `mimeTypeValid?` | |
| 52 | +| `widget.resource.failed` | `routes/apps/mcp-apps/index.ts` | `widgetType`, `resourceUri?`, `errorCode` | |
| 53 | +| `mcp.connection.closed_with_pending_requests` | `index.ts` (system event) | `errorCode` | |
| 54 | + |
| 55 | +All events live in `server/utils/log-events.ts`. Add new events there before emitting them. |
| 56 | + |
| 57 | +--- |
| 58 | + |
| 59 | +## Adding a new event |
| 60 | + |
| 61 | +1. Add the event name and payload type to `RequestEventMap` (or `SystemEventMap`) in `log-events.ts`. |
| 62 | +2. Emit it with `getRequestLogger(c, "routes.your.component").event("your.event.name", payload)`. |
| 63 | +3. Add a row to the catalog table above. |
| 64 | + |
| 65 | +--- |
| 66 | + |
| 67 | +## Scrubbing |
| 68 | + |
| 69 | +All payloads are scrubbed by `scrubLogPayload` before reaching Axiom. Forbidden key substrings |
| 70 | +(token, secret, email, authorization, cookie, apikey, stripe\*, pkce\*) are replaced with `"[redacted]"`. |
| 71 | +String values are scanned for Bearer tokens, JWTs, email addresses, and `sk-` keys. |
| 72 | + |
| 73 | +Never put raw error messages containing user input directly into event payloads without first |
| 74 | +verifying they don't carry secrets. |
| 75 | + |
| 76 | +--- |
| 77 | + |
| 78 | +## ESLint enforcement |
| 79 | + |
| 80 | +`eslint.config.js` warns on new `logger.warn|error|info` calls in `server/routes/web/`. |
| 81 | +Run `npx eslint server/routes/web/` to check before committing route changes. |
0 commit comments