logger

package module
v0.0.0-...-fd322cf Latest Latest
Warning

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

Go to latest
Published: Feb 17, 2026 License: MIT Imports: 16 Imported by: 2

README

Go Logger

Lightweight logger built on Go log/slog, with console/file output, structured fields, custom templates, middleware, context attributes, and file rotation.

Requires Go 1.25+

Features

  • Multi-output: Console & file with independent configuration
  • Flexible formats: Text, JSON, and custom template support
  • Smart coloring: ANSI colors for console (auto-disabled for files)
  • Auto-rotation: Size-based rotation with configurable retention and gzip compression
  • Structured logging: Full slog API with groups and attributes
  • Middleware: Composable handler middleware chain
  • Context attributes: Inject slog.Attr via context.Context
  • Dynamic level: Change log level at runtime via SetLevel()
  • Attribute transformation: Custom processing via WithReplaceAttr
  • Drop-in replacement: Embeds *slog.Logger + easy SetDefault()
  • Efficient runtime: Buffered file writes and optimized custom formatter path

Installation

go get github.com/simp-lee/logger

Quick Start

package main

import "github.com/simp-lee/logger"

func main() {
    log, err := logger.New()
    if err != nil {
        panic(err)
    }
    defer log.Close()

    log.Info("Hello, world!")
    log.Info("User login", "userId", 123, "username", "johndoe")
}

Configuration Options

All options are functional options passed to logger.New(...). Unspecified fields use defaults from DefaultConfig().

Behavior Notes (AI-Friendly)
  • Options are applied in order; later options override earlier ones.
  • WithFilePath(path) automatically enables file output.
  • If WithFile(true) is set without a path, New() returns an error.
  • If both console and file are disabled, logger creation still succeeds and falls back to a default slog.TextHandler on os.Stderr.
Global Options
Option Description Default
WithLevel(slog.Level) Minimum logging level slog.LevelInfo
WithLevelVar(*slog.LevelVar) Use a shared LevelVar for dynamic level control across loggers auto-created internally
WithAddSource(bool) Include source file information (filename:function:line) false
WithTimeFormat(string) Go time format string for timestamps "2006/01/02 15:04:05"
WithTimeZone(*time.Location) Time zone for timestamps time.Local
WithReplaceAttr(func) Custom attribute transformation function nil
WithMiddleware(...Middleware) Append handler middlewares (applied in order) nil
Console Options
Option Description Default
WithConsole(bool) Enable/disable console logging true
WithConsoleWriter(io.Writer) Console output destination os.Stderr
WithConsoleColor(bool) Enable ANSI colored output true
WithConsoleFormat(OutputFormat) Console log format (FormatText, FormatJSON, FormatCustom) FormatCustom
WithConsoleFormatter(string) Custom format template for console (auto-sets format to FormatCustom) "{time} {level} {message} {file} {attrs}"
File Options
Option Description Default
WithFile(bool) Enable/disable file logging (requires non-empty path when enabled) false
WithFilePath(string) Path to the log file (auto-enables file logging) ""
WithFileFormat(OutputFormat) File log format (FormatText, FormatJSON, FormatCustom) FormatCustom
WithFileFormatter(string) Custom format template for file (auto-sets format to FormatCustom) "{time} {level} {message} {file} {attrs}"
WithMaxSizeMB(int) Max file size in MB before rotation (0 = disable rotation, negative = default) 10
WithRetentionDays(int) Days to retain rotated files (<=0 resets to default) 7
WithCompressRotated(bool) Gzip compress rotated log files false
WithMaxBackups(int) Max number of rotated backups to keep (0 = unlimited) 0
Shorthand Options

Set both console and file configurations at once:

Option Description
WithFormat(OutputFormat) Set log format for both console and file
WithFormatter(string) Set custom template for both console and file (auto-sets format to FormatCustom)

Output Formats

Three built-in formats:

