slogs

package module
v0.0.3 Latest Latest
Warning

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

Go to latest
Published: Jul 31, 2026 License: MIT Imports: 14 Imported by: 0

README

slogs

中文文档

A lightweight, zero-external-dependency unified logging package built on Go's standard log/slog (Go 1.21+), providing structured logging, file rotation, multi-target output, and named logger management.

Features

  • Zero external dependencies — Built on log/slog (Go 1.21+ stdlib); file rotation is vendored internally (adapted from lumberjack, MIT License)
  • Dual-target output — Console (stderr) + file, dispatched via a built-in fan-out multiHandler
  • File rotation — Built-in Rotator with size/count/age-based rolling + gzip compression
  • Structured attributesWith() attaches key-value context across all output targets
  • Named loggersManager manages multiple isolated named logger instances
  • Global default — Lazy initialization + package-level convenience functions, ready out of the box
  • Thread-safe — All exported types and functions are safe for concurrent use

Installation

go get github.com/winezer0/slogs

Quick Start

Package-level Functions (Zero Config)
import "github.com/winezer0/slogs"

slogs.Info("server started", "port", 8080)
slogs.Errorf("connection failed: %v", err)
Explicit Initialization
cfg := slogs.NewConfig("debug", "logs/app.log", "json")
if err := slogs.Init(cfg); err != nil {
    log.Fatal(err)
}
slogs.Debug("initialized with file output")
Standalone Logger Instance
cfg := slogs.Config{
    Level:      "info",
    Format:     "text",
    FilePath:   "logs/audit.log",
    MaxSize:    50,
    MaxBackups: 5,
    MaxAge:     14,
    Compress:   true,
}
logger, err := slogs.NewLogger(cfg)
if err != nil {
    log.Fatal(err)
}
defer logger.Close()

// Structured attributes
reqLogger := logger.With("request_id", "abc-123")
reqLogger.Info("processing request", "method", "GET", "path", "/api/scan")
Named Loggers (Multi-module Isolation)
// Create
scanLogger, _ := slogs.CreateLogger("scanengine", slogs.NewConfig("info", "logs/scan.log", "json"))
auditLogger, _ := slogs.CreateLogger("auditflow", slogs.NewConfig("debug", "logs/audit.log", "json"))

// Retrieve
logger, ok := slogs.GetLogger("scanengine")

// Close all on shutdown
slogs.CloseAll()
Access Underlying slog.Logger
logger := slogs.Default()
slogLogger := logger.Slog() // *slog.Logger, can be passed to frameworks like eino

Configuration

logging:
  level: info          # debug | info | warn | error
  format: text         # text (human-readable) | json (structured)
  file_path: ""        # log file path; empty = console only
  max_size: 100        # max megabytes per file before rotation
  max_backups: 3       # max number of old files to retain
  max_age: 30          # max days to retain old files
  compress: true       # gzip compress rotated files
Field Type Default Description
Level string "info" Minimum log level: debug, info, warn, error
Format string "text" Console and file output format: text or json
FilePath string "" Log file path; empty disables file output
MaxSize int 100 Max MB per file before rotation
MaxBackups int 3 Max old files to retain
MaxAge int 30 Max days to retain old files
Compress bool true Gzip compress rotated files

Log Levels

Level Value Purpose
DEBUG -4 Development debug info
INFO 0 Normal operational status
WARN 4 Recoverable anomalies / degradation
ERROR 8 Errors requiring attention

Output Format

Console text mode (stderr):

time=2026-07-27T18:00:00.000+08:00 level=INFO source=main.go:42 msg="server started" port=8080

Console json mode (stderr):

{"time":"2026-07-27T18:00:00.000+08:00","level":"INFO","source":{"function":"main.main","file":"main.go","line":42},"msg":"server started","port":8080}

File output (format follows Config.Format):

{"time":"2026-07-27T18:00:00.000+08:00","level":"INFO","source":{"function":"main.main","file":"main.go","line":42},"msg":"server started","port":8080}

File Structure

File Responsibility
doc.go Package-level documentation
config.go Config struct, defaults, factory functions
logger.go Logger wrapper, multiHandler, level parsing, file handler
default.go Global default logger + package-level functions
manager.go Named logger registry (create/get/close-all)
rotator.go Built-in log file rotator (adapted from lumberjack)
chown.go No-op chown for non-Linux platforms
chown_linux.go Preserve file ownership on Linux

