|
|
@@ -0,0 +1,70 @@
|
|
|
+# Logging
|
|
|
+
|
|
|
+## Overview
|
|
|
+
|
|
|
+All server-side logging goes through [Winston](https://github.com/winstonjs/winston). A single root logger is created at module load and shared across the process. Each domain gets a **child logger** that automatically attaches a `domain` field to every log entry, making it easy to filter, tail, and route logs by domain in any log aggregation system.
|
|
|
+
|
|
|
+Logging is server-only. Client-side code does not import from `$lib/server/`.
|
|
|
+
|
|
|
+## Request Tracing
|
|
|
+
|
|
|
+Every request is assigned a UUID (`requestId`) generated in `handle()` and stored in both `event.locals.requestId` and the request-scoped `AsyncLocalStorage` context (`src/lib/server/context.ts`).
|
|
|
+
|
|
|
+A custom Winston format reads from `AsyncLocalStorage` on every log call and injects `requestId` automatically. This means every log line emitted anywhere during a request — across domain loggers, infrastructure clients, auth — carries the same `requestId` with no manual threading required.
|
|
|
+
|
|
|
+## Log Format
|
|
|
+
|
|
|
+Format is selected by `NODE_ENV`:
|
|
|
+
|
|
|
+| `NODE_ENV` | Format | Use case |
|
|
|
+| ------------- | ------------------------------------------------------------------------------------------------- | -------------------- |
|
|
|
+| `development` | Colorized, human-readable `HH:mm:ss LEVEL [domain] (requestId) message {...extra}` | Local dev |
|
|
|
+| anything else | Structured JSON with `timestamp`, `level`, `domain`, `requestId`, `message`, and any extra fields | Production / staging |
|
|
|
+
|
|
|
+JSON output is structured so every field is at the top level — no nested `meta` objects — which makes querying in tools like Loki, Elasticsearch, or CloudWatch straightforward.
|
|
|
+
|
|
|
+## Log Levels
|
|
|
+
|
|
|
+Standard Winston levels in descending severity: `error`, `warn`, `info`, `http`, `verbose`, `debug`, `silly`.
|
|
|
+
|
|
|
+Set `LOG_LEVEL` env var to control the minimum level emitted (default: `info`).
|
|
|
+
|
|
|
+## Domain Loggers
|
|
|
+
|
|
|
+Each server module imports its own named logger from `$lib/server/logger.ts`. The `domain` field is injected automatically by the child logger — callers never set it manually.
|
|
|
+
|
|
|
+| Logger export | Domain tag | Used in |
|
|
|
+| -------------- | ---------------- | ------------------------------- |
|
|
|
+| `logger` | _(root, no tag)_ | General / unclassified |
|
|
|
+| `serverLogger` | `server` | `server.ts`, `hooks.server.ts` |
|
|
|
+| `authLogger` | `auth` | `auth.ts` |
|
|
|
+| `gitLogger` | `git` | `git/github.ts`, `git/cache.ts` |
|
|
|
+| `k8sLogger` | `k8s` | `k8s.ts` |
|
|
|
+
|
|
|
+Domain-specific loggers for agents, models, tools, knowledge-bases, and gateways are added as those modules are implemented.
|
|
|
+
|
|
|
+## Usage
|
|
|
+
|
|
|
+```ts
|
|
|
+import { serverLogger as log } from '$lib/server/logger';
|
|
|
+
|
|
|
+log.info('Server ready');
|
|
|
+log.warn('K8s CR not found', { name, namespace });
|
|
|
+log.error('Failed to open PR', { err, agentName }); // always pass error as `err` for stack capture
|
|
|
+```
|
|
|
+
|
|
|
+Pass structured fields as the second argument rather than interpolating into the message string. This keeps the message string stable (searchable as a constant) and fields queryable individually.
|
|
|
+
|
|
|
+## What to Log
|
|
|
+
|
|
|
+| Event | Level | Fields |
|
|
|
+| ---------------------- | ------- | --------------------------------------- |
|
|
|
+| Server init / shutdown | `info` | — |
|
|
|
+| Component initialized | `info` | component name, key config (no secrets) |
|
|
|
+| Login success | `info` | `username` |
|
|
|
+| Login failure | `warn` | `username` (never log passwords) |
|
|
|
+| Outbound API call | `debug` | `url`, method |
|
|
|
+| Outbound API error | `error` | `url`, `status`, `err` |
|
|
|
+| Unhandled exception | `error` | `err` (stack) |
|
|
|
+
|
|
|
+Never log secrets, passwords, tokens, or full request/response bodies.
|