Format Constant Description
Text FormatText Standard slog.TextHandler output
JSON FormatJSON Standard slog.JSONHandler output
Custom FormatCustom Template-based format with placeholders
Custom Template Placeholders
Placeholder Content
{time} Timestamp (formatted by WithTimeFormat + WithTimeZone)
{level} Log level (DEBUG, INFO, WARN, ERROR)
{message} Log message
{file} Source location filename:function:line (requires WithAddSource(true))
{attrs} User attributes (key=value ...)

Empty placeholders (e.g. {file} without AddSource) are trimmed with surrounding whitespace automatically.

log, err := logger.New(
    logger.WithConsoleFormatter("{time} | {level} | {message} | {attrs}"),
)
if err != nil {
    panic(err)
}
defer log.Close()

Color Output

Console coloring (ANSI) is toggled with WithConsoleColor(true/false). File output never includes color codes.

Level color mapping: DEBUG = Bright Cyan, INFO = Green, WARN = Yellow, ERROR = Red (message text, error attribute key and value all emphasized).

Mixed Formats (Console vs File)

Formats are independent per output. Common pattern — human-readable console + structured file:

log, err := logger.New(
    logger.WithConsoleFormat(logger.FormatCustom),
    logger.WithConsoleFormatter("{time} {level} {message}"),
    logger.WithConsoleColor(true),
    logger.WithFilePath("./logs/app.log"),
    logger.WithFileFormat(logger.FormatJSON),
)
if err != nil {
    panic(err)
}
defer log.Close()

File Rotation & Retention

  • Trigger: Current file size exceeds WithMaxSizeMB(N) MB. Set 0 to disable rotation.
  • Naming: basename.YYYYMMDD.HHMMSS.mmm.ext (adds .counter suffix on collision).
  • Compression: Enable with WithCompressRotated(true) — rotated files are gzipped asynchronously (.gz suffix).
  • Retention: Files older than WithRetentionDays(D) are purged daily.
  • Backup limit: WithMaxBackups(N) caps the number of rotated files kept (oldest removed first). 0 = unlimited.
  • Lazy open: The log file is not opened until the first write, avoiding leaked file descriptors for unused loggers.
  • Buffered I/O: Writes go through a 64 KB buffer; call Sync() to flush explicitly.
log, err := logger.New(
    logger.WithFilePath("./logs/app.log"),
    logger.WithMaxSizeMB(50),
    logger.WithRetentionDays(30),
    logger.WithCompressRotated(true),
    logger.WithMaxBackups(10),
)
if err != nil {
    panic(err)
}
defer log.Close()

Middleware

Middleware is a func(slog.Handler) slog.Handler that wraps the final handler. Middlewares are applied in declaration order via WithMiddleware.

// Example: add a request ID to every log record
func RequestIDMiddleware(requestID string) logger.Middleware {
    return func(next slog.Handler) slog.Handler {
        return next.WithAttrs([]slog.Attr{slog.String("request_id", requestID)})
    }
}

log, err := logger.New(
    logger.WithMiddleware(RequestIDMiddleware("abc-123")),
)
Built-in Middleware: Context Attributes

ContextMiddleware() extracts slog.Attr values stored in a context.Context and appends them to each log record.

log, err := logger.New(
    logger.WithMiddleware(logger.ContextMiddleware()),
)
if err != nil {
    panic(err)
}
defer log.Close()

// Store attributes in context
ctx := logger.WithContextAttrs(context.Background(),
    slog.String("request_id", "req-001"),
    slog.String("user_id", "u-42"),
)

// Attributes are automatically included in log output
log.InfoContext(ctx, "processing request")

Context helpers:

Function Description
WithContextAttrs(ctx, ...slog.Attr) Returns a new context with the given attributes appended
FromContext(ctx) Extracts stored []slog.Attr from context (returns a copy)

Attribute Transformation (WithReplaceAttr)

Intercept and edit/remove attributes (built-in keys: time, level, msg, source, plus user attrs). Return an empty slog.Attr{} to drop an attribute.

