The promslog package provides standardized logging configuration for Prometheus components using Go's log/slog package. It offers consistent logging formats, levels, and configuration across the Prometheus ecosystem.
import "github.com/prometheus/common/promslog"This package wraps Go's standard log/slog package with Prometheus-specific conventions, supporting multiple logging formats (logfmt and JSON) and styles (slog and go-kit compatible). It provides thread-safe level control and flexible output configuration.
Related Packages:
Promslog/Flag Package - Flag integration for logging configuration with Kingpin
type LogStyle stringRepresents common logging formats in the Prometheus ecosystem.
const (
SlogStyle LogStyle = "slog" // Standard slog logging format
GoKitStyle LogStyle = "go-kit" // Go-kit compatible logging format
)SlogStyle - Standard slog format with conventional field namesGoKitStyle - Format compatible with go-kit/log for easier migrationvar LevelFlagOptions = []string{"debug", "info", "warn", "error"}Valid options for the log level flag.
var FormatFlagOptions = []string{"logfmt", "json"}Valid options for the log format flag.
type Level struct {
// Controls logging level with an info default. Thread-safe.
}Controls the logging level. The Level type is thread-safe and can be updated at runtime. Defaults to info level.
func (l *Level) Level() slog.LevelReturns the current value of the logging level.
Returns: The current slog.Level
func (l *Level) String() stringReturns a string representation of the current level.
Returns: Level name: "debug", "info", "warn", or "error"
func (l *Level) Set(s string) errorUpdates the logging level with validation. This method is thread-safe and can be called at runtime to change the logging level.
Parameters:
s - Level name ("debug", "info", "warn", or "error")Returns: Error if the level name is invalid
func (l *Level) UnmarshalYAML(unmarshal func(interface{}) error) errorUnmarshals level from YAML configuration.
Parameters:
unmarshal - YAML unmarshal functionReturns: Error if unmarshaling fails
type Format struct {
// Controls logging output format. Not concurrency-safe.
}Controls the logging output format (logfmt or JSON). Unlike Level, Format is not concurrency-safe and should be set before logger creation.
func (f *Format) String() stringReturns the current format as a string.
Returns: Format name: "logfmt" or "json"
func (f *Format) Set(s string) errorUpdates the format with validation.
Parameters:
s - Format name ("logfmt" or "json")Returns: Error if the format name is invalid
type Config struct {
Level *Level
Format *Format
Style LogStyle
Writer io.Writer
}Configuration for the logger.
Fields:
Level - The logging level controller (thread-safe)Format - The output format controllerStyle - The logging style (slog or go-kit)Writer - The output writer (defaults to stderr if nil)func NewLevel() *LevelReturns a new Level with the default level set to info.
Returns: A new Level instance
func NewFormat() *FormatCreates a new Format with the default format set to logfmt.
Returns: A new Format instance
func New(config *Config) *slog.LoggerReturns a new slog.Logger with timestamp annotation. If config.Writer is nil, output goes to stderr.
Parameters:
config - Logger configurationReturns: A configured *slog.Logger
func NewNopLogger() *slog.LoggerReturns a logger that writes to io.Discard, effectively discarding all log output. Useful for testing.
Returns: A no-op logger
package main
import (
"log/slog"
"github.com/prometheus/common/promslog"
)
func main() {
// Create logger with default settings
logger := promslog.New(&promslog.Config{
Level: promslog.NewLevel(),
Format: promslog.NewFormat(),
Style: promslog.SlogStyle,
})
logger.Info("Application started")
logger.Debug("This won't be shown (default level is info)")
logger.Error("An error occurred", "error", "something went wrong")
}package main
import (
"log/slog"
"os"
"github.com/prometheus/common/promslog"
)
func main() {
level := promslog.NewLevel()
level.Set("debug")
format := promslog.NewFormat()
format.Set("json")
logger := promslog.New(&promslog.Config{
Level: level,
Format: format,
Style: promslog.SlogStyle,
Writer: os.Stdout,
})
logger.Debug("Debug message visible")
logger.Info("Info message", "user", "alice", "action", "login")
}package main
import (
"log/slog"
"time"
"github.com/prometheus/common/promslog"
)
func main() {
level := promslog.NewLevel()
logger := promslog.New(&promslog.Config{
Level: level,
Format: promslog.NewFormat(),
Style: promslog.SlogStyle,
})
logger.Info("Starting with info level")
logger.Debug("This won't be shown")
// Change level at runtime
level.Set("debug")
logger.Debug("Now debug is visible")
// Change back to info
time.Sleep(time.Second)
level.Set("info")
logger.Debug("Debug hidden again")
logger.Info("Info still visible")
}package main
import (
"github.com/prometheus/common/promslog"
)
func main() {
// Use go-kit style for compatibility with existing go-kit/log code
logger := promslog.New(&promslog.Config{
Level: promslog.NewLevel(),
Format: promslog.NewFormat(),
Style: promslog.GoKitStyle,
})
logger.Info("Message with go-kit compatible format",
"component", "api",
"method", "GET",
"path", "/metrics")
}package main
import (
"log/slog"
"os"
"github.com/alecthomas/kingpin/v2"
"github.com/prometheus/common/promslog"
"github.com/prometheus/common/promslog/flag"
)
func main() {
app := kingpin.New("myapp", "My application")
// Create config and add flags
config := &promslog.Config{}
flag.AddFlags(app, config)
kingpin.MustParse(app.Parse(os.Args[1:]))
// Create logger from config populated by flags
logger := promslog.New(config)
logger.Info("Application started")
}package main
import (
"context"
"log/slog"
"github.com/prometheus/common/promslog"
)
func main() {
logger := promslog.New(&promslog.Config{
Level: promslog.NewLevel(),
Format: promslog.NewFormat(),
Style: promslog.SlogStyle,
})
// Add context to logger
logger = logger.With("service", "api", "version", "1.0")
// Use throughout application
logger.Info("Request received",
"method", "GET",
"path", "/api/users",
"duration_ms", 42)
logger.Error("Database connection failed",
"error", "connection timeout",
"host", "db.example.com",
"retry_count", 3)
}package main
import (
"testing"
"github.com/prometheus/common/promslog"
)
func processData(logger *slog.Logger, data string) {
logger.Info("Processing data", "data", data)
// ... processing logic
}
func TestProcessData(t *testing.T) {
// Use nop logger in tests to avoid log noise
logger := promslog.NewNopLogger()
processData(logger, "test-data")
// Assert on results, not log output
}package main
import (
"os"
"github.com/prometheus/common/promslog"
)
func main() {
logFile, err := os.OpenFile("app.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
if err != nil {
panic(err)
}
defer logFile.Close()
logger := promslog.New(&promslog.Config{
Level: promslog.NewLevel(),
Format: promslog.NewFormat(),
Style: promslog.SlogStyle,
Writer: logFile,
})
logger.Info("Logging to file")
}package main
import (
"log/slog"
"net/http"
"time"
"github.com/prometheus/common/promslog"
)
func loggingMiddleware(logger *slog.Logger, next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
start := time.Now()
next.ServeHTTP(w, r)
duration := time.Since(start)
logger.Info("HTTP request",
"method", r.Method,
"path", r.URL.Path,
"duration_ms", duration.Milliseconds(),
"remote_addr", r.RemoteAddr)
})
}
func main() {
logger := promslog.New(&promslog.Config{
Level: promslog.NewLevel(),
Format: promslog.NewFormat(),
Style: promslog.SlogStyle,
})
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("OK"))
})
http.Handle("/", loggingMiddleware(logger, handler))
http.ListenAndServe(":8080", nil)
}ts=2024-01-15T10:30:00.123Z level=INFO msg="Application started" service=api version=1.0
ts=2024-01-15T10:30:01.456Z level=ERROR msg="Database error" error="connection timeout" retry=3{"ts":"2024-01-15T10:30:00.123Z","level":"INFO","msg":"Application started","service":"api","version":"1.0"}
{"ts":"2024-01-15T10:30:01.456Z","level":"ERROR","msg":"Database error","error":"connection timeout","retry":3}infologfmtos.StderrLevels in order of severity:
debug - Most verboseinfo - Defaultwarn - Warningserror - Errors onlySetting a level filters out all messages below that level.