Structured Logging
Write logs with a fluent API that makes adding fields, metadata, and errors simple.
A consistent logging experience for Go, on top of any logging library.

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")
}{
"level": "error",
"time": "2026-04-25T12:00:00Z",
"msg": "user action failed",
"service": "api",
"userId": "1234",
"err": { "message": "something went wrong" }
}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.
| Name | Description |
|---|---|
| Pretty | Colorized, theme-aware terminal output. Recommended for local dev. |
| Structured | One JSON object per log entry. Recommended for production. |
| Console | Plain fmt.Println-style output to stdout/stderr; minimal formatting. |
| Testing | Captures entries in memory for tests. |
| Blank | Delegates dispatch to a user-supplied function. For prototyping or one-off integrations. |
Transports that ship log entries to a remote endpoint over the network. Async + batched by default.
| Name | Description |
|---|---|
| HTTP | Generic batched HTTP POST to any endpoint. Pluggable Encoder. |
| Datadog | Datadog Logs HTTP intake. Site-aware URL, DD-API-KEY header, status mapping. |
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.
| Name | Description |
|---|---|
| Zerolog | Wraps a *zerolog.Logger |
| Zap | Wraps a *zap.Logger |
| log/slog | Wraps a stdlib *slog.Logger. Forwards WithContext to handlers. |
| phuslu/log | High-performance zero-alloc JSON logger. Always exits on fatal. |
| logrus | The classic structured logger |
| charmbracelet/log | Pretty terminal-friendly logger from Charm |
| OpenTelemetry Logs | Emits to an OTel log.Logger. Forwards WithContext so SDK processors can correlate with the active span. |
Hook into the LogLayer pipeline to transform metadata, fields, data, messages, log level, or per-transport dispatch.
| Name | Description |
|---|---|
| Redact | Replace 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. |
| Sampling | Drop 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 Strings | Opt 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 Injector | Inject 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 Injector | Inject 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.