slog

package
v1.2.4 Latest Latest
Warning

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

Go to latest
Published: Jan 1, 2026 License: Apache-2.0 Imports: 9 Imported by: 12

README

SLog

Structured logging with asynchronous output and flexible configuration based on Go's log/slog.

Overview

The slog package provides enhanced structured logging capabilities with features like asynchronous log processing, multiple output destinations, automatic file rotation, and caller information tracking. It wraps Go's standard log/slog package with additional convenience features.

Features

  • Structured Logging - Key-value pair logging in JSON format
  • Multiple Log Levels - Trace, Debug, Info, Warn, Error, Fatal
  • Asynchronous Processing - Queue-based non-blocking log writes
  • Flexible Output - stdout, stderr, or file output
  • Daily Rotation - Automatic log file rotation by date
  • Caller Tracking - Optional file/line/function information
  • Thread-Safe - Concurrent logging from multiple goroutines
  • Flush Control - Ensure all logs are written before shutdown

Installation

go get -u github.com/common-library/go/log/slog

Quick Start

import "github.com/common-library/go/log/slog"

var logger slog.Log

func main() {
    defer logger.Flush()
    
    logger.SetLevel(slog.LevelInfo)
    logger.SetOutputToFile("app", "log", true)
    
    logger.Info("Server started", "port", 8080)
    logger.Error("Connection failed", "error", err.Error())
}

API Reference

Log Levels
const (
    LevelTrace = Level(-8)  // Most verbose
    LevelDebug = Level(-4)  // Debug information
    LevelInfo  = Level(0)   // General information
    LevelWarn  = Level(4)   // Warning messages
    LevelError = Level(8)   // Error messages
    LevelFatal = Level(12)  // Critical errors
)
Log Type
type Log struct {
    // Unexported fields
}
Logging Methods
Trace
func (l *Log) Trace(message string, arguments ...any)

Logs at trace level. Arguments are key-value pairs.

Debug
func (l *Log) Debug(message string, arguments ...any)

Logs at debug level.

Info
func (l *Log) Info(message string, arguments ...any)

Logs at info level.

Warn
func (l *Log) Warn(message string, arguments ...any)

Logs at warning level.

Error
func (l *Log) Error(message string, arguments ...any)

Logs at error level.

Fatal
func (l *Log) Fatal(message string, arguments ...any)

Logs at fatal level (does NOT terminate application).

Configuration Methods
SetLevel
func (l *Log) SetLevel(level Level)

Sets minimum log level threshold.

SetOutputToStdout
func (l *Log) SetOutputToStdout()

Redirects logs to standard output.

SetOutputToStderr
func (l *Log) SetOutputToStderr()

Redirects logs to standard error.

SetOutputToFile
func (l *Log) SetOutputToFile(fileName, fileExtensionName string, addDate bool)

Redirects logs to file with optional daily rotation.

SetWithCallerInfo
func (l *Log) SetWithCallerInfo(withCallerInfo bool)

Enables/disables caller information in logs.

GetLevel
func (l *Log) GetLevel() Level

Returns current log level.

Flush
func (l *Log) Flush()

Blocks until all queued logs are written.

Complete Examples

Basic Logging
package main

import (
    "github.com/common-library/go/log/slog"
)

var logger slog.Log

func main() {
    defer logger.Flush()
    
    logger.SetLevel(slog.LevelInfo)
    logger.SetOutputToStdout()
    
    logger.Info("Application started")
    logger.Info("Processing request", "method", "GET", "path", "/api/users")
    logger.Info("Application stopped")
}
File Logging with Rotation
package main

import (
    "github.com/common-library/go/log/slog"
)

var logger slog.Log

func main() {
    defer logger.Flush()
    
    // Creates files like: app_20231218.log, app_20231219.log, etc.
    logger.SetOutputToFile("app", "log", true)
    logger.SetLevel(slog.LevelDebug)
    
    logger.Debug("Debug information", "value", 42)
    logger.Info("Server started", "port", 8080)
    logger.Warn("High memory usage", "percent", 85)
}
Error Handling
package main

import (
    "errors"
    "github.com/common-library/go/log/slog"
)

var logger slog.Log

