Files
teamai-test/.teamai/skills/common/golang-samber-slog/references/backend-handlers.md
T

7.2 KiB

Backend Handlers

All backend handlers implement slog.Handler and follow the Option{}.NewXxxHandler() constructor pattern.

Common Option Fields

Every handler's Option struct includes:

Field Purpose
Level Minimum log level (default: slog.LevelDebug)
AddSource Include source file/line in log output
ReplaceAttr Callback to modify attributes before emission
Converter Custom payload builder for the target format
AttrFromContext Slice of functions extracting attributes from context.Context

Cloud Backends

Datadog — slog-datadog

import slogdatadog "github.com/samber/slog-datadog/v2"

handler := slogdatadog.Option{
    Level: slog.LevelInfo,
    // Service, Source, Hostname, Tags configured via Datadog client
}.NewDatadogHandler()
defer handler.(interface{ Stop(context.Context) error }).Stop(context.Background()) // REQUIRED: flush buffered logs

Batch mode is the default — logs are buffered and sent periodically (default 5s). Call Stop(ctx) on shutdown or buffered logs are lost. The handler also exposes Flush(ctx) for mid-lifecycle flushes. For synchronous delivery, check the Option configuration.

Sentry — slog-sentry

import slogsentry "github.com/samber/slog-sentry/v2"

handler := slogsentry.Option{
    Level:   slog.LevelWarn,
    Hub:     sentry.CurrentHub(),
    AddSource: true,
}.NewSentryHandler()

// Flush on shutdown
defer sentry.Flush(2 * time.Second)

Recognized attributes: error (any error type), request (*http.Request), dist, environment, release, server_name, transaction. Use slog.Group("tags", ...) for Sentry tags and slog.Group("user", ...) for user context.

Error keys: Global ErrorKeys = []string{"error", "err"} — attributes with these keys are treated as error objects.

Loki — slog-loki

import slogloki "github.com/samber/slog-loki/v3"

lokiClient, _ := loki.New(lokiCfg)
defer lokiClient.Stop() // REQUIRED: flush buffered logs

handler := slogloki.Option{
    Level:  slog.LevelDebug,
    Client: lokiClient,
}.NewLokiHandler()

Labels vs metadata: By default, attributes are sent as Loki labels. For high-cardinality data (request IDs, trace IDs), enable HandleRecordsWithMetadata: true to send as structured metadata instead — this avoids label explosion that degrades Loki performance.

Graylog — slog-graylog

import sloggraylog "github.com/samber/slog-graylog/v2"

gelfWriter, _ := gelf.NewWriter("localhost:12201")
handler := sloggraylog.Option{
    Level:  slog.LevelDebug,
    Writer: gelfWriter,
}.NewGraylogHandler()

Uses GELF (Graylog Extended Log Format) over UDP.

Messaging Backends

Kafka — slog-kafka

import slogkafka "github.com/samber/slog-kafka/v2"

writer := &kafka.Writer{
    Addr:  kafka.TCP("localhost:9092"),
    Topic: "logs",
    Async: true, // non-blocking writes
}
handler := slogkafka.Option{
    Level:       slog.LevelDebug,
    KafkaWriter: writer,
    Timeout:     60 * time.Second,
}.NewKafkaHandler()
defer writer.Close() // REQUIRED: flush pending messages

Fluentd — slog-fluentd

import slogfluentd "github.com/samber/slog-fluentd/v2"

client, _ := fluent.New(fluent.Config{
    FluentHost: "localhost", FluentPort: 24224,
})
handler := slogfluentd.Option{
    Level:  slog.LevelDebug,
    Client: client,
    Tag:    "api",
}.NewFluentdHandler()
defer client.Close()

Logstash — slog-logstash

import sloglogstash "github.com/samber/slog-logstash/v2"

conn, _ := net.Dial("tcp", "localhost:9999")
handler := sloglogstash.Option{
    Level: slog.LevelDebug,
    Conn:  conn,
}.NewLogstashHandler()
defer conn.Close()