Dependencies

  • log/slog — Go standard library
  • No third-party dependencies (file rotation is vendored in rotator.go, adapted from lumberjack MIT License)

Testing

go test ./... -v -cover

API Documentation

Full API documentation is available at pkg.go.dev/github.com/winezer0/slogs.

License

MIT

Documentation

Overview

Package slogs provides a lightweight, zero-external-dependency unified logging solution built on Go's standard log/slog package (Go 1.21+).

Features

  • Dual-target output: console (stderr) and file (JSON), dispatched via a built-in fan-out multiHandler.
  • File rotation: built-in Rotator (adapted from lumberjack, MIT License) supports size-based rolling, backup retention, age-based cleanup, and gzip compression.
  • Structured attributes: Logger.With attaches key-value context that propagates to all output targets.
  • Named loggers: package-level CreateLogger/GetLogger manage multiple isolated logger instances via a singleton Manager.
  • Global default: lazy-initialized default logger with package-level convenience functions (Info, Debug, Error, etc.).

Quick Start

Zero-configuration usage with the global default logger:

slogs.Info("server started", "port", 8080)
slogs.Errorf("connection failed: %v", err)

Explicit initialization with file output:

cfg := slogs.NewConfig("debug", "logs/app.log", "json")
if err := slogs.Init(cfg); err != nil {
    log.Fatal(err)
}

Standalone logger instance:

logger, err := slogs.NewLogger(slogs.Config{
    Level:    "info",
    FilePath: "logs/audit.log",
    MaxSize:  50,
})
if err != nil {
    log.Fatal(err)
}
defer logger.Close()
logger.Info("audit event", "user", "admin")

Named loggers for multi-module isolation:

scanLog, _ := slogs.CreateLogger("scan", slogs.NewConfig("info", "logs/scan.log", "json"))
auditLog, _ := slogs.CreateLogger("audit", slogs.NewConfig("debug", "logs/audit.log", "json"))
defer slogs.CloseAll()

Configuration

The Config struct controls logging behavior:

  • Level: minimum log level ("debug", "info", "warn", "error").
  • Format: console format ("text" or "json").
  • FilePath: log file path; empty disables file output.
  • MaxSize: max megabytes per file before rotation (default 100).
  • MaxBackups: max old files to retain (default 3).
  • MaxAge: max days to retain old files (default 30).
  • Compress: gzip-compress rotated files (default true).

Thread Safety

All exported types and functions are safe for concurrent use. The Manager uses a read-write mutex; Logger and Rotator use internal synchronization.

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func CloseAll

func CloseAll() error

CloseAll closes all managed loggers, closes the default logger, and resets the registry.

func Debug

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

Debug logs at debug level using the default logger.

func Debugf

func Debugf(template string, args ...any)

Debugf logs a formatted message at debug level using the default logger.

func Error

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

Error logs at error level using the default logger.

func Errorf

func Errorf(template string, args ...any)

Errorf logs a formatted message at error level using the default logger.

func Info

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

Info logs at info level using the default logger.

func Infof

func Infof(template string, args ...any)

Infof logs a formatted message at info level using the default logger.

func Init

func Init(config Config) error

Init initializes the global default logger with the given configuration. Once the default logger has been initialized (either via Init or lazy init), subsequent calls are no-ops and return nil.

Example

ExampleInit demonstrates initializing the global default logger with file output.

package main

import (
	"fmt"
	"os"
	"path/filepath"

	"github.com/winezer0/slogs"
)

func main() {
	dir, _ := os.MkdirTemp("", "slogs-example")
	defer os.RemoveAll(dir)

	cfg := slogs.NewConfig("debug", filepath.Join(dir, "app.log"), "json")
	if err := slogs.Init(cfg); err != nil {
		fmt.Println("init error:", err)
		return
	}
	slogs.Debug("initialized with file output")
	// Output is written to stderr and the log file.
}

func RemoveLogger added in v0.0.3

func RemoveLogger(name string) error

RemoveLogger removes and closes a named logger from the registry.

func SetDefault

func SetDefault(logger *Logger)

SetDefault replaces the global default logger (useful for testing or late configuration). The previous default logger is closed to prevent resource leaks.

Example

ExampleSetDefault demonstrates replacing the global default logger.

package main

import (
	"fmt"
	"os"
	"path/filepath"

	"github.com/winezer0/slogs"
)