func processData() error {
    defer logger.Flush()
    
    logger.SetLevel(slog.LevelInfo)
    logger.SetOutputToFile("errors", "log", true)
    
    if err := validateInput(); err != nil {
        logger.Error("Validation failed",
            "error", err.Error(),
            "function", "validateInput",
        )
        return err
    }
    
    logger.Info("Processing completed successfully")
    return nil
}

func validateInput() error {
    return errors.New("invalid input format")
}

func main() {
    processData()
}
With Caller Information
package main

import (
    "github.com/common-library/go/log/slog"
)

var logger slog.Log

func processRequest(id int) {
    logger.Info("Processing started", "requestID", id)
    // Includes: {"CallerInfo":{"File":"main.go","Line":15,"Function":"main.processRequest"},...}
}

func main() {
    defer logger.Flush()
    
    logger.SetLevel(slog.LevelDebug)
    logger.SetOutputToStdout()
    logger.SetWithCallerInfo(true) // Enable caller info
    
    processRequest(123)
}
Multiple Log Levels
package main

import (
    "github.com/common-library/go/log/slog"
    "time"
)

var logger slog.Log

func main() {
    defer logger.Flush()
    
    logger.SetLevel(slog.LevelTrace)
    logger.SetOutputToFile("detailed", "log", true)
    
    logger.Trace("Entering function", "function", "main")
    logger.Debug("Configuration loaded", "config", "app.yaml")
    logger.Info("Server starting")
    logger.Warn("Cache nearly full", "usage", "90%")
    logger.Error("Failed to connect", "service", "database")
    logger.Fatal("Critical system error", "component", "core")
}
Dynamic Level Change
package main

import (
    "os"
    "github.com/common-library/go/log/slog"
)

var logger slog.Log

func main() {
    defer logger.Flush()
    
    // Set level based on environment
    if os.Getenv("DEBUG") == "true" {
        logger.SetLevel(slog.LevelDebug)
        logger.SetWithCallerInfo(true)
    } else {
        logger.SetLevel(slog.LevelInfo)
        logger.SetWithCallerInfo(false)
    }
    
    logger.SetOutputToStdout()
    
    logger.Debug("Debug mode enabled") // Only if DEBUG=true
    logger.Info("Application started")
}
HTTP Server Logging
package main

import (
    "net/http"
    "time"
    "github.com/common-library/go/log/slog"
)

var logger slog.Log

func loggingMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        start := time.Now()
        
        logger.Info("Request started",
            "method", r.Method,
            "path", r.URL.Path,
            "remote", r.RemoteAddr,
        )
        
        next.ServeHTTP(w, r)
        
        logger.Info("Request completed",
            "method", r.Method,
            "path", r.URL.Path,
            "duration", time.Since(start).Milliseconds(),
        )
    })
}

func main() {
    defer logger.Flush()
    
    logger.SetLevel(slog.LevelInfo)
    logger.SetOutputToFile("access", "log", true)
    
    http.Handle("/", loggingMiddleware(http.HandlerFunc(handler)))
    http.ListenAndServe(":8080", nil)
}

func handler(w http.ResponseWriter, r *http.Request) {
    w.Write([]byte("Hello"))
}
Structured Application Logging
package main

import (
    "github.com/common-library/go/log/slog"
)

var (
    appLogger   slog.Log
    auditLogger slog.Log
)

func main() {
    defer appLogger.Flush()
    defer auditLogger.Flush()
    
    // Application logs
    appLogger.SetLevel(slog.LevelInfo)
    appLogger.SetOutputToFile("app", "log", true)
    
    // Audit logs (separate file)
    auditLogger.SetLevel(slog.LevelInfo)
    auditLogger.SetOutputToFile("audit", "log", true)
    
    appLogger.Info("Application started")
    
    // Log user action to audit log
    auditLogger.Info("User action",
        "userID", 123,
        "action", "login",
        "ip", "192.168.1.1",
    )
    
    appLogger.Info("Processing request")
    
    auditLogger.Info("Data access",
        "userID", 123,
        "resource", "/api/users/456",
        "operation", "READ",
    )
}

Best Practices

1. Always Flush Before Exit
// Good: Ensures all logs are written
func main() {
    defer logger.Flush()
    // ... application code ...
}

