# Contract: Logging (Go Web Applications) Version: 1.0 ## Core Rule **All logging goes to the server binary's stdout/stderr — never to the browser console.** - `console.log`, `console.error`, `console.warn` in JavaScript are for debugging only. Remove them before committing. They are not a substitute for server-side logging. - Diagnostic information (errors, slow queries, ingest results, background task status) must be logged server-side so it is visible in the terminal where the binary runs. ## Go Logging Standard Use the standard library `log/slog` (Go 1.21+) or `log` for simpler projects. ```go // correct — structured, visible in binary console slog.Info("export started", "user", userID, "rows", count) slog.Error("db query failed", "err", err) // wrong — goes to browser devtools, invisible in production // (JS template rendered in HTML) // console.log("export started") ``` ## Log Levels | Level | When to use | |---------|-------------| | `Debug` | Detailed flow tracing; disabled in production builds | | `Info` | Normal operation milestones: server start, request handled, export done | | `Warn` | Recoverable anomaly: retry, fallback used, deprecated path | | `Error` | Operation failed; always include `"err", err` as a structured attribute | ## What to Always Log - Server startup: address, port, config source. - Background task start/finish/error with task name and duration. - CSV/file export: scope, row count, duration. - Ingest/import: file name, rows accepted, rows rejected, error summary. - Any `500` response: handler name + error. ## What Not to Log - Do not log full request bodies or CSV content — can contain PII or large payloads. - Do not log passwords, tokens, or secrets even partially. - Do not add `fmt.Println` for debugging and leave it in committed code. ## htmx / Browser-rendered Responses - Handler errors that result in an htmx partial re-render must still log the error server-side. - Do not rely on the browser network tab as the only visibility into server errors. ## Format - Use structured key-value attributes, not concatenated strings. ```go // correct slog.Error("sync failed", "component", comp.Serial, "err", err) // wrong — hard to grep, hard to parse log.Printf("sync failed for component %s: %v", comp.Serial, err) ```