🚨 Shai-Hulud Strikes Again:834 Packages Compromised.Technical Analysis β†’
Socket
Book a DemoInstallSign in
Socket

darvaza.org/slog

Package Overview
Dependencies
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

darvaza.org/slog

Go Modules
Version
v0.8.1
Version published
Created
Source

slog

Go Reference Go Report Card codecov Socket Badge

darvaza.org/slog provides a backend-agnostic interface for structured logging in Go. It defines a simple, standardised API that libraries can use without forcing a specific logging implementation on their users.

Features

  • Backend-agnostic: Define logging interfaces without forcing implementation choices.
  • Structured logging: Support for typed fields with string keys.
  • Method chaining: Fluent API for composing log entries.
  • Six log levels: Debug, Info, Warn, Error, Fatal, and Panic.
  • Context integration: Store and retrieve loggers from context values.
  • Standard library compatible: Adapters for Go's standard log package.
  • Multiple handlers: Pre-built integrations with popular logging libraries.
  • Immutable logger instances: Each modification creates a new logger, enabling safe concurrent use and proper branching behaviour.

Installation

go get darvaza.org/slog

Quick Start

package main

import (
    "darvaza.org/slog"
    "darvaza.org/slog/handlers/discard"
)

func main() {
    // Create a logger (using discard handler for example)
    logger := discard.New()

    // Log with different levels
    logger.Info().Print("Application started")

    // Add fields
    logger.Debug().
        WithField("user", "john").
        WithField("action", "login").
        Print("User logged in")

    // Use Printf-style formatting
    logger.Warn().
        WithField("retry_count", 3).
        Printf("Connection failed, will retry")
}

Interface

The slog.Logger interface provides a fluent API where most methods return a Logger for method chaining. A log entry is composed by:

  • Setting the log level
  • Optionally adding fields and call stack information
  • Emitting the entry with a Print method

Disabled log entries incur minimal overhead as string formatting and field collection are skipped.

Log Levels

The library supports six log levels with clear semantics:

  • Debug: Detailed information for developers.
  • Info: General informational messages.
  • Warn: Warning messages for potentially harmful situations.
  • Error: Error conditions that allow continued operation.
  • Fatal: Critical errors that terminate the program (like log.Fatal()).
  • Panic: Errors that trigger a recoverable panic (like log.Panic()).

Create log entries using named methods (Debug(), Info(), etc.) or WithLevel(level).

Enabled State

A log entry is "enabled" if the handler will actually emit it. Operating on disabled loggers is safe and efficient - string formatting and field collection are skipped.

Use WithEnabled() to check if a level is enabled:

if log, ok := logger.Debug().WithEnabled(); ok {
    // Expensive debug logging
    log.WithField("details", expensiveOperation()).Print("Debug info")
} else if log, ok := logger.Info().WithEnabled(); ok {
    // Simpler info logging
    log.Print("Operation completed")
}

Note: Fatal and Panic levels always execute regardless of enabled state.

Fields

Fields are key/value pairs for structured logging:

  • Keys must be non-empty strings
  • Values can be any type
  • Fields are attached using WithField(key, value)
  • Multiple fields can be attached by chaining calls
import "time"

start := time.Now()
// ... perform some work ...

logger.Info().
    WithField("user_id", 123).
    WithField("duration", time.Since(start)).
    Print("Request processed")

Branching Behaviour

Each logger instance is immutable. When you call methods like WithField() or WithLevel(), you get a new logger instance that inherits from the parent:

// Create a base logger with common fields
baseLogger := logger.WithField("service", "api")

// Branch off for different request handlers
userLogger := baseLogger.WithField("handler", "user")
adminLogger := baseLogger.WithField("handler", "admin")

// Each logger maintains its own field chain
userLogger.Info().Print("Processing user request")
// Output includes: service=api, handler=user

adminLogger.Info().Print("Processing admin request")
// Output includes: service=api, handler=admin

// Original logger is unchanged
baseLogger.Info().Print("Base logger message") // only has service=api

This design ensures:

  • Thread-safe concurrent use without locks
  • No unintended field pollution between different code paths
  • Clear ownership and lifecycle of logger configurations

Call Stack

Attach stack traces to log entries using WithStack(skip):

logger.Error().
    WithStack(0).  // 0 = current function
    WithField("error", err).
    Print("Operation failed")