// Avoid: May lose recent logs
func main() {
    // ... application code ...
    // No flush
}
2. Use Appropriate Log Levels
// Good: Match severity to level
logger.Trace("Variable value", "x", x)     // Detailed debugging
logger.Debug("Function called", "name", f) // Development info
logger.Info("Server started", "port", 80)  // Normal operation
logger.Warn("Retry attempt", "count", 3)   // Potential issue
logger.Error("Failed to save", "err", err) // Operation failed
logger.Fatal("Config missing")             // Critical error

// Avoid: Wrong levels
logger.Error("Server started") // Not an error
logger.Info("Critical failure") // Should be Fatal
3. Use Structured Key-Value Pairs
// Good: Structured data
logger.Info("User logged in",
    "userID", 123,
    "username", "alice",
    "ip", remoteAddr,
)

// Avoid: Unstructured messages
logger.Info(fmt.Sprintf("User %d (%s) logged in from %s", 123, "alice", remoteAddr))
4. Enable Caller Info Selectively
// Good: Only in development
if os.Getenv("ENV") == "development" {
    logger.SetWithCallerInfo(true)
}

// Avoid: Always enabled in production (performance impact)
logger.SetWithCallerInfo(true)
5. Use Daily Rotation for Production
// Good: Automatic rotation
logger.SetOutputToFile("app", "log", true)
// Creates: app_20231218.log, app_20231219.log, etc.

// Avoid: Single large file
logger.SetOutputToFile("app", "log", false)
// Creates: app.log (grows indefinitely)

Performance Tips

  1. Asynchronous by Design - Logs are queued and written asynchronously
  2. Flush Strategically - Only flush when necessary (shutdown, critical points)
  3. Disable Caller Info in Production - Adds overhead for getting stack information
  4. Use Appropriate Levels - Set level to Info or higher in production
  5. Structured Logging - More efficient than string formatting

Testing

func TestLogging(t *testing.T) {
    var logger slog.Log
    
    // Use file output for test verification
    logger.SetOutputToFile("test", "log", false)
    logger.SetLevel(slog.LevelDebug)
    
    logger.Info("Test message", "key", "value")
    logger.Flush()
    
    // Read and verify log file
    data, err := os.ReadFile("test.log")
    if err != nil {
        t.Fatal(err)
    }
    
    if !strings.Contains(string(data), "Test message") {
        t.Error("Log message not found")
    }
    
    // Cleanup
    os.Remove("test.log")
}

Dependencies

  • log/slog - Go standard library structured logging
  • github.com/common-library/go/collection - Queue implementation
  • github.com/common-library/go/lock - Mutex implementation
  • github.com/common-library/go/utility - Caller info utilities

Further Reading

Documentation

Overview

Package slog provides structured logging with asynchronous output and flexible configuration.

This package wraps Go's standard log/slog with additional features including asynchronous logging, file rotation, multiple output destinations, and caller information tracking.

Features:

  • Structured logging with key-value pairs
  • Multiple log levels (Trace, Debug, Info, Warn, Error, Fatal)
  • Asynchronous logging with queue-based buffering
  • Output to stdout, stderr, or files
  • Daily log file rotation
  • Caller information tracking
  • Thread-safe operations

Example:

var logger slog.Log
logger.SetLevel(slog.LevelInfo)
logger.SetOutputToFile("app", "log", true)
logger.Info("Server started", "port", 8080)
logger.Flush()

Index

Constants

View Source
const (
	LevelTrace = Level(-8)
	LevelDebug = Level(slog.LevelDebug)
	LevelInfo  = Level(slog.LevelInfo)
	LevelWarn  = Level(slog.LevelWarn)
	LevelError = Level(slog.LevelError)
	LevelFatal = Level(12)
)

Variables

This section is empty.

Functions

This section is empty.

Types

type Level

type Level int

type Log

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

Log is struct that provides log related methods.

func (*Log) Debug

func (l *Log) Debug(message string, arguments ...any)

Debug logs a message at debug level with optional key-value pairs.

Debug level is used for detailed diagnostic information useful during development and troubleshooting.

Parameters:

  • message: The log message
  • arguments: Optional key-value pairs (must be even number of arguments)

Example:

var logger slog.Log
logger.Debug("Query executed", "sql", "SELECT * FROM users", "duration", 45)

Example with error context:

logger.Debug("Retrying connection",
    "attempt", 2,
    "maxAttempts", 3,
    "error", lastErr.Error(),
)

func (*Log) Error

