log

package
v1.3.2 Latest Latest
Warning

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

Go to latest
Published: Apr 26, 2026 License: Apache-2.0 Imports: 5 Imported by: 0

README

Shared Logging Library

Authority: DD-005 v2.0 (Observability Standards - Unified Logging Interface)

Overview

This package provides a unified logging interface for all Kubernaut services using:

  • logr.Logger as the standard interface across all services
  • zap as the high-performance backend (hidden inside this package)

Architecture

┌─────────────────────────────────────────────────────────────┐
│                     Your Code                                │
│                                                              │
│   logger.Info("Working", "key", "value")                    │
│              │                                               │
│              ▼                                               │
│   ┌─────────────────────┐                                   │
│   │   logr.Logger       │  ← Interface (what your code uses)│
│   │   (unified API)     │                                   │
│   └─────────┬───────────┘                                   │
│             │                                                │
│             ▼                                                │
│   ┌─────────────────────┐                                   │
│   │   zapr adapter      │  ← Converts logr calls to zap     │
│   │   (pkg/log only)    │                                   │
│   └─────────┬───────────┘                                   │
│             │                                                │
│             ▼                                                │
│   ┌─────────────────────┐                                   │
│   │   zap.Logger        │  ← High-performance backend       │
│   │   (hidden inside    │     (JSON output, zero-alloc)     │
│   │    pkg/log)         │                                   │
│   └─────────────────────┘                                   │
└─────────────────────────────────────────────────────────────┘

Key Rule: When to Use What

Scenario What to Import Example
main.go (create logger) kubelog "github.com/jordigilh/kubernaut/pkg/log" logger := kubelog.NewLogger(opts)
Struct fields "github.com/go-logr/logr" logger logr.Logger
Function parameters "github.com/go-logr/logr" func New(logger logr.Logger)
Business code "github.com/go-logr/logr" logger.Info("message")

⚠️ NEVER import "go.uber.org/zap" directly in business code.

The zap backend is hidden inside pkg/log. Your code only sees logr.Logger.

Why logr.Logger?

Benefit Description
Unified Interface Single logr.Logger type across stateless and CRD controller services
controller-runtime Native CRD controllers use logr natively
zap Performance High performance (zero-allocation) via zapr adapter
Shared Library Consistency All pkg/* libraries accept logr.Logger
Structured JSON Output Consistent format across all services
Industry Standard logr is the Kubernetes ecosystem standard

Usage

Stateless HTTP Services (Gateway, Data Storage, Context API)
import "github.com/jordigilh/kubernaut/pkg/log"

func main() {
    // Create logger with production defaults
    logger := log.NewLogger(log.Options{
        Development: false,
        Level:       0, // INFO
        ServiceName: "data-storage",
    })
    defer log.Sync(logger)

    // Pass to components
    server := NewServer(cfg, logger.WithName("server"))
    handler := NewHandler(logger.WithName("handler"))
}
CRD Controllers
import (
    ctrl "sigs.k8s.io/controller-runtime"
)

func main() {
    // Use native logr from controller-runtime
    logger := ctrl.Log.WithName("notification-controller")

    // Pass to shared libraries (compatible interface)
    auditStore := audit.NewBufferedStore(client, config, "notification", logger.WithName("audit"))
}
Shared Libraries (pkg/*)
import "github.com/go-logr/logr"  // ✅ Import interface only, NOT zap

type Component struct {
    logger logr.Logger  // ✅ Store interface, NOT *zap.Logger
}

// Accept logr.Logger - works with both stateless services and CRD controllers
func NewComponent(logger logr.Logger) *Component {
    return &Component{logger: logger.WithName("component")}
}

func (c *Component) DoWork() {
    // ✅ Use logr syntax (key-value pairs)
    c.logger.Info("Processing request",
        "key", "value",
        "count", 42,
    )
}

⚠️ DO NOT do this in shared libraries:

import "go.uber.org/zap"  // ❌ WRONG - don't import zap directly

type Component struct {
    logger *zap.Logger    // ❌ WRONG - don't use zap type
}
Environment-Based Configuration
// Reads LOG_LEVEL, LOG_FORMAT, SERVICE_NAME from environment
logger := log.NewLoggerFromEnvironment()

Environment variables:

  • LOG_LEVEL: "debug", "info", "warn", "error" (default: "info")
  • LOG_FORMAT: "json", "console" (default: "json")
  • SERVICE_NAME: Service identifier for logs

Log Levels (DD-005)

Level V() Use Case Example
INFO V(0) Normal operational events logger.Info("Request processed")
DEBUG V(1) Detailed debugging information logger.V(1).Info("Parsing payload")
TRACE V(2) Very detailed tracing logger.V(2).Info("Field value", "field", value)
ERROR N/A Error conditions (actionable) logger.Error(err, "Failed to process")

Standard Fields

Use the constants in fields.go for consistent field names:

import "github.com/jordigilh/kubernaut/pkg/log"

logger.Info("Request processed",
    log.KeyRequestID, requestID,
    log.KeyDurationMS, durationMs,
    log.KeyStatusCode, statusCode,
    log.KeyEndpoint, "/api/v1/workflows",
)

Migration from *zap.Logger

Before (old pattern)
import "go.uber.org/zap"

type Component struct {
    logger *zap.Logger
}

func NewComponent(logger *zap.Logger) *Component {
    return &Component{logger: logger}
}

func (c *Component) DoWork() {
    c.logger.Info("Working",
        zap.String("key", "value"),
        zap.Int("count", 42),
    )
}
After (new pattern)
import "github.com/go-logr/logr"  // ✅ Interface only

type Component struct {
    logger logr.Logger  // ✅ Interface type
}

func NewComponent(logger logr.Logger) *Component {
    return &Component{logger: logger}
}

func (c *Component) DoWork() {
    // ✅ logr syntax: key-value pairs (zap runs underneath!)
    c.logger.Info("Working",
        "key", "value",
        "count", 42,
    )
}

Note: Even though you're using logr.Logger, the actual logging is done by zap under the hood. You get zap's performance without importing it directly.

Key Differences
Aspect *zap.Logger logr.Logger
Field syntax zap.String("key", "value") "key", "value"
Error logging logger.Error("msg", zap.Error(err)) logger.Error(err, "msg")
Debug logging logger.Debug("msg") logger.V(1).Info("msg")
Named loggers logger.Named("sub") logger.WithName("sub")
With fields logger.With(zap.String(...)) logger.WithValues("key", "value")

Interoperability

If you need a *zap.Logger for legacy code:

logger := log.NewLogger(opts)
zapLogger := log.GetZapLogger(logger)
if zapLogger != nil {
    legacyComponent := NewLegacyComponent(zapLogger)
}

Files

  • logger.go - Core logger creation and configuration
  • fields.go - Standard field keys and values
  • README.md - This documentation

Documentation

Overview

Package log provides a unified logging interface for all Kubernaut services.

Authority: DD-005 v2.0 (Observability Standards - Unified Logging Interface)

This package provides: - logr.Logger as the unified interface across all services - zap as the high-performance backend via zapr adapter - Consistent configuration for production and development - Helper functions for common logging patterns

Usage:

Stateless HTTP Services (Gateway, Data Storage, Context API):

logger := log.NewLogger(log.Options{
    Development: false,
    Level:       0, // INFO
})
defer log.Sync(logger)

// Pass to components
server := NewServer(cfg, logger.WithName("server"))
handler := NewHandler(logger.WithName("handler"))

CRD Controllers:

// Use native logr from controller-runtime
logger := ctrl.Log.WithName("notification-controller")

// Pass to shared libraries (compatible interface)
auditStore := audit.NewBufferedStore(client, config, "notification", logger.WithName("audit"))

Shared Libraries (pkg/*):

// Accept logr.Logger - works with both stateless services and CRD controllers
func NewComponent(logger logr.Logger) *Component {
    return &Component{logger: logger.WithName("component")}
}

Index

Constants

View Source
const (
	// Request tracing fields
	KeyRequestID = "request_id"
	KeyTraceID   = "trace_id"
	KeySpanID    = "span_id"

	// Service identification
	KeyService     = "service"
	KeyComponent   = "component"
	KeyEnvironment = "environment"
	KeyVersion     = "version"
	KeyGitCommit   = "git_commit"
	KeyBuildDate   = "build_date"

	// Kubernetes context
	KeyNamespace  = "namespace"
	KeyPodName    = "pod_name"
	KeyNodeName   = "node_name"
	KeyCluster    = "cluster"
	KeyController = "controller"

	// HTTP request fields
	KeyMethod     = "method"
	KeyEndpoint   = "endpoint"
	KeyPath       = "path"
	KeyStatusCode = "status_code"
	KeySourceIP   = "source_ip"
	KeyUserAgent  = "user_agent"

	// Performance metrics
	KeyDurationMS      = "duration_ms"
	KeyDurationSeconds = "duration_seconds"
	KeyBytesProcessed  = "bytes_processed"
	KeyRetryCount      = "retry_count"

	// Business domain fields
	KeySignalName       = "signal_name"
	KeyFingerprint      = "fingerprint"
	KeySeverity         = "severity"
	KeyWorkflowID       = "workflow_id"
	KeyWorkflowName     = "workflow_name"
	KeyWorkflowVersion  = "workflow_version"
	KeyIncidentType     = "incident_type"
	KeyRemediationID    = "remediation_id"
	KeyCorrelationID    = "correlation_id"
	KeyAlertFingerprint = "alert_fingerprint"

	// Database fields
	KeyDatabase  = "database"
	KeyTable     = "table"
	KeyQuery     = "query"
	KeyRowCount  = "row_count"
	KeyOperation = "operation"

	// Error fields
	KeyError      = "error"
	KeyErrorCode  = "error_code"
	KeyErrorType  = "error_type"
	KeyStackTrace = "stack_trace"

	// Audit fields
	KeyEventType = "event_type"
	KeyOutcome   = "outcome"
	KeyActor     = "actor"
	KeyResource  = "resource"
	KeyAction    = "action"

	// Graceful shutdown fields (DD-007)
	KeyPhase  = "phase"
	KeyStatus = "status"
)

Standard field keys for structured logging.

DD-005: Use these constants to ensure consistent field names across all services. This enables effective log aggregation and querying in production.

Example:

logger.Info("Request processed",
    log.KeyRequestID, requestID,
    log.KeyDurationMS, durationMs,
    log.KeyStatusCode, statusCode,
)
View Source
const (
	// Outcomes
	OutcomeSuccess = "success"
	OutcomeFailure = "failure"
	OutcomeSkipped = "skipped"

	// Phases (DD-007 graceful shutdown)
	PhaseStartup          = "startup"
	PhaseRunning          = "running"
	PhaseShutdown         = "shutdown"
	PhaseEndpointRemoval  = "endpoint_removal"
	PhaseDrainingConns    = "draining_connections"
	PhaseClosingResources = "closing_resources"
	PhaseShutdownComplete = "shutdown_complete"

	// Statuses
	StatusHealthy   = "healthy"
	StatusUnhealthy = "unhealthy"
	StatusDegraded  = "degraded"
	StatusReady     = "ready"
	StatusNotReady  = "not_ready"
)

Standard field values for common scenarios.

Variables

This section is empty.

Functions

func GetZapLogger

func GetZapLogger(logger logr.Logger) *zap.Logger

GetZapLogger extracts the underlying *zap.Logger from a logr.Logger.

This is useful for interoperability with libraries that require *zap.Logger. Returns nil if the logger is not backed by zap.

DD-005: Migration helper - use sparingly, prefer logr.Logger interface

Example:

logger := log.NewLogger(opts)
zapLogger := log.GetZapLogger(logger)
if zapLogger != nil {
    // Use with legacy code that requires *zap.Logger
    legacyComponent := NewLegacyComponent(zapLogger)
}

func NewLogger

func NewLogger(opts Options) logr.Logger

NewLogger creates a new logr.Logger with the specified options.

DD-005 v2.0: This is the primary way to create loggers for stateless services. CRD controllers should use ctrl.Log from controller-runtime instead.

Example:

logger := log.NewLogger(log.Options{
    Development: false,
    Level:       0,
    ServiceName: "data-storage",
})
defer log.Sync(logger)

func NewLoggerFromEnvironment

func NewLoggerFromEnvironment() logr.Logger

NewLoggerFromEnvironment creates a logger configured from environment variables.

Environment variables: - LOG_LEVEL: "debug", "info", "warn", "error" (default: "info") - LOG_FORMAT: "json", "console" (default: "json") - SERVICE_NAME: Service identifier for logs

DD-005: Environment-driven configuration for Kubernetes deployments

func Sync

func Sync(logger logr.Logger)

Sync flushes any buffered log entries.

Call this before the application exits to ensure all logs are written. This is a best-effort operation - errors are ignored.

Example:

logger := log.NewLogger(opts)
defer log.Sync(logger)

Types

type Options

type Options struct {
	// Development enables development mode with human-readable output.
	// Default: false (production mode with JSON output)
	Development bool

	// Level sets the minimum logging level.
	// DD-005 verbosity levels:
	// - 0 = INFO (default, always shown)
	// - 1 = DEBUG (shown when verbosity >= 1)
	// - 2 = TRACE (shown when verbosity >= 2)
	//
	// In production, use 0. In development, use 1 or 2.
	Level int

	// ServiceName is added to all log entries for identification.
	// Example: "gateway", "data-storage", "notification-controller"
	ServiceName string

	// DisableCaller disables caller information in log entries.
	// Default: false (caller info enabled)
	DisableCaller bool

	// DisableStacktrace disables stack traces for error-level logs.
	// Default: false (stack traces enabled for errors)
	DisableStacktrace bool
}

Options configures the logger behavior.

DD-005: Standard configuration options for all services

func DefaultOptions

func DefaultOptions() Options

DefaultOptions returns production-ready default options.

DD-005: Production defaults - JSON output format - INFO level (V=0) - Caller info enabled - Stack traces enabled for errors

func DevelopmentOptions

func DevelopmentOptions() Options

DevelopmentOptions returns development-friendly options.

DD-005: Development defaults - Human-readable console output - DEBUG level (V=1) - Caller info enabled - Stack traces enabled for errors

Jump to

Keyboard shortcuts

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