Documentation
¶
Overview ¶
Package logs implements `cadish logs` — the NCSA-style access-log tail.
Source design (the "clean source" decision, DECISIONS D18): cadish already emits one structured slog line per request (internal/server/accesslog.go). The simplest robust streaming source is therefore a *file cadish writes*, not a new admin IPC channel: point cadish's access log at a file (the slog JSON handler), then `cadish logs -f FILE` tails it. This keeps `logs` a pure, decoupled reader (stdlib only, no fsnotify) that also works on a pipe or on stdin — an operator can `cadish run 2>access.json` and `cadish logs -f access.json` in another shell, or pipe directly. The reader parses each JSON line into a Record, applies host/path/status/cache filters, and renders it as text, JSON, or NCSA (Apache-combined) — all of which are pure functions tested in isolation.
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func Follow ¶
func Follow(ctx context.Context, path string, out, errOut io.Writer, filter Filter, format Format, opts FollowOptions) error
Follow tails the file at path, parsing/filtering/rendering each appended NDJSON access line until ctx is cancelled. It uses simple polling (no fsnotify dependency — honors the no-new-deps invariant) and handles truncation/rotation: if the file shrinks below the read offset, it seeks back to the start. It returns nil on ctx cancellation, or the first non-recoverable I/O error.
func Render ¶
Render formats one Record per the chosen Format, returning a single line WITHOUT a trailing newline (the caller adds it). For FormatJSON it re-marshals the raw object (compact) so any extra operator fields survive.
func Stream ¶
func Stream(r io.Reader, out io.Writer, errOut io.Writer, filter Filter, format Format) (int, error)
Stream reads NDJSON access lines from r, applies filter, renders each surviving record per format, and writes them (newline-terminated) to out. It returns on EOF (the non-follow path). Malformed lines are written to errOut as a warning and skipped, so a single bad line does not abort the stream. It returns the number of records emitted.
Types ¶
type Filter ¶
type Filter struct {
Host string
Path string
Cache string
Status int // exact status, 0 = any
StatusClass int // 1..5 for 1xx..5xx, 0 = any
MinStatus int // inclusive lower bound, 0 = none
}
Filter is a set of predicates a Record must satisfy to be emitted. A zero Filter matches everything. Host/Path are case-insensitive substring matches; Cache is an exact (case-insensitive) match against the cache-status token; Status, when set, matches either an exact code (e.g. 404) or a class (e.g. 4 -> 4xx, via StatusClass).
type FollowOptions ¶
type FollowOptions struct {
// FromStart, when true, emits the whole file before tailing (like `tail -n +1
// -f`); otherwise only lines appended after start are emitted (like `tail -f`).
FromStart bool
// PollInterval is how often the loop checks for appended data. Defaults to 200ms.
PollInterval time.Duration
}
FollowOptions tunes the file-tail follow loop.
type Format ¶
type Format int
Format selects the output rendering for a Record.
func ParseFormat ¶
ParseFormat resolves a -format flag value to a Format.
type Record ¶
type Record struct {
Time time.Time `json:"-"`
Msg string `json:"msg"`
Method string `json:"method"`
Host string `json:"host"`
Path string `json:"path"`
Status int `json:"status"`
Bytes int64 `json:"bytes"`
Cache string `json:"cache"`
Upstream string `json:"upstream"`
DurMs int64 `json:"dur_ms"`
// contains filtered or unexported fields
}
Record is one parsed access-log line. The field names mirror the slog access line keys emitted by internal/server/accesslog.go (method/host/path/status/ bytes/cache/upstream/dur_ms), plus the slog envelope's time/level/msg. Unknown keys are ignored; missing keys default to their zero value.
func ParseLine ¶
ParseLine parses one NDJSON access-log line into a Record. A blank line, or a slog line that is NOT a per-request access line (cadish's access line carries msg:"request"; startup/info/warn lines do not), yields (rec, false, nil) so callers skip it — this filters the non-request slog noise that shares the same file. A malformed JSON line returns an error. The slog *text* handler is NOT supported — configure the JSON handler when feeding `cadish logs` (see docs/logs.md).