dlog

package module
v1.0.1 Latest Latest
Warning

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

Go to latest
Published: Mar 5, 2026 License: MIT Imports: 11 Imported by: 2

README

dlog

codecov Go Reference

A simple Go's log/slog wrapper designed to provide debug logging capable of dual output (stdout and file).

Features

  • Generic API: Fully wraps standard log/slog types while giving a simplified DebugLogger abstraction
  • Zero Friction: Clean functional options without disrupting typical slog idioms
  • Dual Output: Write logs simultaneously to stdout and to a file
  • Multiple Formats: Supports json, text, logfmt, and an optimized logstash JSON format
  • Configurable Sync Modes: Fine-tune file I/O performance via SyncImmediate, SyncBuffered, or SyncPeriodic background flushing
  • Field Filtering: Include or exclude specific fields dynamically to manage data loss vs privacy
  • Zero Overhead when Disabled: Provides a fast NoOpLogger fallback when disabled

Installation

go get github.com/rohmanhakim/dlog

Quick Start

package main

import (
    "context"
    "log/slog"
    "time"
    
    "github.com/rohmanhakim/dlog"
)

func main() {
    // Execute with initialized slog wrapper options
    logger, err := dlog.NewSlogLogger(
        true, // enabled
        dlog.FormatLogstash, // specify log format
        dlog.WithOutputFile("debug.log"), // optional log file, will use stdout-only if not specified
        dlog.WithMinLevel(dlog.LevelDebug),
        dlog.WithSyncMode(dlog.SyncPeriodic),
        dlog.WithSyncInterval(1*time.Second),
    )
    if err != nil {
        panic(err)
    }
    defer logger.Close()
    
    ctx := context.Background()

    // Straightforward logging
    logger.LogInfo(ctx, "App started", dlog.FieldMap{"version": "1.0.0"})

    // Persistent context injection
    reqLogger := logger.
        WithGroup("request").
        WithFields(dlog.FieldMap{"id": "req-123"})
        
    reqLogger.LogDebug(ctx, "processing payload", dlog.FieldMap{"size": 1024})
}

Configuration Options

Functional Options

The NewSlogLogger function accepts multiple functional options. All options have sensible defaults:

Option Description Default
WithMinLevel(level Level) Set the minimum log level LevelDebug
WithIncludeFields(fields []string) Only output explicitly listed fields Empty list (include all)
WithExcludeFields(fields []string) Strip sensitive fields from output Empty list (exclude none)
WithFields(fields FieldMap) Pre-populate the logger with persistent fields nil
WithGroup(name string) Prefix subsequent attributes with a group name ""
WithOutputFile(path string) Enable file output in addition to stdout "" (stdout only)
WithSyncMode(mode SyncMode) Control file buffering logic (see Sync Modes below) SyncImmediate
WithSyncInterval(interval time.Duration) Override flushing interval for SyncPeriodic 1 second

Logging Handlers

Formats

dlog is capable of rendering logs into a variety of formats using the Format type enum as an argument in NewSlogLogger.

Format Description
FormatJSON Standard slog nested format (time, level, msg).
FormatText Human-readable output format ideal for local debug.
FormatLogfmt Standard key=value machine-readable output.
FormatLogstash Highly optimized flatten structure substituting variables (e.g. @timestamp, log.level).
Field Filtering

Filter noisy attributes directly without parsing intermediate payloads. Excludes take precedence over Includes.

logger, _ := dlog.NewSlogLogger(
    true, dlog.FormatJSON,
    dlog.WithExcludeFields([]string{"password", "token"}),
)
// Result: `password` and `token` attributes are quietly dropped from final output.

Sync Modes

When writing to a file, I/O efficiency matters. Use SyncMode to control buffering:

Mode Description Profile
SyncImmediate Flushes to disk synchronously after every log line. Scripts, low volume applications
SyncBuffered Relies on default sizes (bufio). Flushes on Close(). Burst workloads without losing output
SyncPeriodic Triggers a background goroutine to Flush() at fixed intervals. Extremely high throughput services

API Reference

Types
// DebugLogger provides structured debug logging capabilities.
// All methods are no-ops when debug mode is disabled.
type DebugLogger interface {
    Enabled() bool
    LogDebug(ctx context.Context, message string, fieldMap ...FieldMap)
    LogInfo(ctx context.Context, message string, fieldMap ...FieldMap)
    LogWarn(ctx context.Context, message string, fieldMap ...FieldMap)
    LogError(ctx context.Context, message string, err error, fieldMap ...FieldMap)
    WithFields(fields FieldMap) DebugLogger
    WithGroup(name string) DebugLogger
    Close() error
}