func main() {
	dir, _ := os.MkdirTemp("", "slogs-example")
	defer os.RemoveAll(dir)

	cfg := slogs.NewConfig("warn", filepath.Join(dir, "warn.log"), "json")
	logger, err := slogs.NewLogger(cfg)
	if err != nil {
		fmt.Println("error:", err)
		return
	}
	defer logger.Close()

	slogs.SetDefault(logger)
	// Now package-level functions use the new logger.
	slogs.Warn("disk usage high", "percent", 92)
}

func Warn

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

Warn logs at warn level using the default logger.

func Warnf

func Warnf(template string, args ...any)

Warnf logs a formatted message at warn level using the default logger.

Types

type Config

type Config struct {
	// Level is the minimum log level: debug, info, warn, error.
	Level string `yaml:"level"`
	// Format is the console output format: "text" (human-readable) or "json" (structured).
	Format string `yaml:"format"`
	// FilePath is the log file path; empty means no file output.
	FilePath string `yaml:"file_path"`
	// MaxSize is the maximum size in megabytes of a single log file before rotation.
	MaxSize int `yaml:"max_size"`
	// MaxBackups is the maximum number of old log files to retain.
	MaxBackups int `yaml:"max_backups"`
	// MaxAge is the maximum number of days to retain old log files.
	MaxAge int `yaml:"max_age"`
	// Compress determines whether rotated files are compressed.
	Compress bool `yaml:"compress"`
}

Config holds logging configuration.

func DefaultConfig

func DefaultConfig() Config

DefaultConfig returns a sensible default configuration (info level, text console, no file).

Example

ExampleDefaultConfig demonstrates obtaining the default configuration.

package main

import (
	"fmt"

	"github.com/winezer0/slogs"
)

func main() {
	cfg := slogs.DefaultConfig()
	fmt.Println(cfg.Level)
	fmt.Println(cfg.Format)
	fmt.Println(cfg.MaxSize)
}
Output:
info
text
100

func NewConfig

func NewConfig(level, filePath, format string) Config

NewConfig creates a Config with the given level, file path, and format, applying defaults for rotation parameters.

Example

ExampleNewConfig demonstrates creating a configuration with custom parameters.

package main

import (
	"fmt"

	"github.com/winezer0/slogs"
)

func main() {
	cfg := slogs.NewConfig("error", "/var/log/myapp.log", "json")
	fmt.Println(cfg.Level)
	fmt.Println(cfg.FilePath)
	fmt.Println(cfg.Compress)
}
Output:
error
/var/log/myapp.log
true

type Logger

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

Logger wraps slog.Logger with configuration and lifecycle management.

func CreateLogger

func CreateLogger(name string, config Config) (*Logger, error)

CreateLogger creates and registers a named logger.

Example

ExampleCreateLogger demonstrates creating and retrieving named loggers.

package main

import (
	"fmt"
	"os"
	"path/filepath"

	"github.com/winezer0/slogs"
)

func main() {
	dir, _ := os.MkdirTemp("", "slogs-example")
	defer os.RemoveAll(dir)
	defer slogs.CloseAll()

	_, err := slogs.CreateLogger("scan", slogs.NewConfig("info", filepath.Join(dir, "scan.log"), "json"))
	if err != nil {
		fmt.Println("error:", err)
		return
	}

	logger, ok := slogs.GetLogger("scan")
	fmt.Println("retrieved:", ok)
	logger.Info("scan completed", "targets", 42)
}
Output:
retrieved: true

func Default

func Default() *Logger

Default returns the global default logger, initializing it if necessary.

Example

ExampleDefault demonstrates using the global default logger with zero configuration.

package main

import (
	"github.com/winezer0/slogs"
)

func main() {
	logger := slogs.Default()
	logger.Info("using default logger", "version", "1.0.0")
}

func GetLogger

func GetLogger(name string) (*Logger, bool)

GetLogger retrieves a previously created named logger.

func NewLogger

func NewLogger(config Config) (*Logger, error)

NewLogger creates a Logger from the given configuration. Console output goes to stderr; file output (if configured) uses JSON with rotation.

Example

ExampleNewLogger demonstrates creating a standalone logger instance.

package main

import (
	"fmt"
	"os"
	"path/filepath"

	"github.com/winezer0/slogs"
)