log, err := logger.New(
    logger.WithReplaceAttr(func(groups []string, a slog.Attr) slog.Attr {
        if a.Key == "password" {
            return slog.String("password", "***")
        }
        return a
    }),
)

Logger Methods

Logger embeds *slog.Logger, so all standard slog methods (e.g. Info, Warn, Error, Debug, With, WithGroup, InfoContext, etc.) are available directly.

Additional methods on *Logger:

Method Description
Close() error Release all resources (files, goroutines, timers). Always defer this.
SetDefault() Set this logger as the default for slog.Info(), slog.Error(), etc.
SetLevel(slog.Level) Dynamically change the minimum log level at runtime. No-op for Default() loggers.
Rotate() error Trigger log file rotation immediately. No-op if file logging is not enabled.
Sync() error Flush buffered data to the underlying file. No-op if file logging is not enabled.
Constructors
Function Description
New(...Option) (*Logger, error) Create a configured logger with resource management. Recommended.
Default() *Logger Wrap slog.Default(). Does not support SetLevel(). No Close() needed.

Standard Library Integration

log, err := logger.New(logger.WithAddSource(true))
if err != nil {
    panic(err)
}
defer log.Close()

log.SetDefault()
slog.Info("routed through custom logger", "module", "auth")

Dynamic Log Level

log, err := logger.New(logger.WithLevel(slog.LevelInfo))
if err != nil {
    panic(err)
}
defer log.Close()

log.Debug("hidden")          // not printed (level is Info)
log.SetLevel(slog.LevelDebug)
log.Debug("now visible")     // printed

Complete Example

package main

import (
    "context"
    "log/slog"

    "github.com/simp-lee/logger"
)

func main() {
    log, err := logger.New(
        logger.WithLevel(slog.LevelDebug),
        logger.WithAddSource(true),
        logger.WithConsoleFormatter("{time} [{level}] {message} {attrs}"),
        logger.WithFilePath("./logs/app.log"),
        logger.WithFileFormat(logger.FormatJSON),
        logger.WithMaxSizeMB(50),
        logger.WithRetentionDays(30),
        logger.WithCompressRotated(true),
        logger.WithMiddleware(logger.ContextMiddleware()),
    )
    if err != nil {
        panic(err)
    }
    defer log.Close()

    log.SetDefault()

    log.Info("startup", "version", "1.0.0")

    ctx := logger.WithContextAttrs(context.Background(),
        slog.String("request_id", "req-001"),
    )
    log.InfoContext(ctx, "handling request", "path", "/api/users")

    userLogger := log.WithGroup("user")
    userLogger.Info("login", "id", 42)
}

API Quick Reference

# Constructors
logger.New(opts ...Option) (*Logger, error)
logger.Default() *Logger

# Logger methods (in addition to embedded *slog.Logger)
(*Logger) Close() error
(*Logger) SetDefault()
(*Logger) SetLevel(slog.Level)
(*Logger) Rotate() error
(*Logger) Sync() error

# Global options
WithLevel(slog.Level)                          WithLevelVar(*slog.LevelVar)
WithAddSource(bool)                            WithTimeFormat(string)
WithTimeZone(*time.Location)                   WithReplaceAttr(func([]string, slog.Attr) slog.Attr)
WithMiddleware(...Middleware)

# Console options
WithConsole(bool)                              WithConsoleWriter(io.Writer)
WithConsoleColor(bool)                         WithConsoleFormat(OutputFormat)
WithConsoleFormatter(string)

# File options
WithFile(bool)                                 WithFilePath(string)
WithFileFormat(OutputFormat)                    WithFileFormatter(string)
WithMaxSizeMB(int)                             WithRetentionDays(int)
WithCompressRotated(bool)                      WithMaxBackups(int)

# Shorthand options
WithFormat(OutputFormat)                        WithFormatter(string)

# Output formats
FormatText   FormatJSON   FormatCustom

# Context helpers
WithContextAttrs(ctx, ...slog.Attr) context.Context
FromContext(ctx) []slog.Attr

# Built-in middleware
ContextMiddleware() Middleware

# Types
type Middleware func(slog.Handler) slog.Handler
type OutputFormat string   // "text" | "json" | "custom"

License

MIT License

Documentation

Index

Constants

View Source
const (
	FormatText   OutputFormat = "text"
	FormatJSON   OutputFormat = "json"
	FormatCustom OutputFormat = "custom"

	DefaultTimeFormat    = "2006/01/02 15:04:05"
	DefaultMaxSizeMB     = 10
	DefaultRetentionDays = 7
	DefaultFormatter     = "{time} {level} {message} {file} {attrs}"
	DefaultFormat        = FormatText
)
View Source
const (
	// Placeholders
	PlaceholderTime    = "{time}"
	PlaceholderLevel   = "{level}"
	PlaceholderMessage = "{message}"
	PlaceholderFile    = "{file}"
	PlaceholderAttrs   = "{attrs}"
)

Variables

This section is empty.

Functions

func FromContext

func FromContext(ctx context.Context) []slog.Attr

func WithContextAttrs

func WithContextAttrs(ctx context.Context, attrs ...slog.Attr) context.Context

Types

type Config

type Config struct {
	// Base configuration
	Level slog.Level

	AddSource  bool
	TimeFormat string
	TimeZone   *time.Location

	// Configurations for different log destinations
	Console ConsoleConfig
	File    FileConfig

	// ReplaceAttr is a function that can be used to replace attributes in log messages
	ReplaceAttr func(groups []string, a slog.Attr) slog.Attr

	// Middlewares wraps the final handler in declaration order
	Middlewares []Middleware
	// contains filtered or unexported fields
}

func DefaultConfig

func DefaultConfig() *Config

type ConsoleConfig

type ConsoleConfig struct {
	Enabled   bool         // Enable console logging
	Writer    io.Writer    // Console output destination
	Color     bool         // Enable colorized output
	Format    OutputFormat // text, json, custom
	Formatter string       // Custom formatter string, only used if Format is FormatCustom
}

func (*ConsoleConfig) GetColor

func (c *ConsoleConfig) GetColor() bool

func (*ConsoleConfig) GetFormat

func (c *ConsoleConfig) GetFormat() OutputFormat

ConsoleConfig implements outputConfig interface

func (*ConsoleConfig) GetFormatter

func (c *ConsoleConfig) GetFormatter() string

type FileConfig

type FileConfig struct {
	Enabled       bool
	Format        OutputFormat
	Formatter     string // Custom formatter string, only used if Format is FormatCustom
	Path          string // Path to the log file
	MaxSizeMB     int    // Maximum size of the log file in megabytes
	RetentionDays int    // Number of days to retain log files
	Compress      bool   // Compress rotated log files with gzip
	MaxBackups    int    // Maximum number of rotated backups to keep, 0 means unlimited
}

func (*FileConfig) GetColor

func (c *FileConfig) GetColor() bool

func (*FileConfig) GetFormat

func (c *FileConfig) GetFormat() OutputFormat

FileConfig implements outputConfig interface

func (*FileConfig) GetFormatter

func (c *FileConfig) GetFormatter() string

type Logger

type Logger struct {
	*slog.Logger
	// contains filtered or unexported fields
}

Logger wraps slog.Logger with automatic resource management By embedding *slog.Logger, it inherits all methods like Info, Error, Debug, Warn, With, WithGroup, etc.

func Default

func Default() *Logger

Default returns a new Logger using the default slog configuration. Note: The returned Logger does not support dynamic level changes via SetLevel().

func New

func New(opts ...Option) (*Logger, error)

New creates a new Logger with automatic resource cleanup This is the recommended way to create a logger

func (*Logger) Close

func (l *Logger) Close() error

Close cleans up any resources held by the logger Always call this when you're done with the logger to prevent resource leaks

func (*Logger) Rotate

func (l *Logger) Rotate() error

func (*Logger) SetDefault

func (l *Logger) SetDefault()

