log

package module
v0.0.0-...-2ebc84a Latest Latest
Warning

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

Go to latest
Published: Sep 16, 2025 License: Apache-2.0 Imports: 21 Imported by: 80

README

log

A flexible and structured logging package for Golang applications in the Deckhouse ecosystem.

Overview

The log package provides a consistent logging interface for Deckhouse components. It builds upon Go's log/slog package to offer structured logging capabilities with additional features tailored for Kubernetes and cloud-native applications.

Features

  • Structured Logging: Built on top of Go's log/slog with key-value pairs
  • Multiple Log Levels: TRACE, DEBUG, INFO, WARN, ERROR, FATAL
  • Context-Aware Logging: Automatic source file location and stack traces for errors
  • Multiple Output Formats: JSON and Text handlers with customizable formatting
  • Named Loggers: Hierarchical logger naming with dot-separated components
  • Stack Trace Support: Automatic stack trace capture for error and fatal levels
  • Raw Data Support: JSON and YAML raw data logging with automatic parsing
  • Thread-Safe: Concurrent logging support with atomic operations
  • Development Features: IDE-friendly source formatting and customizable time functions

Installation

go get github.com/deckhouse/deckhouse/pkg/log

Quick Start

Using the Global Logger
package main

import (
    "log/slog"
    "github.com/deckhouse/deckhouse/pkg/log"
)

func main() {
    // Basic logging
    log.Info("Application started")
    
    // Logging with key-value pairs (using slog attributes)
    log.Info("Processing request", 
        slog.String("requestID", "abc-123"),
        slog.String("method", "GET"),
        slog.String("path", "/api/v1/users"),
    )
    
    // Using slog attributes
    log.Info("User action", 
        slog.String("userID", "123"),
        slog.String("action", "login"),
        slog.Duration("duration", time.Second*2),
    )
    
    // Error logging (automatically includes stack trace)
    log.Error("Failed to process request", slog.String("error", err.Error()))
    
    // Context-aware logging
    log.InfoContext(ctx, "Request processed", slog.String("status", "success"))
}
Creating Custom Loggers
package main

import (
    "os"
    "log/slog"
    "github.com/deckhouse/deckhouse/pkg/log"
)

func main() {
    // Create a JSON logger
    jsonLogger := log.NewLogger(
        log.WithOutput(os.Stdout),
        log.WithLevel(slog.LevelDebug),
        log.WithHandlerType(log.JSONHandlerType),
    )
    
    // Create a text logger
    textLogger := log.NewLogger(
        log.WithOutput(os.Stderr),
        log.WithLevel(slog.LevelInfo),
        log.WithHandlerType(log.TextHandlerType),
    )
    
    // Create a no-op logger for testing
    nopLogger := log.NewNop()
    
    jsonLogger.Info("Using JSON logger")
    textLogger.Info("Using text logger")
}

Log Levels

The package provides six log levels with the following hierarchy:

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 (includes stack trace)
    LevelFatal Level = 12  // Fatal errors (exits application)
)
Working with Log Levels
// Set global log level
log.SetDefaultLevel(log.LevelDebug)

// Get the default logger
logger := log.Default()

// Log at specific levels
logger.Debug("Debug information")
logger.Info("Informational message")
logger.Warn("Warning message")
logger.Error("Error message") // Automatically includes stack trace
logger.Fatal("Fatal message") // Exits with os.Exit(1)

// Parse level from string
level, err := log.ParseLevel("debug")
if err != nil {
    log.Error("Invalid log level", log.Err(err))
}

// Check if a level would be logged
if logger.Enabled(context.TODO(), log.LevelDebug.Level()) {
    // Only prepare expensive debug data if it would be logged
    debugData := prepareExpensiveDebugData()
    logger.Debug("Expensive debug info", slog.Any("data", debugData))
}

Named Loggers

Create hierarchical loggers with dot-separated names:

// Create named loggers
controllerLogger := log.Default().Named("controller")
deploymentLogger := controllerLogger.Named("deployment")
podLogger := deploymentLogger.Named("pod")

