slogx

package module
v0.0.0-...-1038aed Latest Latest
Warning

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

Go to latest
Published: Apr 1, 2026 License: MIT Imports: 15 Imported by: 0

README

░██████╗██╗░░░░░░█████╗░░██████╗░██╗░░██╗
██╔════╝██║░░░░░██╔══██╗██╔════╝░╚██╗██╔╝
╚█████╗░██║░░░░░██║░░██║██║░░██╗░░╚███╔╝░
░╚═══██╗██║░░░░░██║░░██║██║░░╚██╗░██╔██╗░
██████╔╝███████╗╚█████╔╝╚██████╔╝██╔╝╚██╗
╚═════╝░╚══════╝░╚════╝░░╚═════╝░╚═╝░░╚═╝

Go Reference Go Report Card

slogx extends Go's log/slog with per-package log level filtering, file output with rotation, trace ID filtering, and an HTTP API for runtime control.

Installation

go get ella.to/slogx

FilterHandler

FilterHandler wraps any slog.Handler and adds the ability to set different log levels for different packages. It inspects the caller's package path at log time and applies the matching rule.

inner := slog.NewJSONHandler(os.Stdout, nil)

handler := slogx.NewFilterHandler(inner,
    slogx.WithDefaultLevel(slog.LevelInfo),
    slogx.WithLogLevel("myapp/db", slog.LevelDebug),
    slogx.WithLogLevel("myapp/http", slog.LevelWarn),
)

slog.SetDefault(slog.New(handler))

In this setup, most of your app logs at Info and above, but the db package logs everything down to Debug, and the http package only logs Warn and above.

Exclusive Filtering

When you only want to see logs from specific packages and silence everything else:

handler := slogx.NewFilterHandler(inner,
    slogx.WithLogLevel("myapp/auth", slog.LevelDebug),
    slogx.WithExclusiveFiltering(),
)

Only logs from myapp/auth will appear. Everything else is dropped regardless of level.

Runtime Configuration

All settings can be changed while the application is running:

handler.SetDefaultLevel(slog.LevelDebug)
handler.SetLogLevel("myapp/payments", slog.LevelDebug)
handler.RemoveLogLevel("myapp/payments")
handler.SetEnabled(false)  // disable all logging
handler.SetEnabled(true)   // re-enable
handler.SetExclusiveFiltering(true)

Trace ID Filtering

Filter logs to only show entries that carry a specific trace ID. Useful when debugging a particular request in a noisy production environment.

type traceKey struct{}

handler.SetTraceIdKey(traceKey{})
handler.SetFilterTraceId("request-abc-123")

// Only logs where the context contains traceKey{} = "request-abc-123"
// or where an attribute value matches "request-abc-123" will pass through.

Clear the filter to go back to normal:

handler.SetFilterTraceId("")

FileHandler

FileHandler writes structured JSON logs to files with automatic rotation. Each run gets its own timestamped directory, and files rotate when they hit a size or record count limit.

fileHandler, err := slogx.NewFileHandler("/var/log/myapp",
    slogx.WithMaxBytes(50 * 1024 * 1024),  // rotate at 50 MB
    slogx.WithMaxRecords(100_000),           // or at 100k records
)
defer fileHandler.Close()

slog.SetDefault(slog.New(fileHandler))

Directory structure:

/var/log/myapp/
  2025-03-25_14-30-00/
    00001.log
    00002.log
    00003.log
  2025-03-24_09-15-00/
    00001.log

Each log line is a JSON object written by slog.JSONHandler, so it works with any log aggregation tool.

Combining with FilterHandler

The common pattern is to wrap FileHandler with FilterHandler:

fileHandler, _ := slogx.NewFileHandler("/var/log/myapp")
filterHandler := slogx.NewFilterHandler(fileHandler,
    slogx.WithDefaultLevel(slog.LevelInfo),
    slogx.WithLogLevel("myapp/db", slog.LevelDebug),
)

slog.SetDefault(slog.New(filterHandler))

Reading Logs Back

List available sessions, then read logs with pagination:

sessions, err := slogx.ListSessions("/var/log/myapp")
// sessions[0].Name      = "2025-03-25_14-30-00"
// sessions[0].FileCount = 3

reader, err := slogx.NewLogReader(sessions[0].Path, 100) // 100 entries per page

page, err := reader.ReadPage(1, 0) // file 1, line offset 0
for _, entry := range page.Entries {
    fmt.Printf("[%s] %s: %s\n", entry.Level, entry.Time.Format(time.RFC3339), entry.Message)
}

// Or read everything
entries, err := reader.ReadAll()

// Or stream with context cancellation
entryCh, errCh := reader.StreamLogs(ctx)
for entry := range entryCh {
    fmt.Println(entry.Message)
}

HTTP API

Expose FilterHandler controls over HTTP for runtime log management. Mount it as a sub-handler on your existing mux:

mux.Handle("/debug/log/", http.StripPrefix("/debug/log", handler.HttpHandler()))

Endpoints

GET /level — Returns current config as JSON:

{
  "disabled": false,
  "default": "INFO",
  "rules": {"myapp/db": "DEBUG"},
  "exclusive_filtering": false,
  "filter_trace_id": ""
}

POST /level?path=myapp/db&level=DEBUG — Set log level for a path (use path=default for the default level)

DELETE /level?path=myapp/db — Remove a path-specific rule

POST /enable?enable=true — Enable or disable all logging

POST /trace-id?trace-id=abc-123 — Filter logs by trace ID

DELETE /trace-id — Clear trace ID filter

GET /trace-id — Get current trace ID filter

License

MIT — see LICENSE for details.

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func CloseHandler

func CloseHandler(h slog.Handler) error

CloseHandler closes a handler if it implements Closer.

func CompressOldSessions

func CompressOldSessions(baseDir string, olderThan time.Duration) ([]string, error)

CompressOldSessions compresses sessions older than the given duration. Returns the paths of compressed sessions.

func CountLogs

func CountLogs(sessionPath string) (int64, error)

CountLogs counts the total number of log entries in a session. This scans all files and may be slow for large sessions.

func ExportSession

func ExportSession(sessionPath string, w io.Writer) error

ExportSession exports a session to a single file or writer.

func FlushHandler

func FlushHandler(h slog.Handler) error

FlushHandler flushes a handler if it implements Flusher.

func ParseFileIndex

func ParseFileIndex(filename string) (int, error)

ParseFileIndex extracts the numeric index from a log filename.

Types

type Closer

type Closer interface {
	Close() error
}

Closer interface for handlers that need cleanup.

type FileHandler

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

FileHandler is a slog.Handler that writes logs to files with rotation support.

func NewFileHandler

func NewFileHandler(baseDir string, opts ...FileHandlerOpt) (*FileHandler, error)

NewFileHandler creates a new FileHandler that writes logs to files. The directory structure will be: baseDir/<timestamp>/00001.log where timestamp is the current date and time when the handler is created.

func (*FileHandler) Close

func (h *FileHandler) Close() error

Close flushes and closes the current log file.

func (*FileHandler) Enabled

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

Enabled implements slog.Handler.

func (*FileHandler) Flush

func (h *FileHandler) Flush() error

Flush forces a flush of the current buffer to disk.

func (*FileHandler) Handle

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

Handle implements slog.Handler.

func (*FileHandler) SessionDir

func (h *FileHandler) SessionDir() string

SessionDir returns the current session directory path.

func (*FileHandler) WithAttrs

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

WithAttrs implements slog.Handler.

func (*FileHandler) WithGroup

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

WithGroup implements slog.Handler.

type FileHandlerOpt

type FileHandlerOpt func(*FileHandler)

FileHandlerOpt is an option for configuring the FileHandler.

func WithMaxBytes

func WithMaxBytes(maxBytes int64) FileHandlerOpt

WithMaxBytes sets the maximum size in bytes before rotating to a new file.

func WithMaxRecords

func WithMaxRecords(maxRecords int64) FileHandlerOpt

WithMaxRecords sets the maximum number of log records before rotating to a new file.

type FileStats

type FileStats struct {
	FileName  string    `json:"file_name"`
	FilePath  string    `json:"file_path"`
	Size      int64     `json:"size"`
	LineCount int64     `json:"line_count"`
	ModTime   time.Time `json:"mod_time"`
}

FileStats returns statistics about log files in a session.

func GetSessionStats

func GetSessionStats(sessionPath string) ([]FileStats, error)

GetSessionStats returns statistics about all log files in a session.

type FilterHandler

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

func NewFilterHandler

func NewFilterHandler(inner slog.Handler, opts ...FilterHandlerOpt) *FilterHandler

func (*FilterHandler) Enabled

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

func (*FilterHandler) GetConfig

func (h *FilterHandler) GetConfig() (defaultLevel slog.Level, rules map[string]slog.Level, disabled bool, exclusiveFiltering bool, traceIdKey any, filterTraceId string)

GetConfig returns the current configuration (for debugging/inspection)

func (*FilterHandler) GetFilterTraceId

func (h *FilterHandler) GetFilterTraceId() string

GetFilterTraceId returns the current trace ID filter

func (*FilterHandler) Handle

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

func (*FilterHandler) HttpHandler

func (h *FilterHandler) HttpHandler() http.Handler

func (*FilterHandler) RemoveLogLevel

func (h *FilterHandler) RemoveLogLevel(path string)

RemoveLogLevel removes a path-specific log level, falling back to default

func (*FilterHandler) SetDefaultLevel

func (h *FilterHandler) SetDefaultLevel(level slog.Level)

SetDefaultLevel changes the default log level at runtime

func (*FilterHandler) SetEnabled

func (h *FilterHandler) SetEnabled(enabled bool)

SetEnabled enables or disables all logging

func (*FilterHandler) SetExclusiveFiltering

func (h *FilterHandler) SetExclusiveFiltering(exclusive bool)

SetExclusiveFiltering enables or disables exclusive filtering mode at runtime

func (*FilterHandler) SetFilterTraceId

func (h *FilterHandler) SetFilterTraceId(traceId string)

SetFilterTraceId filters logs to only show those with the specified trace ID If set to empty string, disables trace ID filtering

func (*FilterHandler) SetLogLevel

func (h *FilterHandler) SetLogLevel(path string, level slog.Level)

SetLogLevel sets or updates the log level for a specific path at runtime

func (*FilterHandler) SetTraceIdKey

func (h *FilterHandler) SetTraceIdKey(key any)

SetTraceIdKey configures the key used to extract trace ID from context

func (*FilterHandler) WithAttrs

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

func (*FilterHandler) WithGroup

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

type FilterHandlerOpt

type FilterHandlerOpt func(*FilterHandler)

func WithDefaultLevel

func WithDefaultLevel(level slog.Level) FilterHandlerOpt

WithDefaultLevel sets the default log level for all packages

func WithExclusiveFiltering

func WithExclusiveFiltering() FilterHandlerOpt

WithExclusiveFiltering enables exclusive filtering mode. When enabled, only logs from paths that have explicit rules (set via WithLogLevel) will be shown. All other logs will be filtered out regardless of their level.

func WithLogLevel

func WithLogLevel(path string, level slog.Level) FilterHandlerOpt

WithLogLevel sets the log level for a specific package path

type Flusher

type Flusher interface {
	Flush() error
}

Flusher interface for handlers that need flushing.

type HttpHandler

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

func NewHttpHandler

func NewHttpHandler(log *FilterHandler) *HttpHandler

func (*HttpHandler) ServeHTTP

func (h *HttpHandler) ServeHTTP(w http.ResponseWriter, r *http.Request)

type LogEntry

type LogEntry struct {
	Time    time.Time              `json:"time"`
	Level   string                 `json:"level"`
	Message string                 `json:"msg"`
	Attrs   map[string]interface{} `json:"-"` // all other attributes
	Raw     json.RawMessage        `json:"-"` // the raw JSON for custom parsing
}

LogEntry represents a single log entry read from a file.

func SearchSession

func SearchSession(sessionPath string, criteria SearchCriteria, limit int) ([]LogEntry, error)

SearchSession searches a session for log entries matching the criteria.

func TailLogs

func TailLogs(sessionPath string, n int) ([]LogEntry, error)

TailLogs reads the last n log entries from a session.

type LogIterator

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

LogIterator provides an iterator interface for reading logs.

func NewLogIterator

func NewLogIterator(sessionPath string) (*LogIterator, error)

