Documentation
¶
Overview ¶
Package logging provides structured logging for the Entire CLI using slog.
A Logger owns one log file. The entry point builds one, puts it in the context with WithLogger, and closes it from there; nothing here is process-global.
Index ¶
- Constants
- func Debug(ctx context.Context, msg string, attrs ...any)
- func Error(ctx context.Context, msg string, attrs ...any)
- func Info(ctx context.Context, msg string, attrs ...any)
- func ParseLevel(s string) (level slog.Level, ok bool)
- func SessionLoggerFromContext(ctx context.Context) *slog.Logger
- func Warn(ctx context.Context, msg string, attrs ...any)
- func WithAgent(ctx context.Context, agentName types.AgentName) context.Context
- func WithComponent(ctx context.Context, component string) context.Context
- func WithLogger(ctx context.Context, l *Logger) context.Context
- func WithSessionID(ctx context.Context, sessionID string) context.Context
- type Config
- type Logger
Constants ¶
const ( LogFileName = "entire.log" LogName = LogsName + "/" + LogFileName )
LogFileName is the log file's basename, and LogName addresses it from the .entire root — the pair readers (doctor logs, trace) use so they open the same file this package writes without re-joining LogsDir themselves.
const LogLevelEnvVar = "ENTIRE_LOG_LEVEL"
LogLevelEnvVar is the environment variable that controls log level.
const LogsDir = ".entire/logs"
LogsDir is the directory where log files are stored (relative to repo root). It is the spelling for messages, git paths, and doctor output; the writer below addresses the same directory as LogsName inside the .entire root.
const LogsName = "logs"
LogsName is LogsDir relative to the .entire root, which is the coordinate Config.Root resolves names in.
Variables ¶
This section is empty.
Functions ¶
func ParseLevel ¶ added in v0.10.3
ParseLevel maps a log level name to a slog.Level. ok is false for an unrecognized non-empty name, so the caller can warn about a typo. An empty name is INFO and reports ok.
func SessionLoggerFromContext ¶ added in v0.10.3
SessionLoggerFromContext returns the context's logger stamped with its session ID, for packages that hold an injected *slog.Logger and call it without a context (redact) — those calls never reach log(), so they cannot pick the attribute up themselves.
Only the session is stamped. redact tags its own lines with component=redaction, and slog does not dedupe attrs, so adding component here would emit the key twice.
func WithAgent ¶
WithAgent adds an agent name to the context. Agent names identify the AI agent generating activity (e.g., "claude-code", "cursor", "aider").
func WithComponent ¶
WithComponent adds a component name to the context. Component names help identify the subsystem generating logs (e.g., "hooks", "strategy", "session").
func WithLogger ¶ added in v0.10.3
WithLogger attaches a Logger to the context. The exit point closes it by reading it back out — the logger has no other owner — so don't stash it anywhere that outlives the command.
func WithSessionID ¶ added in v0.10.3
WithSessionID adds a session ID to the context, so lines logged under it are filterable by session. Re-stamping a derived context shadows the outer session for that scope only.
It deliberately does not validate: this is an slog attribute, not a path, so guarding traversal belongs where the ID is resolved from the filesystem.
Types ¶
type Config ¶ added in v0.10.3
type Config struct {
// Root resolves the directory tree Dir lives in, and is called on the first
// line actually written — never by New. A function rather than an *os.Root
// because the caller's root is .entire, which must not be created just
// because a command started: a command that logs nothing must leave an
// untouched repo untouched. Required rather than defaulted: only the caller
// knows whether writing there is allowed.
Root func() (*os.Root, error)
// Dir is the log directory's name within Root, created if absent.
Dir string
// Level is the minimum level to emit; the zero value is slog.LevelInfo.
Level slog.Level
}
type Logger ¶ added in v0.10.3
type Logger struct {
// contains filtered or unexported fields
}
Logger is safe for concurrent use: slog handlers may be called from several goroutines and bufio.Writer is not goroutine-safe, so mu serializes every write and Close. Writes after Close are dropped — losing a log line must never surface as an error in the caller.
The file is created on first write, not by New, so a command that logs nothing (shell completion, version, help) neither pays for opening it nor leaves an empty entire.log behind.
func LoggerFromContext ¶ added in v0.10.3
LoggerFromContext returns the Logger attached by WithLogger, or nil when logging is not file-backed. Its methods are nil-safe, so a nil result can be closed or asked for its Slog without a guard.
func New ¶ added in v0.10.3
New returns a Logger that writes JSON to cfg.Dir/entire.log, creating both on the first line actually written. It never falls back to stderr: an injected logger that wrote to the terminal would splash operational lines over the user's output.
A directory that cannot be created or opened is not reported here — by then lines have already been dropped, which is the same outcome as any other write failure and must never surface as an error in the caller. Two things can still surface it: Close returns it, and EnsureOpen asks for it up front, which is how `entire doctor` reports a log sink that is silently swallowing every diagnostic.
func (*Logger) Close ¶ added in v0.10.3
Close flushes the buffer and closes the log file, and reports a failure to open it if one happened. Idempotent, and safe to call concurrently with logging: later writes are dropped rather than reopening the file.
func (*Logger) EnsureOpen ¶ added in v0.10.3
EnsureOpen opens the log file now instead of on the first line written, and reports a directory it cannot use.
It exists so the failure has one caller that looks: the deferred open drops the line and returns nil, so an unwritable .entire/logs makes every diagnostic vanish with no exit code, no message, and an empty log to grep — indistinguishable from "Entire never ran". `entire doctor` calls this to name it. Idempotent, and nil-safe so a caller need not know whether the entry point installed a logger.
It creates the directory and file, which is what the next logged line would have done anyway. That does cost the empty-file-free property New buys for commands that log nothing, so call it from commands that are diagnosing, not from hot paths.