// Results in logger names: "controller", "controller.deployment", "controller.deployment.pod"
controllerLogger.Info("Controller started")
deploymentLogger.Info("Deployment created", slog.String("name", "my-app"))
podLogger.Info("Pod scheduled", slog.String("node", "worker-1"))

Advanced Features

Raw Data Logging

Log JSON and YAML data with automatic parsing:

// Raw JSON logging
logger.Info("Configuration loaded",
    log.RawJSON("config", `{"debug": true, "timeout": 30}`))

// Raw YAML logging  
logger.Info("Manifest applied",
    log.RawYAML("manifest", `
apiVersion: v1
kind: Pod
metadata:
  name: my-pod
`))
Error Handling Helpers
// Helper functions for common patterns
logger.Info("Type information", log.Type("object", myStruct))
logger.Error("Operation failed", log.Err(err))
Grouping and Context
// Group related fields
groupedLogger := logger.WithGroup("http")
groupedLogger.Info("Request received", 
    slog.String("method", "GET"), 
    slog.String("path", "/api/users"))
// Output: {"level":"info","msg":"Request received","http":{"method":"GET","path":"/api/users"}}

// Add persistent context
contextLogger := logger.With(
    slog.String("service", "user-api"),
    slog.String("version", "1.2.3"))
contextLogger.Info("Service started")
// Output: {"level":"info","msg":"Service started","service":"user-api","version":"1.2.3"}

Output Formats

JSON Format (Default)
{
  "level": "info",
  "logger": "controller.deployment",
  "msg": "Deployment created",
  "source": "controllers/deployment.go:45",
  "name": "my-app",
  "namespace": "default",
  "time": "2006-01-02T15:04:05Z"
}
Text Format
2006-01-02T15:04:05Z INFO logger=controller.deployment msg='Deployment created' source=controllers/deployment.go:45 name='my-app' namespace='default'

Configuration Options

// Available options when creating a logger
logger := log.NewLogger(
    log.WithLevel(slog.LevelDebug),           // Set log level
    log.WithOutput(os.Stdout),                // Set output writer
    log.WithHandlerType(log.JSONHandlerType), // JSON or Text handler
    log.WithTimeFunc(time.Now),               // Custom time function for testing
)

Global Logger Management

// Set a custom logger as the global default
customLogger := log.NewLogger(log.WithLevel(slog.LevelDebug))
log.SetDefault(customLogger)

// Change the global log level
log.SetDefaultLevel(log.LevelError)

// Get the current global logger
currentLogger := log.Default()

Best Practices

  1. Use structured logging: Always prefer key-value pairs over formatted strings

    // Good
    log.Info("User created", 
        slog.Uint64("userID", user.ID), 
        slog.String("role", user.Role))
    
    // Avoid (deprecated)
    log.Infof("User created: ID=%d, Role=%s", user.ID, user.Role)
    
  2. Use appropriate log levels:

    • Trace: Very detailed debugging information
    • Debug: Detailed information for debugging (enables source location)
    • Info: General information about application flow
    • Warn: Something unexpected but recoverable happened
    • Error: An error occurred but application can continue (includes stack trace)
    • Fatal: A fatal error occurred, application will exit
  3. Use named loggers for components:

    controllerLogger := log.Default().Named("deployment-controller")
    controllerLogger.Info("Starting reconciliation", 
        slog.String("namespace", ns), 
        slog.String("name", name))
    
  4. Use context-aware logging:

    log.InfoContext(ctx, "Processing request", 
        slog.String("traceID", getTraceID(ctx)),
        slog.String("userID", getUserID(ctx)))
    
  5. Leverage persistent context:

    requestLogger := log.Default().With(
        slog.String("requestID", reqID),
        slog.String("userID", userID))
    requestLogger.Info("Request started")
    requestLogger.Info("Request completed")
    

Error Handling and Stack Traces

The package automatically captures stack traces for Error and Fatal level logs:

// Stack trace is automatically captured
log.Error("Database connection failed", log.Err(err))

// Manual context with stack trace
ctx := context.Background()
log.ErrorContext(ctx, "Critical failure", slog.String("component", "auth"))

Development Features

IDE Integration

Set the IDEA_DEVELOPMENT environment variable to get IDE-friendly source formatting:

export IDEA_DEVELOPMENT=1

This changes the source format from file.go:123 to file.go:123 for better IDE integration.

Testing Support
// Create a logger that discards output for tests
testLogger := log.NewNop()

// Create a logger with custom time for deterministic tests
testLogger := log.NewLogger(
    log.WithTimeFunc(func(t time.Time) time.Time {
        return time.Date(2006, 1, 2, 15, 4, 5, 0, time.UTC)
    }))

Migration from Printf-style Logging

The package provides deprecated printf-style methods for backward compatibility, but structured logging is recommended:

// Deprecated - will be removed
log.Infof("User %s logged in", username)

// Recommended
log.Info("User logged in", slog.String("username", username))

Documentation

Overview

global logger is deprecated

Index

Constants

View Source
const (
	LoggerNameKey = "logger"
	StacktraceKey = "stacktrace"
)
View Source
const KeyComponent = "component"

Variables

This section is empty.

Functions

func Debug

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

func DebugContext

func DebugContext(ctx context.Context, msg string, args ...any)

func Debugf deprecated

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

Deprecated: use Debug instead

func Err

func Err(err error) slog.Attr

func Error

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

func ErrorContext

func ErrorContext(ctx context.Context, msg string, args ...any)

func Errorf deprecated

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

Deprecated: use Error instead

func Fatal

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

func FatalContext

func FatalContext(ctx context.Context, msg string, args ...any)

func Fatalf deprecated

func Fatalf(format string, args ...any)

Deprecated: use Fatal instead

func Info

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

func InfoContext

func InfoContext(ctx context.Context, msg string, args ...any)

func Infof deprecated

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

Deprecated: use Info instead

func Log

func Log(ctx context.Context, level Level, msg string, args ...any)

func LogAttrs

func LogAttrs(ctx context.Context, level Level, msg string, attrs ...slog.Attr)

func Logf deprecated

func Logf(ctx context.Context, level Level, format string, args ...any)

Deprecated: use Log instead

func RawJSON

func RawJSON(key, text string) slog.Attr

func RawYAML

func RawYAML(key, text string) slog.Attr

func SetDefault

func SetDefault(l *Logger)

func SetDefaultLevel

func SetDefaultLevel(l Level)

func Type

func Type(key string, t any) slog.Attr

func Warn

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

func WarnContext

func WarnContext(ctx context.Context, msg string, args ...any)

func Warnf deprecated

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

Deprecated: use Warn instead

Types

type AddSourceVar

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

func (*AddSourceVar) Set

func (v *AddSourceVar) Set(b bool)

func (*AddSourceVar) Source

func (v *AddSourceVar) Source() *bool

func (*AddSourceVar) String

func (v *AddSourceVar) String() string

type Handler

type Handler interface {
	Enabled(context.Context, slog.Level) bool
	Handle(ctx context.Context, r slog.Record) error
	Named(name string) slog.Handler
	SetOutput(w io.Writer)
	WithAttrs(attrs []slog.Attr) slog.Handler
	WithGroup(name string) slog.Handler
}

type HandlerType

type HandlerType int
const (
	JSONHandlerType HandlerType = iota
	TextHandlerType
)

type Level

type Level slog.Level
const (
	LevelTrace Level = -8
	LevelDebug Level = -4
	LevelInfo  Level = 0
	LevelWarn  Level = 4
	LevelError Level = 8
	LevelFatal Level = 12
)

func LogLevelFromStr

func LogLevelFromStr(rawLogLevel string) Level

func ParseLevel

func ParseLevel(rawLogLevel string) (Level, error)

func (Level) Level

func (l Level) Level() slog.Level

func (Level) String

func (l Level) String() string

type LogOutput

type LogOutput struct {
	Level      string         `json:"level"`
	Name       string         `json:"logger"`
	Message    string         `json:"msg"`
	Source     string         `json:"source"`
	Fields     map[string]any `json:"-"`
	Stacktrace string         `json:"stacktrace"`
	Time       string         `json:"time"`
}

func (*LogOutput) MarshalJSON

func (lo *LogOutput) MarshalJSON() ([]byte, error)

func (*LogOutput) Text

func (lo *LogOutput) Text() ([]byte, error)

type Logger

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

func Default