func main() {
	dir, _ := os.MkdirTemp("", "slogs-example")
	defer os.RemoveAll(dir)

	cfg := slogs.Config{
		Level:      "info",
		Format:     "text",
		FilePath:   filepath.Join(dir, "audit.log"),
		MaxSize:    50,
		MaxBackups: 5,
		MaxAge:     14,
		Compress:   true,
	}
	logger, err := slogs.NewLogger(cfg)
	if err != nil {
		fmt.Println("error:", err)
		return
	}
	defer logger.Close()

	logger.Info("audit event", "user", "admin", "action", "login")
}

func (*Logger) Close

func (l *Logger) Close() error

Close flushes and releases file resources.

func (*Logger) Debug

func (l *Logger) Debug(msg string, args ...any)

Debug logs at debug level.

func (*Logger) Debugf

func (l *Logger) Debugf(template string, args ...any)

Debugf logs a formatted message at debug level.

func (*Logger) Error

func (l *Logger) Error(msg string, args ...any)

Error logs at error level.

func (*Logger) Errorf

func (l *Logger) Errorf(template string, args ...any)

Errorf logs a formatted message at error level.

func (*Logger) Info

func (l *Logger) Info(msg string, args ...any)

Info logs at info level.

func (*Logger) Infof

func (l *Logger) Infof(template string, args ...any)

Infof logs a formatted message at info level.

func (*Logger) Slog

func (l *Logger) Slog() *slog.Logger

Slog returns the underlying *slog.Logger for direct use.

func (*Logger) Warn

func (l *Logger) Warn(msg string, args ...any)

Warn logs at warn level.

func (*Logger) Warnf

func (l *Logger) Warnf(template string, args ...any)

Warnf logs a formatted message at warn level.

func (*Logger) With

func (l *Logger) With(args ...any) *Logger

With returns a Logger with the given attributes attached.

Example

ExampleLogger_With demonstrates attaching structured attributes to a logger.

package main

import (
	"github.com/winezer0/slogs"
)

func main() {
	logger := slogs.Default()
	reqLogger := logger.With("request_id", "abc-123", "service", "gateway")
	reqLogger.Info("processing request", "method", "GET", "path", "/api/users")
}

type Manager

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

Manager manages named logger instances.

func (*Manager) CloseAll

func (m *Manager) CloseAll() error

CloseAll closes all managed loggers, closes the default logger, and clears the registry.

func (*Manager) Create

func (m *Manager) Create(name string, config Config) (*Logger, error)

Create creates and registers a named logger.

func (*Manager) Get

func (m *Manager) Get(name string) (*Logger, bool)

Get retrieves a named logger.

func (*Manager) Remove added in v0.0.3

func (m *Manager) Remove(name string) error

Remove removes and closes a named logger from the registry. Returns an error if the logger does not exist.

type Rotator

type Rotator struct {
	// Filename is the file to write logs to. Backup log files will be retained
	// in the same directory.
	Filename string

	// MaxSize is the maximum size in megabytes of the log file before rotation.
	// Defaults to 100 megabytes.
	MaxSize int

	// MaxAge is the maximum number of days to retain old log files.
	MaxAge int

	// MaxBackups is the maximum number of old log files to retain.
	MaxBackups int

	// LocalTime determines if the time used for formatting the timestamps in
	// backup files is the computer's local time. Default is UTC.
	LocalTime bool

	// Compress determines if rotated log files should be compressed using gzip.
	Compress bool
	// contains filtered or unexported fields
}

Rotator is an io.WriteCloser that writes to a rotating log file.

Rotator opens or creates the logfile on first Write. If the file exists and is less than MaxSize megabytes, it will open and append to that file. If the file exists and its size is >= MaxSize megabytes, the file is renamed by putting the current time in a timestamp in the name immediately before the file's extension. A new log file is then created using original filename.

Backups use the form `name-timestamp.ext` where timestamp is formatted with `2006-01-02T15-04-05.000`.

func (*Rotator) Close

func (r *Rotator) Close() error

Close implements io.Closer, and closes the current logfile.

func (*Rotator) Rotate

func (r *Rotator) Rotate() error

Rotate causes Rotator to close the existing log file and immediately create a new one. After rotating, this initiates compression and removal of old log files according to the configuration.

func (*Rotator) Write

func (r *Rotator) Write(p []byte) (n int, err error)

Write implements io.Writer. If a write would cause the log file to exceed MaxSize, the file is closed, renamed with a timestamp, and a new file created.

Jump to

Keyboard shortcuts

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