or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

assets.mdconfig.mdexpfmt.mdhelpers-templates.mdindex.mdmodel.mdpromslog-flag.mdpromslog.mdroute.mdserver.mdversion.md
tile.json

promslog.mddocs/

Promslog Package

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

import "github.com/prometheus/common/promslog"

Overview

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

Constants

LogStyle

type LogStyle string

Represents 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 names
  • GoKitStyle - Format compatible with go-kit/log for easier migration

Variables

var 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.

Core Types

Level

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.

Methods

func (l *Level) Level() slog.Level

Returns the current value of the logging level.

Returns: The current slog.Level

func (l *Level) String() string

Returns a string representation of the current level.

Returns: Level name: "debug", "info", "warn", or "error"

func (l *Level) Set(s string) error

Updates 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) error

Unmarshals level from YAML configuration.

Parameters:

  • unmarshal - YAML unmarshal function

Returns: Error if unmarshaling fails

Format

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.

Methods

func (f *Format) String() string

Returns the current format as a string.

Returns: Format name: "logfmt" or "json"

func (f *Format) Set(s string) error

Updates the format with validation.

Parameters:

  • s - Format name ("logfmt" or "json")

Returns: Error if the format name is invalid

Config

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 controller
  • Style - The logging style (slog or go-kit)
  • Writer - The output writer (defaults to stderr if nil)

Functions

NewLevel

func NewLevel() *Level

Returns a new Level with the default level set to info.

Returns: A new Level instance

NewFormat

func NewFormat() *Format

Creates a new Format with the default format set to logfmt.

Returns: A new Format instance

New

func New(config *Config) *slog.Logger

Returns a new slog.Logger with timestamp annotation. If config.Writer is nil, output goes to stderr.

Parameters:

  • config - Logger configuration

Returns: A configured *slog.Logger

NewNopLogger

func NewNopLogger() *slog.Logger

Returns a logger that writes to io.Discard, effectively discarding all log output. Useful for testing.

Returns: A no-op logger

Usage Examples

Basic Usage

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")
}

Custom Configuration

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")
}

Dynamic Level Changes

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")
}

GoKit Style for Migration

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")
}

With Flags (using promslog/flag)

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")
}

Structured Logging

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)
}

Testing with NopLogger

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
}

Custom Writer for File Logging

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")
}

HTTP Handler with Logging

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)
}

Format Examples

Logfmt Output

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

JSON Output

{"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}

Notes

Thread Safety

  • Level: Thread-safe, can be updated at runtime
  • Format: Not thread-safe, should be set before creating the logger

Default Values

  • Default level: info
  • Default format: logfmt
  • Default writer: os.Stderr

Level Hierarchy

Levels in order of severity:

  1. debug - Most verbose
  2. info - Default
  3. warn - Warnings
  4. error - Errors only

Setting a level filters out all messages below that level.