mtlog

Message Template Logging for Go

Serilog-inspired structured logging with message templates,
rich formatting, and native Seq support.

17.3ns
per operation
0
allocations
Pipeline
architecture

Why mtlog?

Message Templates

log.Info("User {UserId} logged in from {IP}", userId, ip)
log.Info("Order {@Order} created", order)
log.Info("Processing time: {Duration:F2}ms", 123.456)

Properties are extracted from templates and preserved throughout the pipeline. Templates serve as both human-readable messages and event types.

Type-Safe ForType

userLogger := mtlog.ForType[User](logger)
userLogger.Info("User created")
// SourceContext: "User"

orderLogger := mtlog.ForType[OrderService](logger)
// Automatic categorization by type

Automatic source context from Go types. No more string constants. Perfect for service-oriented architectures.

Zero Allocations

// Simple logging: 0 allocations
log.Info("Application started")

// Below minimum level: 0 allocations
log.Debug("This won't allocate")

Carefully optimized for the common path. Simple messages and filtered events allocate zero bytes.

Ecosystem Compatible

// Works with slog
slogger := mtlog.NewSlogLogger(...)
slog.SetDefault(slogger)

// Works with logr (Kubernetes)
import mtlogr "github.com/willibrandon/mtlog/adapters/logr"
logrLogger := mtlogr.NewLogger(...)

Drop-in adapters for Go's standard slog and Kubernetes logr. Use mtlog's power with existing code.

Performance Characteristics

Zero-Allocation Path

  • • Simple string messages: 0 allocs
  • • Below minimum level: 0 allocs
  • • Dynamic level checks: 0 allocs
  • • Filtered events: 0 allocs

Operation Timing

  • • Simple logging: 17.3 ns/op
  • • Level filtering: 1.5 ns/op
  • • With properties: ~200 ns/op
  • • ForType (cached): ~148 ns/op

Carefully optimized for the common path with zero allocations for simple operations.

Benchmarked with go test -bench . -benchmem on AMD Ryzen 9 9950X, Go 1.23

View detailed benchmarks →

Quick Start

go get mtlog.dev
package main

import (
    "mtlog.dev"
    "mtlog.dev/core"
)

func main() {
    // Create a logger with console output
    log := mtlog.New(
        mtlog.WithConsole(),
        mtlog.WithMinimumLevel(core.InformationLevel),
    )

    // Simple logging
    log.Info("Application started")
    
    // Message templates with properties
    userId := 123
    log.Info("User {UserId} logged in", userId)
    
    // Type-based logging
    userLogger := mtlog.ForType[User](log)
    userLogger.Info("User operation completed")
}