func (l *Log) Error(message string, arguments ...any)

Error logs a message at error level with optional key-value pairs.

Error level is used for errors that prevent specific operations from completing successfully but don't crash the application.

Parameters:

  • message: The log message
  • arguments: Optional key-value pairs (must be even number of arguments)

Example:

var logger slog.Log
logger.Error("Failed to save user", "userID", 123, "error", err.Error())

Example with context:

logger.Error("API request failed",
    "endpoint", "/api/users",
    "method", "POST",
    "statusCode", 500,
    "error", err.Error(),
)

func (*Log) Fatal

func (l *Log) Fatal(message string, arguments ...any)

Fatal logs a message at fatal level with optional key-value pairs.

Fatal level indicates critical errors that require immediate attention. Note: Unlike some logging libraries, this does NOT terminate the application.

Parameters:

  • message: The log message
  • arguments: Optional key-value pairs (must be even number of arguments)

Example:

var logger slog.Log
logger.Fatal("Database connection lost", "error", err.Error())

Example for critical failures:

logger.Fatal("Configuration file corrupt",
    "file", "config.yaml",
    "error", err.Error(),
    "action", "manual intervention required",
)

func (*Log) Flush

func (l *Log) Flush()

Flush blocks until all queued log entries are written.

This method ensures all pending asynchronous log writes are completed before returning. It's essential to call this before application shutdown to ensure no logs are lost.

Behavior:

  • Blocks the calling goroutine until all queued logs are written
  • Processes the internal logging queue completely
  • Thread-safe and can be called concurrently

Example at application shutdown:

var logger slog.Log
defer logger.Flush()

logger.Info("Application starting")
// ... application logic ...
logger.Info("Application shutting down")
// Flush ensures shutdown message is written

Example in tests:

func TestLogging(t *testing.T) {
    var logger slog.Log
    logger.SetOutputToFile("test", "log", false)

    logger.Info("Test message")
    logger.Flush() // Ensure log is written before test ends

    // Verify log file contents
}

func (*Log) GetLevel

func (l *Log) GetLevel() Level

GetLevel returns the current minimum log level.

Returns:

  • Level: Current log level threshold

Only log messages at or above this level will be written. The returned value is one of: LevelTrace, LevelDebug, LevelInfo, LevelWarn, LevelError, or LevelFatal.

Example:

var logger slog.Log
currentLevel := logger.GetLevel()

if currentLevel == slog.LevelDebug {
    fmt.Println("Debug logging is enabled")
}

Example for conditional logging:

if logger.GetLevel() <= slog.LevelDebug {
    // Perform expensive debug data collection
    debugData := collectDebugInfo()
    logger.Debug("Debug info", "data", debugData)
}

func (*Log) Info

func (l *Log) Info(message string, arguments ...any)

Info logs a message at info level with optional key-value pairs.

Info level is used for general informational messages about application operation, such as startup, shutdown, and significant state changes.

Parameters:

  • message: The log message
  • arguments: Optional key-value pairs (must be even number of arguments)

Example:

var logger slog.Log
logger.Info("Server started", "port", 8080, "environment", "production")

Example for lifecycle events:

logger.Info("Database connection established",
    "host", "localhost",
    "database", "myapp",
    "poolSize", 10,
)

func (*Log) SetLevel

func (l *Log) SetLevel(level Level)

SetLevel sets the minimum log level threshold.

Parameters:

  • level: New minimum log level (LevelTrace, LevelDebug, LevelInfo, LevelWarn, LevelError, LevelFatal)

Only log messages at or above this level will be written. This operation is asynchronous and queued for processing.

Example:

var logger slog.Log
logger.SetLevel(slog.LevelInfo)

logger.Debug("This will not be logged")
logger.Info("This will be logged")

Example for dynamic level adjustment:

if os.Getenv("DEBUG") == "true" {
    logger.SetLevel(slog.LevelDebug)
} else {
    logger.SetLevel(slog.LevelInfo)
}

Example for production:

logger.SetLevel(slog.LevelWarn) // Only warnings and errors in production

func (*Log) SetOutputToFile

func (l *Log) SetOutputToFile(fileName, fileExtensionName string, addDate bool)

SetOutputToFile configures logging output to a file.