The skip parameter specifies how many stack frames to skip (0 = current function).

Print Methods

Three print methods match the fmt package conventions:

  • Print(v ...any): Like fmt.Print
  • Println(v ...any): Like fmt.Println
  • Printf(format string, v ...any): Like fmt.Printf

These methods emit the log entry with all attached fields.

Standard Library Integration

Integrate with Go's standard log package:

import (
    "log"
    "net/http"

    "darvaza.org/slog"
)

// Assuming you have a slog logger instance
// var logger slog.Logger

// Create a standard logger that writes to slog
stdLogger := slog.NewStdLogger(logger, "[HTTP]", log.LstdFlags)

// Use with libraries expecting *log.Logger
server := &http.Server{
    ErrorLog: stdLogger,
}

For custom parsing, use NewLogWriter() with a handler function.

Architecture Overview

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                        External Dependencies                       β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚    darvaza.org/core         β”‚      Go Standard Library             β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
              β”‚                           β”‚
              β–Ό                           β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                            slog Core                               β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚   Logger Interface   β”‚ Context Integration β”‚ Std Library Adapter   β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚             internal.Loglet (field chain management)               β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
           β”‚
           β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                            Handlers                                β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚  logr   β”‚ logrus  β”‚   zap   β”‚ zerolog β”‚  cblog  β”‚ filter β”‚ discard β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

All handlers use the internal.Loglet type for consistent field chain management and immutable logger behaviour.

Available Handlers

Adapter Types

Handlers fall into two categories based on their integration capabilities:

Bidirectional Adapters

These handlers allow conversion in both directions - you can use the external logging library as a slog backend, OR use slog as a backend for the external library:

  • logr: Full bidirectional adapter for go-logr/logr interface.
    • logr.Logger β†’ slog.Logger (use logr as slog backend)
    • slog.Logger β†’ logr.Logger (use slog as logr backend)
  • logrus: Bidirectional adapter for Sirupsen/logrus.
    • logrus.Logger β†’ slog.Logger (use logrus as slog backend)
    • slog.Logger β†’ logrus.Logger (use slog as logrus backend)
  • zap: Bidirectional adapter between Uber's zap and slog. Use zap as a slog backend or create zap loggers backed by any slog implementation.

Unidirectional Adapters

These handlers only allow using the external logging library as a backend for slog. They wrap existing loggers but don't provide the reverse conversion:

  • zerolog: Wraps rs/zerolog as a slog backend.

Utility Handlers

These handlers provide additional functionality without external dependencies:

  • cblog: Channel-based handler for custom log processing.

  • filter: Middleware to filter and transform log entries.

  • mock: Mock logger implementation that records messages for testing and verification. Provides a fully functional slog.Logger that captures all log entries with their levels, messages, and fields for programmatic inspection.

    import (
        "testing"
    
        "darvaza.org/slog/handlers/mock"
    )
    
    func TestMyCode(t *testing.T) {
        logger := mock.NewLogger()
    
        // Use logger in your code
        myFunction(logger)
    
        // Verify what was logged
        messages := logger.GetMessages()
        if len(messages) != 1 {
            t.Fatalf("expected 1 message, got %d", len(messages))
        }
    
        msg := messages[0]
        if msg.Level != slog.Info || msg.Message != "expected message" {
            t.Errorf("unexpected log entry: %v", msg)
        }
    }
    
  • discard: No-op handler for testing and optional logging.

Adapter Differences

Bidirectional adapters are valuable when:

  • Integration with libraries that expect a specific logger interface is required.
  • Gradual migration between logging systems is in progress.
  • A common interface is desired across different application components while maintaining compatibility with existing code.

Unidirectional adapters are simpler and suitable when:

  • An existing logger serves as the slog backend without reverse integration.
  • New applications can adopt slog as the primary logging interface.
  • Libraries expecting the backend's specific interface are not a concern.

Testing

The package provides comprehensive test utilities for handler implementations in internal/testing. These utilities help ensure consistent testing patterns and reduce code duplication across handlers.

See internal/testing/README.md for detailed documentation on using the test utilities, including:

  • Test logger for recording and verifying log messages
  • Assertion helpers for message verification
  • Compliance test suite for interface conformance
  • Concurrency testing utilities

Development

See AGENT.md for development guidelines and LICENCE.txt for licensing information.

FAQs

Package last updated on 01 Sep 2025

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts