Skip to content

LogLayerUnifies Go Logging

A consistent logging experience for Go, on top of any logging library.

LogLayer logo by Akshaya Madhavan

Quick Example

go
package main

import (
    "errors"

    "go.loglayer.dev"
    "go.loglayer.dev/transports/structured"
)

func main() {
    log := loglayer.New(loglayer.Config{
        Transport: structured.New(structured.Config{}),
    })

    // WithFields returns a NEW logger; assign it.
    log = log.WithFields(loglayer.Fields{"service": "api"})

    log.WithMetadata(loglayer.Metadata{"userId": "1234"}).
        WithError(errors.New("something went wrong")).
        Error("user action failed")
}
json
{
  "level": "error",
  "time": "2026-04-25T12:00:00Z",
  "msg": "user action failed",
  "service": "api",
  "userId": "1234",
  "err": { "message": "something went wrong" }
}

Transports

Renderers

Self-contained transports that format the entry and write it to an io.Writer. Pick one of these when you want LogLayer to do the rendering itself.

NameDescription
PrettyColorized, theme-aware terminal output. Recommended for local dev.
StructuredOne JSON object per log entry. Recommended for production.
ConsolePlain fmt.Println-style output to stdout/stderr; minimal formatting.
TestingCaptures entries in memory for tests.
BlankDelegates dispatch to a user-supplied function. For prototyping or one-off integrations.

Network

Transports that ship log entries to a remote endpoint over the network. Async + batched by default.

NameDescription
HTTPGeneric batched HTTP POST to any endpoint. Pluggable Encoder.
DatadogDatadog Logs HTTP intake. Site-aware URL, DD-API-KEY header, status mapping.

Supported Loggers

Transports that hand the entry off to an existing third-party logger you already configure. Pick one of these when you have an established logging stack and want LogLayer's API on top.

NameDescription
ZerologWraps a *zerolog.Logger
ZapWraps a *zap.Logger
log/slogWraps a stdlib *slog.Logger. Forwards WithContext to handlers.
phuslu/logHigh-performance zero-alloc JSON logger. Always exits on fatal.
logrusThe classic structured logger
charmbracelet/logPretty terminal-friendly logger from Charm
OpenTelemetry LogsEmits to an OTel log.Logger. Forwards WithContext so SDK processors can correlate with the active span.

Plugins

Hook into the LogLayer pipeline to transform metadata, fields, data, messages, log level, or per-transport dispatch.

NameDescription
RedactReplace values for a configured set of keys (and optional regex patterns) before metadata or fields reach a transport. Walks structs, maps, and slices via reflection; preserves the runtime type.
SamplingDrop a fraction of emissions to keep volume and cost under control. FixedRate (per-emission Bernoulli draw), FixedRatePerLevel (per-level rate), and Burst (rate cap per rolling window). Composes with itself for "1% kept, capped at 100/sec" patterns.
Format StringsOpt the logger into fmt.Sprintf-style format strings: log.Info("user %d", id) resolves to "user 1234" before downstream hooks see it.
Datadog APM Trace InjectorInject the active Datadog APM trace and span IDs (dd.trace_id, dd.span_id) into every log entry that carries a context, enabling Datadog's log/trace correlation. Tracer-agnostic; bring your own dd-trace-go (v1 or v2).
OpenTelemetry Trace InjectorInject the active OTel trace_id and span_id (and optional trace_flags) into every log entry that carries a context. Use with non-OTel transports for log/trace correlation; the OTel pipeline does this itself when shipping via transports/otellog.

LogLayer for Go is made with ❤️ by Theo Gravity / Disaresta. Logo by Akshaya Madhavan.