Parameters:

  • fileName: Base name of the log file (without extension)
  • fileExtensionName: File extension (e.g., "log", "txt")
  • addDate: If true, appends current date (YYYYMMDD) to filename

Behavior:

  • Creates or appends to the specified file
  • If addDate is true, filename becomes: fileName_YYYYMMDD.extension
  • If addDate is false, filename becomes: fileName.extension
  • Automatic daily rotation when addDate is true
  • Falls back to stdout if file cannot be opened
  • Logs are written in JSON format

Example without date:

var logger slog.Log
logger.SetOutputToFile("application", "log", false)
// Writes to: application.log

Example with daily rotation:

logger.SetOutputToFile("app", "log", true)
// Writes to: app_20231218.log
// Automatically creates new file next day: app_20231219.log

Example for different environments:

env := os.Getenv("ENV")
logger.SetOutputToFile(env+"-app", "log", true)
// production-app_20231218.log or development-app_20231218.log

func (*Log) SetOutputToStderr

func (l *Log) SetOutputToStderr()

SetOutputToStderr configures logging output to standard error.

All subsequent log messages will be written to stderr (os.Stderr). This operation is asynchronous and queued for processing.

Behavior:

  • Switches output destination to stderr
  • Maintains current log level
  • Logs are written in JSON format

Example:

var logger slog.Log
logger.SetOutputToStderr()
logger.Error("This goes to stderr")

Example for error-only logging:

errorLogger := &slog.Log{}
errorLogger.SetOutputToStderr()
errorLogger.SetLevel(slog.LevelError)

func (*Log) SetOutputToStdout

func (l *Log) SetOutputToStdout()

SetOutputToStdout configures logging output to standard output.

All subsequent log messages will be written to stdout (os.Stdout). This operation is asynchronous and queued for processing.

Behavior:

  • Switches output destination to stdout
  • Maintains current log level
  • Logs are written in JSON format

Example:

var logger slog.Log
logger.SetOutputToStdout()
logger.Info("This goes to stdout")

Example for development:

if os.Getenv("ENV") == "development" {
    logger.SetOutputToStdout()
} else {
    logger.SetOutputToFile("app", "log", true)
}

func (*Log) SetWithCallerInfo

func (l *Log) SetWithCallerInfo(withCallerInfo bool)

SetWithCallerInfo enables or disables caller information in log entries.

Parameters:

  • withCallerInfo: If true, includes file name, line number, and function name in logs

When enabled, each log entry will include a "CallerInfo" field with:

  • File: Source file name
  • Line: Line number
  • Function: Function name

This operation is asynchronous and queued for processing.

Example:

var logger slog.Log
logger.SetWithCallerInfo(true)
logger.Info("User logged in", "userID", 123)
// Log includes: {"CallerInfo":{"File":"main.go","Line":45,"Function":"handleLogin"},...}

Example for debugging:

if os.Getenv("DEBUG") == "true" {
    logger.SetWithCallerInfo(true) // Enable in debug mode
} else {
    logger.SetWithCallerInfo(false) // Disable in production for performance
}

func (*Log) Trace

func (l *Log) Trace(message string, arguments ...any)

Trace logs a message at trace level with optional key-value pairs.

Trace is the lowest log level, typically used for very detailed debugging information that is rarely needed in production.

Parameters:

  • message: The log message
  • arguments: Optional key-value pairs (must be even number of arguments)

The arguments are interpreted as alternating keys and values. Keys should be strings, values can be any type.

Example:

var logger slog.Log
logger.Trace("Function entered", "function", "processData", "params", 3)

Example with multiple pairs:

logger.Trace("Processing request",
    "requestID", "req-123",
    "userID", 456,
    "timestamp", time.Now(),
)

func (*Log) Warn

func (l *Log) Warn(message string, arguments ...any)

Warn logs a message at warning level with optional key-value pairs.

Warn level indicates potentially harmful situations that don't prevent the application from functioning but should be investigated.

Parameters:

  • message: The log message
  • arguments: Optional key-value pairs (must be even number of arguments)

Example:

var logger slog.Log
logger.Warn("High memory usage", "used", "85%", "threshold", "80%")

Example for degraded performance:

logger.Warn("Slow query detected",
    "query", "SELECT * FROM large_table",
    "duration", "5.2s",
    "threshold", "1s",
)

Jump to

Keyboard shortcuts

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