logging

package
v0.3.4 Latest Latest
Warning

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

Go to latest
Published: Jan 14, 2026 License: GPL-3.0 Imports: 19 Imported by: 0

Documentation

Overview

internal/logging/config.go

internal/logging/context.go

Package logging provides structured logging with OpenTelemetry integration.

Overview

Logging package wraps Zap with:

  • Custom Trace level (-2, below Debug)
  • Dual output (stdout + OpenTelemetry)
  • Automatic context field injection (trace_id, tenant, session)
  • Defense-in-depth secret redaction
  • Level-aware sampling (errors never sampled)

Usage

Create logger from config:

cfg := logging.NewDefaultConfig()
logger, err := logging.NewLogger(cfg, otelProvider)
if err != nil {
    log.Fatal(err)
}
defer logger.Sync()

Log with context:

ctx := logging.WithTenant(ctx, &logging.Tenant{OrgID: "acme"})
ctx = logging.WithSessionID(ctx, "sess_123")
logger.Info(ctx, "request processed", zap.Duration("duration", d))

Output includes automatic correlation:

{
  "ts": "2025-11-24T10:15:30Z",
  "level": "info",
  "msg": "request processed",
  "trace_id": "abc123",
  "tenant.org": "acme",
  "session.id": "sess_123",
  "duration": "45ms"
}

Configuration Precedence

Configuration follows standard contextd precedence:

  1. Defaults (NewDefaultConfig)
  2. File (config.yaml)
  3. Environment variables (CONTEXTD_LOGGING_*)

Secret Redaction

Secrets are redacted at multiple layers:

  1. Domain primitives (config.Secret type)
  2. Encoder-level field name filtering
  3. Encoder-level pattern matching

Use helpers for manual redaction:

logger.Info(ctx, "auth received",
    logging.RedactedString("authorization", authHeader))

Sampling

Level-aware sampling prevents log floods:

  • Trace: first 1 per second, drop rest
  • Debug: first 10 per second, drop rest
  • Info: first 100, then 1 every 10
  • Warn: first 100, then 1 every 100
  • Error+: never sampled

Disable for debugging:

cfg.Sampling.Enabled = false

Testing

Use TestLogger for test assertions:

tl := logging.NewTestLogger()
tl.Info(ctx, "test message", zap.String("key", "value"))
tl.AssertLogged(t, zapcore.InfoLevel, "test message")
tl.AssertField(t, "test message", "key", "value")
tl.AssertNoSecrets(t)

Concurrency Safety

Logger is safe for concurrent use. Child loggers (With, Named) are independent and do not affect parent or siblings.

Performance

Logging overhead: <1ms per entry in hot paths Zero allocations when level disabled Sampling reduces volume by ~90% in high-throughput scenarios

internal/logging/levels.go

internal/logging/logger.go

internal/logging/otel.go

internal/logging/redact.go

internal/logging/sampling.go

internal/logging/testing.go

Index

Constants

View Source
const TraceLevel = zapcore.Level(-2)

TraceLevel is a custom level below Debug for ultra-verbose logging. Value: -2 (Debug is -1, Info is 0)

Use for:

  • Function entry/exit
  • Wire protocol data
  • Byte-level details
  • Almost always filtered in production

Variables

This section is empty.

Functions

func ContextFields

func ContextFields(ctx context.Context) []zap.Field

ContextFields extracts correlation data from context.

func DefaultLevelSamplingConfig

func DefaultLevelSamplingConfig() map[zapcore.Level]LevelSamplingConfig

DefaultLevelSamplingConfig returns default sampling config by level.

func LevelFromString

func LevelFromString(level string) (zapcore.Level, error)

LevelFromString parses a string into a zapcore.Level, supporting "trace".

func RedactedString

func RedactedString(key, val string) zap.Field

RedactedString creates a Zap field with redacted value and length.

func RequestIDFromContext

func RequestIDFromContext(ctx context.Context) string

RequestIDFromContext extracts request ID from context.

func Secret

func Secret(key string, val config.Secret) zap.Field

Secret creates a Zap field for config.Secret with redaction indicator.