// FieldMap is a map of structured field names to values.
type FieldMap map[string]any

// Format represents the output format for debug logging.
type Format string

// SyncMode determines when file writes are flushed to disk.
type SyncMode int
Functions
// Creates a new SlogLogger with the given configuration
func NewSlogLogger(enabled bool, format Format, opts ...Option) (DebugLogger, error)

// Creates a NoOpLogger
func NewNoOpLogger() DebugLogger

// Level constants
const (
    LevelDebug Level = -4
    LevelInfo  Level = 0
    LevelWarn  Level = 4
    LevelError Level = 8
)

// Functional options
func WithMinLevel(level Level) Option
func WithIncludeFields(fields []string) Option
func WithExcludeFields(fields []string) Option
func WithFields(fields FieldMap) Option
func WithGroup(name string) Option
func WithOutputFile(path string) Option
func WithSyncMode(mode SyncMode) Option
func WithSyncInterval(interval time.Duration) Option

License

MIT License - see LICENSE for details.

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func FilterFields

func FilterFields(entry map[string]any, includeFields, excludeFields []string) map[string]any

FilterFields applies include/exclude field filtering to a log entry. If both IncludeFields and ExcludeFields are specified, exclude takes precedence.

Types

type DebugLogger

type DebugLogger interface {
	// Enabled returns true if debug logging is enabled.
	// When false, the logger will skip logging entirely for efficiency.
	Enabled() bool

	// LogDebug logs string message with slog.Level = LevelDebug.
	LogDebug(ctx context.Context, message string, fieldMap ...FieldMap)

	// LogInfo logs string message with slog.Level = LevelInfo.
	LogInfo(ctx context.Context, message string, fieldMap ...FieldMap)

	// LogWarn logs string message with slog.Level = LevelWarn.
	LogWarn(ctx context.Context, message string, fieldMap ...FieldMap)

	// LogError logs string message with slog.level = LevelError.
	LogError(ctx context.Context, message string, err error, fieldMap ...FieldMap)

	// WithFields returns a logger with pre-populated fields.
	WithFields(fields FieldMap) DebugLogger

	// WithGroup returns a logger with all subsequent attributes grouped under the given name.
	WithGroup(name string) DebugLogger

	// Close flushes any buffered output and closes file handles.
	Close() error
}

DebugLogger provides structured debug logging capabilities. All methods are no-ops when debug mode is disabled.

func NewSlogLogger

func NewSlogLogger(enabled bool, format Format, opts ...Option) (DebugLogger, error)

NewSlogLogger creates a new SlogLogger with the given configuration. If enabled is false, a NoOpLogger is returned instead. Optional parameters can be provided using WithMinLevel, WithIncludeFields, WithExcludeFields, and WithOutputFile.

type FieldMap

type FieldMap map[string]any

FieldMap is a map of structured field names to values.

type Format

type Format string

Format represents the output format for debug logging.

const (
	// FormatJSON outputs logs in standard JSON format using slog.JSONHandler.
	// Fields: time, level, msg with nested group objects.
	FormatJSON Format = "json"
	// FormatText outputs logs in human-readable text format.
	FormatText Format = "text"
	// FormatLogfmt outputs logs in logfmt format (key=value pairs).
	FormatLogfmt Format = "logfmt"
	// FormatLogstash outputs logs in Logstash/Elasticsearch compatible JSON format.
	// Fields: @timestamp, log.level, message with flattened group keys (e.g., request.id).
	FormatLogstash Format = "logstash"
)

type HandlerOptions

type HandlerOptions struct {
	// Level is the minimum log level to output.
	Level Level

	// IncludeFields filters fields to include (empty = all).
	// When specified, only fields in this list will be included in the output.
	// Built-in fields like @timestamp, @version, level, message, thread_name
	// should be included explicitly if you want them in the output.
	IncludeFields []string

	// ExcludeFields filters fields to exclude from the output.
	// Useful for removing sensitive data like passwords or API keys.
	ExcludeFields []string
}

HandlerOptions configures handler behavior for all output formats. It provides a unified configuration interface for TextHandler, LogfmtHandler, and LogstashHandler.

type Level added in v1.0.1

type Level int

Level represents a log level. It wraps slog.Level to provide a cleaner API without exposing slog directly.

const (
	// LevelDebug is the debug log level.
	LevelDebug Level = -4
	// LevelInfo is the info log level.
	LevelInfo Level = 0
	// LevelWarn is the warn log level.
	LevelWarn Level = 4
	// LevelError is the error log level.
	LevelError Level = 8
)

