logwriter

package
v0.14.0 Latest Latest
Warning

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

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

Documentation

Overview

Package logwriter provides atomic NDJSON event writing with optional size-based rotation for workspace logs and no rotation for per-slot logs.

Atomicity: each Append opens the file with O_APPEND|O_CREATE|O_WRONLY and writes a single newline-terminated line. POSIX guarantees that writes no larger than PIPE_BUF (512 bytes minimum; 4096+ on Linux and macOS) are atomic on append-only file descriptors, so concurrent slot processes cannot interleave lines for the small payloads we emit (~150–300 bytes).

Rotation (workspace log only): on every Append, the file is stat-ed; if its size exceeds maxSize the numbered series is shifted (.log.1→.log.2, etc.), the current .log is renamed to .log.1, and a fresh .log is opened. The oldest numbered file is removed when the count exceeds maxFiles.

Single-writer assumed for v1 (one process appending per file at a time). Concurrent-process safety relies on O_APPEND atomicity but NOT on rotation being race-free — do not call Append concurrently on the same Writer.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func AppendJSONOnce added in v0.13.0

func AppendJSONOnce(path string, v interface{ MarshalJSON() ([]byte, error) }) error

AppendJSONOnce is the no-rotation variant of AppendJSON. Mirrors AppendOnce: same O_APPEND|O_CREATE|O_WRONLY contract, same parent- dir auto-create. Pulled out so the marshal/write split lives in one place — both Append (logwriter.Event) and AppendJSON (json.Marshaler) route through here.

func AppendOnce

func AppendOnce(path string, e Event) error

AppendOnce writes one event line to path without rotation. Use this for per-slot logs and other call sites that don't carry rotation state across calls — it skips the Writer allocation entirely. The parent directory is created if absent.

Atomicity: opens with O_APPEND|O_CREATE|O_WRONLY and writes a single newline-terminated line. POSIX guarantees writes ≤PIPE_BUF are atomic.

func SlotLogPath

func SlotLogPath(slotDir, slot string) string

SlotLogPath returns the per-slot log path under <slotDir>/log. Per-slot logs are bounded by slot lifetime and never rotate, so callers should use AppendOnce(SlotLogPath(slotDir, slot), event) — no Writer needed.

Types

type Event

type Event struct {
	Timestamp time.Time              `json:"-"`
	Slot      string                 `json:"-"` // optional; omitted when empty
	Event     EventType              `json:"-"`
	Fields    map[string]interface{} `json:"-"`
}

Event is one NDJSON row. MarshalJSON merges Fields into the top-level object so the on-disk row is flat: {"ts":"...","event":"...","slot":"...",...fields}. json:"-" tags keep the zero-value encoding path from double-emitting keys.

func (Event) MarshalJSON

func (e Event) MarshalJSON() ([]byte, error)

MarshalJSON emits a single flat JSON object. Required keys are always present; "slot" is omitted when empty; entries from Fields are merged at the top level. If a Fields key collides with a reserved key ("ts", "event", "slot") it is silently dropped — callers must not reuse reserved names.

type EventType

type EventType string

EventType is one entry in the closed catalog of slot/workspace event kinds. Each constant matches the string written to the on-disk NDJSON "event" field so operator tooling can grep without understanding Go constants.

const (
	// EventJoin is emitted when a slot successfully acquires a session.
	EventJoin EventType = "join"

	// EventCommit is emitted when a slot's commit completes without error.
	EventCommit EventType = "commit"

	// EventCommitError is emitted when a slot's commit fails.
	EventCommitError EventType = "commit_error"

	// EventRenew is emitted when a slot renews its lease.
	EventRenew EventType = "renew"

	// EventClose is emitted when a slot closes and releases its session.
	EventClose EventType = "close"

	// EventDispatched is emitted when the workspace dispatches a new plan.
	EventDispatched EventType = "dispatched"

	// EventError is emitted for any unexpected error worth recording.
	EventError EventType = "error"
)

type Writer

type Writer struct {
	// contains filtered or unexported fields
}

Writer writes NDJSON event lines to a single file, optionally rotating on size. Zero value is not valid; use Open or OpenSlot.

func Open

func Open(path string) *Writer

Open returns a Writer for the workspace log at path. Defaults: 10 MiB maxSize, 5 backup files. Overridable via BONES_LOG_MAX_SIZE (bytes) and BONES_LOG_MAX_FILES.

func OpenSlot

func OpenSlot(slotDir, slot string) *Writer

OpenSlot returns a Writer for a per-slot log at <slotDir>/log. Rotation is disabled (maxSize=0) because per-slot logs are bounded by slot lifetime and must not rotate under the slot process's feet.

func (*Writer) Append

func (w *Writer) Append(e Event) error

Append writes one event line atomically to the log file. If maxSize > 0 and the file has grown beyond it, rotation is performed first. The parent directory is created if absent.

func (*Writer) AppendJSON added in v0.13.0

func (w *Writer) AppendJSON(v interface{ MarshalJSON() ([]byte, error) }) error

AppendJSON writes one JSON-marshalable value as a newline-terminated line to the Writer's file. Used by the hub-log path (#322), which carries a typed LogEntry rather than a logwriter.Event — the reserved-key conventions in Event.MarshalJSON would clash with the hub.log shape, so the hub side defines its own struct and writes it through this thinner door.

Rotation policy is identical to Append: maxSize > 0 triggers rotateIfNeeded before the write.

Jump to

Keyboard shortcuts

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