func Default() *Logger

func NewLogger

func NewLogger(opts ...Option) *Logger

func NewNop

func NewNop() *Logger

func (*Logger) Debugf deprecated

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

Deprecated: use Debug instead

func (*Logger) Error

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

func (*Logger) Errorf deprecated

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

Deprecated: use Error instead

func (*Logger) Fatal

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

func (*Logger) Fatalf deprecated

func (l *Logger) Fatalf(format string, args ...any)

Deprecated: use Fatal instead

func (*Logger) GetLevel

func (l *Logger) GetLevel() Level

func (*Logger) Infof deprecated

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

Deprecated: use Info instead

func (*Logger) Logf deprecated

func (l *Logger) Logf(ctx context.Context, level Level, format string, args ...any)

Deprecated: use Log instead

func (*Logger) Named

func (l *Logger) Named(name string) *Logger

func (*Logger) SetLevel

func (l *Logger) SetLevel(level Level)

func (*Logger) SetOutput

func (l *Logger) SetOutput(w io.Writer)

func (*Logger) Warnf deprecated

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

Deprecated: use Warn instead

func (*Logger) With

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

func (*Logger) WithGroup

func (l *Logger) WithGroup(name string) *Logger

type Option

type Option func(*Options)

func WithHandlerType

func WithHandlerType(handlerType HandlerType) Option

WithHandlerType sets the handler type

func WithLevel

func WithLevel(level slog.Level) Option

WithLevel sets the logging level

func WithOutput

func WithOutput(output io.Writer) Option

WithOutput sets the output writer

func WithTimeFunc

func WithTimeFunc(timeFunc func(t time.Time) time.Time) Option

WithTimeFunc sets the time function

type Options

type Options struct {
	Level       slog.Level
	Output      io.Writer
	HandlerType HandlerType
	TimeFunc    func(t time.Time) time.Time
}

type Raw

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

func NewJSONRaw

func NewJSONRaw(text string) *Raw

made them public to use without slog.Attr

func NewYAMLRaw

func NewYAMLRaw(text string) *Raw

func (*Raw) LogValue

func (r *Raw) LogValue() slog.Value

type Render

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

func (*Render) FieldsToString

func (r *Render) FieldsToString(m any, keyPrefix string)

func (*Render) JSONKeyValue

func (r *Render) JSONKeyValue(key, value string)

func (*Render) RawJsonKeyValue

func (r *Render) RawJsonKeyValue(key, value string)

func (*Render) TextKeyValue

func (r *Render) TextKeyValue(key, value string)

func (*Render) TextQuotedKeyValue

func (r *Render) TextQuotedKeyValue(key, value string)

type SlogJsonHandler

type SlogJsonHandler struct {
	slog.Handler
	// contains filtered or unexported fields
}

func NewJSONHandler

func NewJSONHandler(out io.Writer, opts *slog.HandlerOptions, timeFn func(t time.Time) time.Time) *SlogJsonHandler

func NewSlogHandler

func NewSlogHandler(handler slog.Handler) *SlogJsonHandler

func (*SlogJsonHandler) Handle

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

func (*SlogJsonHandler) Named

func (h *SlogJsonHandler) Named(name string) slog.Handler

func (*SlogJsonHandler) SetOutput

func (h *SlogJsonHandler) SetOutput(w io.Writer)

func (*SlogJsonHandler) WithAttrs

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

func (*SlogJsonHandler) WithGroup

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

type SlogTextHandler

type SlogTextHandler struct {
	slog.Handler
	// contains filtered or unexported fields
}

func NewSlogTextHandler

func NewSlogTextHandler(handler slog.Handler) *SlogTextHandler

func NewTextHandler

func NewTextHandler(out io.Writer, opts *slog.HandlerOptions, timeFn func(t time.Time) time.Time) *SlogTextHandler

func (*SlogTextHandler) Handle

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

func (*SlogTextHandler) Named

func (h *SlogTextHandler) Named(name string) slog.Handler

func (*SlogTextHandler) SetOutput

func (h *SlogTextHandler) SetOutput(w io.Writer)

func (*SlogTextHandler) WithAttrs

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

func (*SlogTextHandler) WithGroup

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

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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