logs

package
v0.2.3 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 2, 2026 License: Apache-2.0 Imports: 9 Imported by: 0

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

func Render(rec Record, format Format) string

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).

func (Filter) Matches

func (f Filter) Matches(rec Record) bool

Matches reports whether rec passes every set predicate in f.

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.

const (
	// FormatText is the default compact, human-readable one-line rendering.
	FormatText Format = iota
	// FormatNCSA is the Apache "combined"-style log line (varnishncsa default).
	FormatNCSA
	// FormatJSON echoes the original JSON object verbatim (a filter pass-through).
	FormatJSON
)

func ParseFormat

func ParseFormat(s string) (Format, error)

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

func ParseLine(line []byte) (Record, bool, error)

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).

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL