log

package
v1.42.0 Latest Latest
Warning

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

Go to latest
Published: Aug 13, 2026 License: AGPL-3.0 Imports: 14 Imported by: 0

README

Stapler Squad Logging System

This package implements a configurable logging system for Stapler Squad with the following features:

Key Features

  • Configurable Log Location: Logs are stored in ~/.stapler-squad/logs/ by default, but this can be changed in the config.
  • Global and Session-Specific Logs: Separate log files are created for each session.
  • Log Rotation: Logs are automatically rotated based on size and age.
  • Configuration Options: Several options can be configured in ~/.stapler-squad/config.json

Configuration

The following logging options can be configured in config.json:

{
  "logs_enabled": true,
  "logs_dir": "",  // Empty for default location (~/.stapler-squad/logs/)
  "log_max_size": 10,  // Max log file size in MB before rotation
  "log_max_files": 5,  // Max number of rotated files to keep
  "log_max_age": 30,  // Max age in days for rotated files
  "log_compress": true,  // Whether to compress rotated files
  "use_session_logs": true  // Whether to create separate log files for each session
}

Usage

Global Logging

The global loggers (InfoLog, WarningLog, and ErrorLog) can be used directly:

log.InfoLog.Printf("This is an info message")
log.WarningLog.Printf("This is a warning message")
log.ErrorLog.Printf("This is an error message")
Session-Specific Logging

For session-specific logging, use the LogForSession function:

// Log to session-specific file and global log
log.LogForSession("session-id", "info", "This is an info message for session %s", "session-id")
log.LogForSession("session-id", "warning", "This is a warning message for session %s", "session-id")
log.LogForSession("session-id", "error", "This is an error message for session %s", "session-id")

Implementation Details

  • Log files are stored in ~/.stapler-squad/logs/ by default
  • Global log file is named claudesquad.log
  • Session log files are named session_<session-id>.log
  • Log rotation is implemented using the lumberjack package
  • Logs are rotated when they reach the configured size
  • Old log files are compressed if the log_compress option is enabled

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	WarningLog *log.Logger
	InfoLog    *log.Logger
	ErrorLog   *log.Logger
	DebugLog   *log.Logger
)

Shim loggers for zero-migration compatibility — populated by LogManager.

View Source
var (
	// ErrSessionLogsDisabled is returned when session logs are disabled in config
	ErrSessionLogsDisabled = fmt.Errorf("session logs disabled in config")
)

Functions

func Close

func Close()

func Debug added in v1.35.0

func Debug(msg string, args ...any)

Debug logs a debug-level message through the default slog handler. The handler drops debug records when the runtime level is above DEBUG, so this is safe to call without an IsDebugEnabled() guard.

func DebugS

func DebugS(message string, fields ...map[string]interface{})

DebugS logs a structured debug message

func Error added in v1.35.0

func Error(msg string, args ...any)

Error logs an error-level message through the default slog handler.

func ErrorS

func ErrorS(message string, fields ...map[string]interface{})

ErrorS logs a structured error message

func FatalS

func FatalS(message string, fields ...map[string]interface{})

FatalS logs a structured fatal message

func ForSession added in v1.15.0

func ForSession(sessionID string) *slog.Logger

ForSession returns a *slog.Logger pre-populated with "session" = sessionID. All calls route through the async slog handler — no stdlib mutex serialization. Session-specific log files still receive the entry via LogForSession when needed.

func GetActiveSessionLogPaths

func GetActiveSessionLogPaths() map[string]string

GetActiveSessionLogPaths returns the paths to all active session log files

func GetConfigDir

func GetConfigDir() (string, error)

GetConfigDir returns the path to the application's configuration directory

func GetGlobalLogPath

func GetGlobalLogPath() string

GetGlobalLogPath returns the path to the global log file

func GetLogDir

func GetLogDir(cfg *LogConfig) (string, error)

GetLogDir returns the directory where logs should be stored

func GetLogFilePath

func GetLogFilePath(cfg *LogConfig) (string, error)

GetLogFilePath returns the full path to the log file

func GetSessionLogFilePath

func GetSessionLogFilePath(cfg *LogConfig, sessionID string) (string, error)

GetSessionLogFilePath returns the full path to a session-specific log file

func GetTestLogDir

func GetTestLogDir() (string, error)

GetTestLogDir returns the directory where test logs should be stored Test logs are isolated in a dedicated subdirectory for easy cleanup

func Info added in v1.35.0

func Info(msg string, args ...any)

Info logs an info-level message through the default slog handler (async, no mutex hold). args are alternating key-value pairs: log.Info("msg", "key", val, "key2", val2)

func InfoS

func InfoS(message string, fields ...map[string]interface{})

InfoS logs a structured info message

func Initialize

func Initialize(daemon bool)

func InitializeForTests

func InitializeForTests(fileLevel LogLevel, consoleLevel LogLevel)

InitializeForTests sets up logging specifically for test environments with dual-stream configuration. This allows DEBUG logs to go to file while ERROR logs appear in console for immediate visibility.

Parameters:

  • fileLevel: Minimum level for file logging (typically DEBUG to capture everything)
  • consoleLevel: Minimum level for console logging (typically ERROR to avoid noise)

Example:

log.InitializeForTests(log.DEBUG, log.ERROR)  // DEBUG→file, ERROR→console

func InitializeWithConfig

func InitializeWithConfig(daemon bool, externalConfig interface{})

InitializeWithConfig sets up logging with the provided configuration.

func IsDebugEnabled added in v1.35.0

func IsDebugEnabled() bool

IsDebugEnabled returns true when the runtime level is DEBUG. Use this to gate expensive format-string construction before calling DebugLog.Printf.

func LogForSession

func LogForSession(sessionID, level, format string, v ...interface{})

LogForSession logs a message to the session-specific log file

func LogSessionPathsToStderr

func LogSessionPathsToStderr()

LogSessionPathsToStderr outputs session log file paths to stderr on exit

func SetRuntimeLevel added in v1.35.0

func SetRuntimeLevel(level LogLevel)

SetRuntimeLevel changes the minimum log level for all output streams immediately. Safe to call from any goroutine. Takes effect on the next log call.

func Warn added in v1.35.0

func Warn(msg string, args ...any)

Warn logs a warning-level message through the default slog handler.

func WarningS

func WarningS(message string, fields ...map[string]interface{})

WarningS logs a structured warning message

Types

type AsyncHandler added in v1.35.0

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

AsyncHandler wraps a slog.Handler with a channel buffer. Log calls enqueue a cloned Record and return immediately; a background goroutine drains the channel. On full buffer the record is dropped and the drop counter increments. WithAttrs and WithGroup share the same underlying channel so a single goroutine drains all derived loggers.

func NewAsyncHandler added in v1.35.0

func NewAsyncHandler(next slog.Handler, bufSize int) *AsyncHandler

NewAsyncHandler wraps next with an async channel of bufSize capacity.

func (*AsyncHandler) Dropped added in v1.35.0

func (h *AsyncHandler) Dropped() int64

Dropped returns the number of records dropped due to a full buffer.

func (*AsyncHandler) Enabled added in v1.35.0

func (h *AsyncHandler) Enabled(ctx context.Context, level slog.Level) bool

Enabled delegates to the underlying handler.

func (*AsyncHandler) Flush added in v1.35.0

func (h *AsyncHandler) Flush(_ context.Context) error

Flush closes the channel and waits for all enqueued records to be written. After Flush the handler must not be used.

func (*AsyncHandler) Handle added in v1.35.0

func (h *AsyncHandler) Handle(ctx context.Context, r slog.Record) error

Handle enqueues the record for async writing. Drops and counts if buffer full. Safe to call concurrently with Flush — the RWMutex ensures close and send are mutually exclusive: Flush cannot close the channel while a send is in progress.

func (*AsyncHandler) StartDrain added in v1.35.0

func (h *AsyncHandler) StartDrain()

StartDrain launches the background drain goroutine. Must be called once before the handler is used. Call Flush to stop it and drain remaining work.

func (*AsyncHandler) WithAttrs added in v1.35.0

func (h *AsyncHandler) WithAttrs(attrs []slog.Attr) slog.Handler

WithAttrs returns a new AsyncHandler whose next handler has the given attrs, sharing the same channel so one drain goroutine serves all derived loggers.

func (*AsyncHandler) WithGroup added in v1.35.0

func (h *AsyncHandler) WithGroup(name string) slog.Handler

WithGroup returns a new AsyncHandler with a grouped next handler, sharing the channel.

type Every

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

Every is used to log at most once every timeout duration.

func NewEvery

func NewEvery(timeout time.Duration) *Every

func (*Every) ShouldLog

func (e *Every) ShouldLog() bool

ShouldLog returns true if the timeout has passed since the last log.

type LogConfig

type LogConfig struct {
	LogsEnabled    bool
	LogsDir        string
	LogMaxSize     int
	LogMaxFiles    int
	LogMaxAge      int
	LogCompress    bool
	UseSessionLogs bool
	LogLevel       LogLevel // Deprecated: Use FileLevel and ConsoleLevel instead
	StructuredLogs bool
	PrettyLogs     bool // For development - formats JSON logs for readability

	// Dual-stream logging configuration (file + console)
	ConsoleEnabled bool     // Enable/disable console output (default: true)
	ConsoleLevel   LogLevel // Minimum level for console (default: ERROR for tests, INFO for production)
	FileEnabled    bool     // Enable/disable file output (default: true)
	FileLevel      LogLevel // Minimum level for file (default: DEBUG)
}

LogConfig holds logging configuration

func ConfigToLogConfig

func ConfigToLogConfig(externalConfig interface{}) *LogConfig

ConfigToLogConfig converts an external config to our internal LogConfig

func DefaultLogConfig

func DefaultLogConfig() *LogConfig

DefaultLogConfig returns the default logging configuration

type LogLevel

type LogLevel int

LogLevel represents the severity of a log entry

const (
	DEBUG LogLevel = iota
	INFO
	WARNING
	ERROR
	FATAL
)

func GetRuntimeLevel added in v1.35.0

func GetRuntimeLevel() LogLevel

GetRuntimeLevel returns the current minimum log level.

func ParseLogLevel

func ParseLogLevel(level string) LogLevel

ParseLogLevel parses a string into a LogLevel

func (LogLevel) String

func (l LogLevel) String() string

String returns the string representation of a log level

type LogManager added in v1.35.0

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

LogManager encapsulates all log state that was previously in package-level globals. Use NewLogManager to create one; use the package-level functions (InfoLog, etc.) via the defaultManager for zero-migration compatibility.

func (*LogManager) Close added in v1.35.0

func (m *LogManager) Close()

Close drains async writers, flushes the slog handler, and closes all log files. Drain order matters: async writers must be drained before the underlying file is closed, otherwise buffered entries are lost.

func (*LogManager) CloseSession added in v1.35.0

func (m *LogManager) CloseSession(id string)

CloseSession removes session-scoped loggers and closes their file handle.

func (*LogManager) ForSession added in v1.35.0

func (m *LogManager) ForSession(id string) (*SessionLoggers, error)

ForSession returns or creates session-scoped loggers.

type SessionLogger added in v1.15.0

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

SessionLogger is a session-scoped logger that automatically injects the session ID into every log call, eliminating the need to pass the session ID manually.

Usage:

logger := log.ForSession(i.Title)
logger.Error("Failed to setup git worktree: %v", err)

func ForSessionLegacy deprecated added in v1.35.0

func ForSessionLegacy(sessionID string) *SessionLogger

ForSessionLegacy returns the old SessionLogger for callers that write to per-session log files. New code should use ForSession instead.

Deprecated: use ForSession.

func (*SessionLogger) Debug added in v1.15.0

func (sl *SessionLogger) Debug(format string, v ...interface{})

func (*SessionLogger) Error added in v1.15.0

func (sl *SessionLogger) Error(format string, v ...interface{})

func (*SessionLogger) Info added in v1.15.0

func (sl *SessionLogger) Info(format string, v ...interface{})

func (*SessionLogger) Warning added in v1.15.0

func (sl *SessionLogger) Warning(format string, v ...interface{})

type SessionLoggers

type SessionLoggers struct {
	WarningLog *log.Logger
	InfoLog    *log.Logger
	ErrorLog   *log.Logger
	DebugLog   *log.Logger
	LogFile    io.Closer
}

SessionLoggers holds the loggers for a specific session

func GetSessionLoggers

func GetSessionLoggers(sessionID string) (*SessionLoggers, error)

GetSessionLoggers creates or retrieves loggers for a specific session

type StructuredLogEntry

type StructuredLogEntry struct {
	Timestamp time.Time              `json:"timestamp"`
	Level     string                 `json:"level"`
	Message   string                 `json:"message"`
	SessionID string                 `json:"session_id,omitempty"`
	Component string                 `json:"component,omitempty"`
	Function  string                 `json:"function,omitempty"`
	File      string                 `json:"file,omitempty"`
	Line      int                    `json:"line,omitempty"`
	Fields    map[string]interface{} `json:"fields,omitempty"`
	Error     string                 `json:"error,omitempty"`
}

StructuredLogEntry represents a structured log entry

type StructuredLogger

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

StructuredLogger provides structured logging functionality

func NewStructuredLogger

func NewStructuredLogger(writer io.Writer, level LogLevel, prettyLog bool) *StructuredLogger

NewStructuredLogger creates a new structured logger

func (*StructuredLogger) Debug

func (sl *StructuredLogger) Debug(message string, fields ...map[string]interface{})

Debug logs a debug message

func (*StructuredLogger) Error

func (sl *StructuredLogger) Error(message string, fields ...map[string]interface{})

Error logs an error message

func (*StructuredLogger) Fatal

func (sl *StructuredLogger) Fatal(message string, fields ...map[string]interface{})

Fatal logs a fatal message

func (*StructuredLogger) Info

func (sl *StructuredLogger) Info(message string, fields ...map[string]interface{})

Info logs an info message

func (*StructuredLogger) Log

func (sl *StructuredLogger) Log(level LogLevel, message string, fields map[string]interface{})

Log writes a structured log entry

func (*StructuredLogger) LogWithFields

func (sl *StructuredLogger) LogWithFields(level LogLevel, message string, fields map[string]interface{})

LogWithFields logs a message with additional fields

func (*StructuredLogger) Warning

func (sl *StructuredLogger) Warning(message string, fields ...map[string]interface{})

Warning logs a warning message

type TraceIDHandler added in v1.35.0

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

TraceIDHandler is a slog.Handler middleware that injects OTel trace_id and span_id into every log record when a span is active in the context. It must be the outermost handler in the chain so trace IDs are extracted at call time, before the record enters the async buffer.

func NewTraceIDHandler added in v1.35.0

func NewTraceIDHandler(next slog.Handler) *TraceIDHandler

NewTraceIDHandler wraps next, injecting trace context into every Handle call.

func (*TraceIDHandler) Enabled added in v1.35.0

func (h *TraceIDHandler) Enabled(ctx context.Context, level slog.Level) bool

func (*TraceIDHandler) Handle added in v1.35.0

func (h *TraceIDHandler) Handle(ctx context.Context, r slog.Record) error

func (*TraceIDHandler) WithAttrs added in v1.35.0

func (h *TraceIDHandler) WithAttrs(attrs []slog.Attr) slog.Handler

func (*TraceIDHandler) WithGroup added in v1.35.0

func (h *TraceIDHandler) WithGroup(name string) slog.Handler

Jump to

Keyboard shortcuts

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