type LogfmtHandler

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

LogfmtHandler creates logfmt-formatted output. Logfmt is a key=value format that is both human-readable and machine-parseable.

Implements slog.Handler interface.

func NewLogfmtHandler

func NewLogfmtHandler(w io.Writer, opts *HandlerOptions) *LogfmtHandler

NewLogfmtHandler creates a new LogfmtHandler writing to the specified writer.

func (*LogfmtHandler) Enabled

func (h *LogfmtHandler) Enabled(_ context.Context, level slog.Level) bool

Enabled returns true if the handler should log at the given level.

func (*LogfmtHandler) Handle

func (h *LogfmtHandler) Handle(_ context.Context, r slog.Record) error

Handle processes the log record and writes it in logfmt format.

func (*LogfmtHandler) WithAttrs

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

WithAttrs returns a new handler with the given attributes added.

func (*LogfmtHandler) WithGroup

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

WithGroup returns a new handler with the given group name. Logfmt handler doesn't support grouping, so it's a no-op.

type LogstashHandler

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

LogstashHandler is a custom slog.Handler that outputs logs in Logstash/Elasticsearch compatible JSON format. It wraps slog.JSONHandler for optimal performance.

Key differences from standard JSONHandler:

  • Field names: @timestamp, log.level, message (instead of time, level, msg)
  • Groups are flattened with dot notation (e.g., request.id instead of request: {id: ...})
  • Supports field filtering via include/exclude lists

Implements slog.Handler interface.

func NewLogstashHandler

func NewLogstashHandler(w io.Writer, opts *HandlerOptions) *LogstashHandler

NewLogstashHandler creates a new LogstashHandler writing to the specified writer.

func (*LogstashHandler) Enabled

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

Enabled returns true if the handler should log at the given level.

func (*LogstashHandler) Handle

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

Handle processes the log record and writes it in Logstash JSON format. It flattens groups and renames standard fields before delegating to JSONHandler.

func (*LogstashHandler) WithAttrs

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

WithAttrs returns a new handler with the given attributes added.

func (*LogstashHandler) WithGroup

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

WithGroup returns a new handler with the given group name prepended to attribute keys.

type MultiWriter

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

MultiWriter writes to multiple outputs simultaneously. It supports writing to stdout and/or a file.

func NewMultiWriter

func NewMultiWriter(outputFile string, syncMode SyncMode, syncInterval time.Duration) (*MultiWriter, error)

NewMultiWriter creates a writer that outputs to stdout and/or file. If outputFile is empty, only stdout is used.

func (*MultiWriter) Close

func (m *MultiWriter) Close() error

Close closes the file handle if one was opened.

func (*MultiWriter) Write

func (m *MultiWriter) Write(p []byte) (n int, err error)

Write implements io.Writer.

type NoOpLogger

type NoOpLogger struct{}

NoOpLogger is a no-operation implementation of DebugLogger. It provides zero overhead when debug mode is disabled. All methods are empty and Enabled() always returns false.

func NewNoOpLogger

func NewNoOpLogger() *NoOpLogger

NewNoOpLogger creates a new NoOpLogger instance.

func (*NoOpLogger) Close

func (n *NoOpLogger) Close() error

Close returns nil - no resources to release.

func (*NoOpLogger) Enabled

func (n *NoOpLogger) Enabled() bool

Enabled returns false - debug logging is disabled.

func (*NoOpLogger) LogDebug

func (n *NoOpLogger) LogDebug(ctx context.Context, message string, fieldMap ...FieldMap)

LogDebug logs string message with slog.Level = LevelDebug.

func (*NoOpLogger) LogError

func (n *NoOpLogger) LogError(_ context.Context, _ string, _ error, _ ...FieldMap)

LogError is a no-op.

func (*NoOpLogger) LogInfo

func (n *NoOpLogger) LogInfo(ctx context.Context, message string, fieldMap ...FieldMap)

LogDebug logs string message with slog.Level = LevelInfo.

func (*NoOpLogger) LogWarn

func (n *NoOpLogger) LogWarn(ctx context.Context, message string, fieldMap ...FieldMap)

LogDebug logs string message with slog.Level = LevelWarn.

func (*NoOpLogger) WithFields

func (n *NoOpLogger) WithFields(_ FieldMap) DebugLogger

WithFields returns the same NoOpLogger instance.

func (*NoOpLogger) WithGroup

func (n *NoOpLogger) WithGroup(_ string) DebugLogger