Output format: JSON with @timestamp, level, message, error, extra fields.

Notification Backends

Slack — slog-slack

import slogslack "github.com/samber/slog-slack/v2"

// Via webhook
handler := slogslack.Option{
    Level:      slog.LevelError,
    WebhookURL: "https://hooks.slack.com/services/...",
    Channel:    "alerts",
}.NewSlackHandler()

// Via bot token
handler := slogslack.Option{
    Level:    slog.LevelError,
    BotToken: "xoxb-...",
    Channel:  "alerts",
}.NewSlackHandler()

Telegram — slog-telegram

import slogtelegram "github.com/samber/slog-telegram/v2"

handler := slogtelegram.Option{
    Level:    slog.LevelError,
    Token:    "your-bot-token",
    Username: "@your-channel",
}.NewTelegramHandler()

Webhook — slog-webhook

import slogwebhook "github.com/samber/slog-webhook/v2"

handler := slogwebhook.Option{
    Level:    slog.LevelError,
    Endpoint: "https://webhook.site/your-id",
    Timeout:  10 * time.Second,
}.NewWebhookHandler()

Storage Backends

Parquet — slog-parquet

import slogparquet "github.com/samber/slog-parquet/v2"

buffer := slogparquet.NewParquetBuffer(bucket, "logs/", 10000, 5*time.Minute)
defer buffer.Flush(true) // REQUIRED: flush remaining records synchronously

handler := slogparquet.Option{
    Level:  slog.LevelDebug,
    Buffer: buffer,
}.NewParquetHandler()

Uses Thanos objstore.Bucket for cloud storage (S3, GCS, Azure). Records are buffered and written as Parquet files when either maxRecords or maxInterval is reached.

Logging Bridges

Bridge the slog.Handler interface to legacy logging frameworks. Use during incremental migration from Zap/Zerolog/Logrus to slog.

slog-zap

import slogzap "github.com/samber/slog-zap/v2"

zapLogger, _ := zap.NewProduction()
handler := slogzap.Option{
    Level:  slog.LevelDebug,
    Logger: zapLogger,
}.NewZapHandler()
slog.SetDefault(slog.New(handler))
// Now all slog.Info() calls route through Zap

slog-zerolog

import slogzerolog "github.com/samber/slog-zerolog/v2"

zerologLogger := zerolog.New(zerolog.ConsoleWriter{Out: os.Stderr})
handler := slogzerolog.Option{
    Level:  slog.LevelDebug,
    Logger: &zerologLogger,
}.NewZerologHandler()

slog-logrus

import sloglogrus "github.com/samber/slog-logrus/v2"

handler := sloglogrus.Option{
    Level:  slog.LevelDebug,
    Logger: logrus.StandardLogger(),
}.NewLogrusHandler()

Graceful Shutdown Checklist

Handlers that buffer records internally and MUST be closed on shutdown:

Handler Shutdown method What happens without it
slog-datadog handler.Stop(ctx) Buffered logs lost (default 5s batch)
slog-loki lokiClient.Stop() Pending push requests dropped
slog-kafka writer.Close() Pending messages never sent
slog-parquet buffer.Flush(true) Partial Parquet file not flushed to storage

For non-batched handlers (Sentry, Slack, Telegram, Webhook), logs are sent synchronously — no close required, but sentry.Flush(timeout) is recommended.

// Production shutdown pattern
func main() {
    lokiClient, _ := loki.New(lokiCfg)
    defer lokiClient.Stop() // flush buffered logs

    lokiHandler := slogloki.Option{
        Level: slog.LevelDebug, Client: lokiClient,
    }.NewLokiHandler()

    // Use signal handling for graceful shutdown
    ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt)
    defer stop()

    // ... start server ...
    <-ctx.Done()
    // deferred Stop() runs here, flushing buffered logs
}