func SessionIDFromContext

func SessionIDFromContext(ctx context.Context) string

SessionIDFromContext extracts session ID from context.

func WithLogger

func WithLogger(ctx context.Context, logger *Logger) context.Context

WithLogger stores logger in context.

func WithRequestID

func WithRequestID(ctx context.Context, requestID string) context.Context

WithRequestID adds request ID to context. Panics if requestID is empty or contains invalid characters.

func WithSessionID

func WithSessionID(ctx context.Context, sessionID string) context.Context

WithSessionID adds session ID to context. Panics if sessionID is empty or contains invalid characters.

func WithTenant

func WithTenant(ctx context.Context, tenant *Tenant) context.Context

WithTenant adds tenant to context. Panics if tenant is nil or contains invalid field values.

Types

type CallerConfig

type CallerConfig struct {
	Enabled bool `koanf:"enabled"`
	Skip    int  `koanf:"skip"`
}

CallerConfig controls caller information in logs.

type Config

type Config struct {
	Level      zapcore.Level     `koanf:"level"`
	Format     string            `koanf:"format"`
	Output     OutputConfig      `koanf:"output"`
	Sampling   SamplingConfig    `koanf:"sampling"`
	Caller     CallerConfig      `koanf:"caller"`
	Stacktrace StacktraceConfig  `koanf:"stacktrace"`
	Fields     map[string]string `koanf:"fields"`
	Redaction  RedactionConfig   `koanf:"redaction"`
}

Config holds logging configuration.

func NewDefaultConfig

func NewDefaultConfig() *Config

NewDefaultConfig returns config with production-ready defaults.

func (*Config) Validate

func (c *Config) Validate() error

Validate checks config for errors.

type LevelSamplingConfig

type LevelSamplingConfig struct {
	Initial    int `koanf:"initial"`
	Thereafter int `koanf:"thereafter"`
}

LevelSamplingConfig defines sampling rate per level.

type Logger

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

Logger wraps Zap with context-aware methods.

func FromContext

func FromContext(ctx context.Context) *Logger

FromContext retrieves logger from context. Returns a default nop logger if not found.

func NewLogger

func NewLogger(cfg *Config, otelProvider log.LoggerProvider) (*Logger, error)

NewLogger creates a logger from config. otelProvider can be nil to disable OTEL output.

func (*Logger) DPanic

func (l *Logger) DPanic(ctx context.Context, msg string, fields ...zap.Field)

func (*Logger) Debug

func (l *Logger) Debug(ctx context.Context, msg string, fields ...zap.Field)

func (*Logger) Enabled

func (l *Logger) Enabled(level zapcore.Level) bool

Enabled returns true if the given level is enabled.

func (*Logger) Error

func (l *Logger) Error(ctx context.Context, msg string, fields ...zap.Field)

func (*Logger) Fatal

func (l *Logger) Fatal(ctx context.Context, msg string, fields ...zap.Field)

func (*Logger) Info

func (l *Logger) Info(ctx context.Context, msg string, fields ...zap.Field)

func (*Logger) Named

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

func (*Logger) Sync

func (l *Logger) Sync() error

Sync flushes any buffered log entries.

func (*Logger) Trace

func (l *Logger) Trace(ctx context.Context, msg string, fields ...zap.Field)

func (*Logger) Underlying

func (l *Logger) Underlying() *zap.Logger

Underlying returns the underlying zap.Logger. Useful when integrating with libraries that require a *zap.Logger.

func (*Logger) Warn

func (l *Logger) Warn(ctx context.Context, msg string, fields ...zap.Field)

func (*Logger) With

func (l *Logger) With(fields ...zap.Field) *Logger

type OutputConfig

type OutputConfig struct {
	Stdout bool `koanf:"stdout"`
	OTEL   bool `koanf:"otel"`
}

OutputConfig controls where logs are written.

type RedactingEncoder

type RedactingEncoder struct {
	zapcore.Encoder
	// contains filtered or unexported fields
}

RedactingEncoder wraps a zapcore.Encoder to redact sensitive fields.

func NewRedactingEncoder

func NewRedactingEncoder(base zapcore.Encoder, cfg RedactionConfig) (*RedactingEncoder, error)

NewRedactingEncoder wraps an encoder with redaction rules. Returns error if any redaction pattern fails to compile.

func (*RedactingEncoder) AddArray

func (e *RedactingEncoder) AddArray(key string, arr zapcore.ArrayMarshaler) error

AddArray redacts sensitive field names.

func (*RedactingEncoder) AddBinary

func (e *RedactingEncoder) AddBinary(key string, val []byte)

AddBinary redacts sensitive field names.

func (*RedactingEncoder) AddByteString

func (e *RedactingEncoder) AddByteString(key string, val []byte)

AddByteString redacts sensitive field names.

func (*RedactingEncoder) AddObject

func (e *RedactingEncoder) AddObject(key string, obj zapcore.ObjectMarshaler) error

AddObject redacts sensitive field names.

func (*RedactingEncoder) AddReflected

func (e *RedactingEncoder) AddReflected(key string, val interface{}) error

AddReflected redacts sensitive field names. Note: This redacts the entire reflected value if the key is sensitive. For deep inspection of reflected structs/maps, use explicit zap.Object() with custom marshalers.

func (*RedactingEncoder) AddString

func (e *RedactingEncoder) AddString(key, val string)

AddString redacts sensitive field names and value patterns.

func (*RedactingEncoder) Clone

func (e *RedactingEncoder) Clone() zapcore.Encoder

Clone creates a copy of the encoder.

type RedactionConfig

type RedactionConfig struct {
	Enabled  bool     `koanf:"enabled"`
	Fields   []string `koanf:"fields"`
	Patterns []string `koanf:"patterns"`
}

RedactionConfig controls sensitive data redaction.

type SamplingConfig

type SamplingConfig struct {
	Enabled bool                                  `koanf:"enabled"`
	Tick    config.Duration                       `koanf:"tick"`
	Levels  map[zapcore.Level]LevelSamplingConfig `koanf:"levels"`
}

SamplingConfig controls log volume reduction.

type StacktraceConfig

type StacktraceConfig struct {
	Level zapcore.Level `koanf:"level"`
}

StacktraceConfig controls stacktrace inclusion.

type Tenant

type Tenant struct {
	OrgID     string
	TeamID    string
	ProjectID string
}

Tenant represents multi-tenant context.

func TenantFromContext

func TenantFromContext(ctx context.Context) *Tenant

TenantFromContext extracts tenant from context.

type TestLogger

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

TestLogger wraps Logger with test observation capabilities.

func NewTestLogger

func NewTestLogger() *TestLogger

NewTestLogger creates a logger for testing with full observation.

func (*TestLogger) All

func (t *TestLogger) All() []observer.LoggedEntry

All returns all logged entries.

func (*TestLogger) AssertField

func (t *TestLogger) AssertField(tb testing.TB, msg, key string, expected interface{})

AssertField verifies a field with key and value exists in message.

func (*TestLogger) AssertLogged

func (t *TestLogger) AssertLogged(tb testing.TB, level zapcore.Level, msgContains string)

AssertLogged verifies a log at level containing message was logged.

func (*TestLogger) AssertNoSecrets

func (t *TestLogger) AssertNoSecrets(tb testing.TB)

AssertNoSecrets verifies no sensitive data leaked in logs.

func (*TestLogger) AssertNotLogged

func (t *TestLogger) AssertNotLogged(tb testing.TB, level zapcore.Level, msgContains string)

AssertNotLogged verifies no log at level containing message was logged.

func (*TestLogger) AssertTraceCorrelation

func (t *TestLogger) AssertTraceCorrelation(tb testing.TB, msg string)

AssertTraceCorrelation verifies trace_id present in message.

func (*TestLogger) FilterMessage

func (t *TestLogger) FilterMessage(msg string) *observer.ObservedLogs

FilterMessage returns entries matching message substring.

func (*TestLogger) Reset

func (t *TestLogger) Reset()

Reset clears all logged entries.

Jump to

Keyboard shortcuts

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