NewLogIterator creates a new iterator for reading logs sequentially.

func (*LogIterator) All

func (it *LogIterator) All() func(yield func(LogEntry) bool)

All returns an iterator function compatible with range loops. Usage: for entry := range iterator.All() { ... }

func (*LogIterator) Close

func (it *LogIterator) Close() error

Close closes the iterator and releases resources.

func (*LogIterator) Error

func (it *LogIterator) Error() error

Error returns any error that occurred during iteration.

func (*LogIterator) Next

func (it *LogIterator) Next() (LogEntry, bool)

Next returns the next log entry and a boolean indicating if there are more entries.

type LogPage

type LogPage struct {
	Entries    []LogEntry `json:"entries"`
	TotalCount int        `json:"total_count"` // total entries across all files (estimate)
	PageSize   int        `json:"page_size"`
	PageNumber int        `json:"page_number"`
	HasMore    bool       `json:"has_more"`
	FileIndex  int        `json:"file_index"`  // current file being read
	LineOffset int        `json:"line_offset"` // line offset within the file
}

LogPage represents a page of log entries with pagination info.

type LogReader

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

LogReader provides paginated access to log files in a session.

func NewLogReader

func NewLogReader(sessionPath string, pageSize int) (*LogReader, error)

NewLogReader creates a new LogReader for the given session directory.

func ReadLatestSession

func ReadLatestSession(baseDir string, pageSize int) (*LogReader, error)

ReadLatestSession reads logs from the most recent session.

func ReadSessionByName

func ReadSessionByName(baseDir, sessionName string, pageSize int) (*LogReader, error)

ReadSessionByName reads logs from a session by its name (timestamp string).

func (*LogReader) FileCount

func (r *LogReader) FileCount() int

FileCount returns the number of log files in the session.

func (*LogReader) ReadAll

func (r *LogReader) ReadAll() ([]LogEntry, error)

ReadAll reads all log entries from the session. Use with caution for large log files - prefer ReadPage for pagination.

func (*LogReader) ReadPage

func (r *LogReader) ReadPage(fileIndex, lineOffset int) (*LogPage, error)

ReadPage reads a page of log entries starting from the given position. fileIndex is 1-based (matching the file naming convention). lineOffset is the line number within the file (0-based).

func (*LogReader) StreamLogs

func (r *LogReader) StreamLogs(ctx context.Context) (<-chan LogEntry, <-chan error)

StreamLogs streams all log entries through a channel. The channel is closed when all entries have been read or an error occurs.

type LogSession

type LogSession struct {
	Name      string    // directory name (timestamp)
	Path      string    // full path to the session directory
	Timestamp time.Time // parsed timestamp
	FileCount int       // number of log files in the session
}

LogSession represents a logging session (a timestamp-based directory).

func ListSessions

func ListSessions(baseDir string) ([]LogSession, error)

ListSessions returns all logging sessions in the base directory. Sessions are sorted by timestamp, newest first.

type MultiHandler

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

MultiHandler combines multiple slog.Handlers, writing to all of them.

func NewMultiHandler

func NewMultiHandler(handlers ...slog.Handler) *MultiHandler

NewMultiHandler creates a handler that writes to multiple handlers. This is useful for writing logs to both console and file simultaneously.

func (*MultiHandler) Close

func (h *MultiHandler) Close() error

Close closes all handlers in a MultiHandler that implement Closer.

func (*MultiHandler) Enabled

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

func (*MultiHandler) Flush

func (h *MultiHandler) Flush() error

Flush flushes all handlers in a MultiHandler that implement Flusher.

func (*MultiHandler) Handle

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

func (*MultiHandler) WithAttrs

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

func (*MultiHandler) WithGroup

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

type SearchCriteria

type SearchCriteria struct {
	Level     string     // filter by log level (e.g., "ERROR", "WARN")
	Message   string     // substring match in message
	StartTime *time.Time // filter entries after this time
	EndTime   *time.Time // filter entries before this time
	AttrKey   string     // filter by attribute key presence
	AttrValue string     // filter by attribute value (requires AttrKey)
}

SearchLogs searches for log entries matching the given criteria.

Jump to

Keyboard shortcuts

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