WithGroup returns the same NoOpLogger instance.

type Option

type Option func(*config)

Option is a functional option for configuring the logger.

func WithExcludeFields

func WithExcludeFields(fields []string) Option

WithExcludeFields sets the fields to exclude from log output.

func WithFields

func WithFields(fields FieldMap) Option

WithFields sets pre-populated fields for all log entries. These fields will be included in every log message from this logger.

func WithGroup

func WithGroup(name string) Option

WithGroup sets a group name for all subsequent attributes. All fields will be prefixed with the group name (e.g., "myservice.field").

func WithIncludeFields

func WithIncludeFields(fields []string) Option

WithIncludeFields sets the fields to include in log output. If specified, only these fields will be included.

func WithMinLevel

func WithMinLevel(level Level) Option

WithMinLevel sets the minimum log level.

func WithOutputFile added in v1.0.1

func WithOutputFile(path string) Option

WithOutputFile enables file output in addition to stdout. If not called, logs are written to stdout only.

func WithSyncInterval

func WithSyncInterval(interval time.Duration) Option

WithSyncInterval sets the flush interval for SyncPeriodic mode. Only used when SyncMode is SyncPeriodic. Default is 1 second.

func WithSyncMode

func WithSyncMode(mode SyncMode) Option

WithSyncMode sets the sync mode for file output. Default is SyncImmediate for maximum durability.

type SlogLogger

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

SlogLogger wraps slog.Logger and implements the DebugLogger interface. It provides structured logging with domain-specific methods for pipeline stages, retry attempts, and rate limiting decisions.

func (*SlogLogger) Close

func (s *SlogLogger) Close() error

Close flushes any buffered output and closes file handles.

func (*SlogLogger) Enabled

func (s *SlogLogger) Enabled() bool

Enabled returns true if debug logging is enabled.

func (*SlogLogger) LogDebug

func (s *SlogLogger) LogDebug(ctx context.Context, message string, fieldMap ...FieldMap)

LogDebug logs a debug-level message with context. Pre-populated fields from WithFields are already baked into the logger.

func (*SlogLogger) LogError

func (s *SlogLogger) LogError(ctx context.Context, message string, err error, fieldMap ...FieldMap)

LogError logs an error-level message with context. Pre-populated fields from WithFields are already baked into the logger.

func (*SlogLogger) LogInfo

func (s *SlogLogger) LogInfo(ctx context.Context, message string, fieldMap ...FieldMap)

LogInfo logs an info-level message with context. Pre-populated fields from WithFields are already baked into the logger.

func (*SlogLogger) LogWarn

func (s *SlogLogger) LogWarn(ctx context.Context, message string, fieldMap ...FieldMap)

LogWarn logs a warn-level message with context. Pre-populated fields from WithFields are already baked into the logger.

func (*SlogLogger) WithFields

func (s *SlogLogger) WithFields(fields FieldMap) DebugLogger

WithFields returns a logger with pre-populated fields. Fields are baked into the slog.Logger for optimal performance.

func (*SlogLogger) WithGroup

func (s *SlogLogger) WithGroup(name string) DebugLogger

WithGroup returns a logger with all subsequent attributes grouped under the given name.

type SyncMode

type SyncMode int

SyncMode determines when file writes are flushed to disk.

const (
	// SyncImmediate flushes after every write (maximum durability, slower).
	SyncImmediate SyncMode = iota

	// SyncBuffered buffers writes and flushes only on Close() (better performance).
	SyncBuffered

	// SyncPeriodic flushes at regular intervals (balance of durability/performance).
	SyncPeriodic
)

type TextHandler

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

TextHandler creates a human-readable text output.

Implements slog.Handler interface.

func NewTextHandler

func NewTextHandler(w io.Writer, opts *HandlerOptions) *TextHandler

NewTextHandler creates a new TextHandler writing to the specified writer.

func (*TextHandler) Enabled

func (h *TextHandler) Enabled(_ context.Context, level slog.Level) bool

Enabled returns true if the handler should log at the given level.

func (*TextHandler) Handle

func (h *TextHandler) Handle(_ context.Context, r slog.Record) error

Handle processes the log record and writes it in human-readable text format.

func (*TextHandler) WithAttrs

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

WithAttrs returns a new handler with the given attributes added.

func (*TextHandler) WithGroup

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

WithGroup returns a new handler with the given group name. Text handler doesn't support grouping, so it's a no-op.

Directories

Path Synopsis
example
field_logging command
simple_logging command

Jump to

Keyboard shortcuts

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