SetDefault sets the current logger as the default logger After setting, standard library calls like slog.Info(), slog.Error() will use this logger Note: Since Logger embeds *slog.Logger, the following methods can be used directly without additional implementation: - With(args ...any): Adds attributes and returns a new Logger - WithGroup(name string): Adds a group and returns a new Logger - Info, Warn, Error, Debug and other log level methods

func (*Logger) SetLevel

func (l *Logger) SetLevel(level slog.Level)

SetLevel dynamically changes the logger's minimum level. This only works when the logger was created with New() (which always provisions a LevelVar). For loggers obtained via Default(), this is a no-op.

func (*Logger) Sync

func (l *Logger) Sync() error

Sync flushes any buffered log data to the underlying file.

type Middleware

type Middleware func(slog.Handler) slog.Handler

func ContextMiddleware

func ContextMiddleware() Middleware

type Option

type Option func(*Config)

Option is a function that modifies a Config

func WithAddSource

func WithAddSource(addSource bool) Option

func WithCompressRotated

func WithCompressRotated(compress bool) Option

func WithConsole

func WithConsole(enabled bool) Option

func WithConsoleColor

func WithConsoleColor(enabled bool) Option

func WithConsoleFormat

func WithConsoleFormat(format OutputFormat) Option

func WithConsoleFormatter

func WithConsoleFormatter(formatter string) Option

WithConsoleFormatter sets the console formatter for logging, and automatically sets the format to FormatCustom The formatter string can contain the following placeholders: - {time}: The timestamp of the log message - {level}: The log level of the message - {message}: The log message - {file}: The source file where the log message was generated - {attrs}: Any additional attributes associated with the log message For example: "{time} [{level}] {file} {message} {attrs}"

func WithConsoleWriter

func WithConsoleWriter(w io.Writer) Option

func WithFile

func WithFile(enabled bool) Option

func WithFileFormat

func WithFileFormat(format OutputFormat) Option

func WithFileFormatter

func WithFileFormatter(formatter string) Option

func WithFilePath

func WithFilePath(path string) Option

func WithFormat

func WithFormat(format OutputFormat) Option

WithFormat sets the format of the log message for both console and file logging

func WithFormatter

func WithFormatter(formatter string) Option

WithFormatter sets the formatter for both console and file logging, and automatically sets the format to FormatCustom

func WithLevel

func WithLevel(level slog.Level) Option

func WithLevelVar

func WithLevelVar(lv *slog.LevelVar) Option

func WithMaxBackups

func WithMaxBackups(n int) Option

func WithMaxSizeMB

func WithMaxSizeMB(maxSizeMB int) Option

WithMaxSizeMB sets the maximum size of the log file in megabytes. Set to 0 to disable file rotation. Negative values will be reset to the default.

func WithMiddleware

func WithMiddleware(mws ...Middleware) Option

func WithReplaceAttr

func WithReplaceAttr(replaceAttr func(groups []string, a slog.Attr) slog.Attr) Option

WithReplaceAttr sets a function that can be used to replace attributes in log messages

func WithRetentionDays

func WithRetentionDays(retentionDays int) Option

WithRetentionDays sets the number of days to retain log files

func WithTimeFormat

func WithTimeFormat(timeFormat string) Option

func WithTimeZone

func WithTimeZone(timeZone *time.Location) Option

type OutputFormat

type OutputFormat string

type ParsedTemplate

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

ParsedTemplate holds the pre-parsed template tokens

type Rotator

type Rotator interface {
	Rotate() error
}

type Syncer

type Syncer interface {
	Sync() error
}

Syncer is an interface for types that can flush buffered data.

type Token

type Token struct {
	Type TokenType
	Text string // For static text tokens
}

Token represents a parsed template component

type TokenType

type TokenType int

TokenType represents the type of a template token

const (
	TokenTypeText TokenType = iota
	TokenTypeTime
	TokenTypeLevel
	TokenTypeMessage
	TokenTypeFile
	TokenTypeAttrs
)

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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