# LogLayer for Go > Transport-agnostic structured logging for Go. A fluent API on top of zerolog, zap, logrus, phuslu/log, charmbracelet/log, or any custom transport. LogLayer for Go gives you one consistent API for messages, persistent fields, per-call metadata, and errors. The underlying logger is a configuration choice. Module path: `go.loglayer.dev`. GitHub: `github.com/loglayer/loglayer-go`. ## Introduction - [Why Use LogLayer?](https://go.loglayer.dev/introduction): Pitch and design rationale. - [Getting Started](https://go.loglayer.dev/getting-started): Install, first example, recommended setup. - [Configuration](https://go.loglayer.dev/configuration): Every Config field. Includes Config.OnTransportPanic (opt-in callback that recovers transport SendToLogger panics; default nil propagates panics, matching Go logging convention). - [Cheat Sheet](https://go.loglayer.dev/cheatsheet): One-page API reference. - [What's New](https://go.loglayer.dev/whats-new): User-visible changes by date. ## Logging API - [Basic Logging](https://go.loglayer.dev/logging-api/basic-logging): The seven level methods (Trace, Debug, Info, Warn, Error, Fatal, Panic), prefix, message assembly. Fatal exits the process by default; Panic dispatches and then panics with the joined message string (recoverable; matches zerolog/zap/logrus convention). Includes log.Writer(level) / log.NewLogLogger(level) for plumbing third-party io.Writer / *log.Logger consumers (http.Server.ErrorLog, gorm, etc.) through the loglayer pipeline. - [Adjusting Log Levels](https://go.loglayer.dev/logging-api/adjusting-log-levels): SetLevel, EnableLevel, DisableLevel, master on/off. - [Fields](https://go.loglayer.dev/logging-api/fields): Persistent keyed data via WithFields, WithoutFields, FieldsKey. - [Metadata](https://go.loglayer.dev/logging-api/metadata): Per-log payload via WithMetadata. loglayer.Metadata is the canonical map alias; loglayer.M is a shorter alias for the same type. Same shape for loglayer.F = Fields. - [Error Handling](https://go.loglayer.dev/logging-api/error-handling): WithError, ErrorOnly, custom ErrorSerializer (recommend rotisserie/eris). Built-in opt-in: loglayer.UnwrappingErrorSerializer walks errors.Unwrap and errors.Join's Unwrap() []error to surface every wrapped cause under "causes". - [Go Context](https://go.loglayer.dev/logging-api/go-context): WithContext on *LogLayer binds a context.Context to every emission from the returned logger; on *LogBuilder it's a per-call override. Surfaced to transports via TransportParams.Ctx and to plugin dispatch hooks via params.Ctx. loghttp middleware auto-binds r.Context(). - [Child Loggers](https://go.loglayer.dev/logging-api/child-loggers): Child(), WithPrefix(), isolation semantics. - [Transport Configuration](https://go.loglayer.dev/transports/configuration): Config.Transport / Transports, BaseConfig (ID/Disabled/Level), transport ID semantics. - [Transport Management](https://go.loglayer.dev/transports/management): AddTransport, RemoveTransport, SetTransports, GetLoggerInstance. - [Groups](https://go.loglayer.dev/logging-api/groups): Named routing rules. Config.Routing.Groups + WithGroup(...string) on logger or builder. Routing.ActiveGroups filter, Routing.Ungrouped modes (UngroupedToAll/None/Transports), runtime mutators, ActiveGroupsFromEnv helper. - [Raw Logging](https://go.loglayer.dev/logging-api/raw): Raw(RawLogEntry) bypasses the builder. RawLogEntry.Source can carry a pre-captured *loglayer.Source so adapters with their own PC (e.g. the slog handler) skip runtime capture. - Source / Caller info (in [Configuration](https://go.loglayer.dev/configuration#source-caller-info)): Config.Source.Enabled captures the call site (file/line/function) of every emission via runtime.Caller; surfaced under Config.Source.FieldName (default "source") in the assembled Data. Off by default; ~620 ns / +5 allocs per emission when on. *loglayer.Source has json tags matching slog convention, plus String() and slog.LogValuer methods so non-JSON transports render readably. SourceFromPC(pc) builds a Source from a captured PC. The slog handler forwards Record.PC automatically. - [Mocking](https://go.loglayer.dev/logging-api/mocking): loglayer.NewMock() for silent mocks; transports/testing for capture. ## Transports - [Overview](https://go.loglayer.dev/transports/): Renderers vs Supported Loggers. - [Multiple Transports](https://go.loglayer.dev/transports/multiple-transports): Fan-out semantics. - [Creating Transports](https://go.loglayer.dev/transports/creating-transports): The Transport interface and BaseTransport helper. transport/transporttest exports RunContract for the 14-test wrapper contract suite plus ParseJSONLine. ### Renderers - [Console](https://go.loglayer.dev/transports/console): Plain fmt.Println-style output. - [Pretty](https://go.loglayer.dev/transports/pretty): Colorized terminal output. Recommended for local dev. - [Structured](https://go.loglayer.dev/transports/structured): One JSON object per entry. - [Testing](https://go.loglayer.dev/transports/testing): In-memory capture for tests. - [Blank](https://go.loglayer.dev/transports/blank): Delegates SendToLogger to a user-supplied function. For prototyping or one-off integrations. ### Network - [HTTP](https://go.loglayer.dev/transports/http): Generic batched HTTP POST. Pluggable Encoder, configurable batching, async worker with Close() to flush. - [Datadog](https://go.loglayer.dev/transports/datadog): Datadog Logs HTTP intake. Site-aware URL, DD-API-KEY header, level→status mapping. Built on the HTTP transport. ### Supported Loggers - [Zerolog](https://go.loglayer.dev/transports/zerolog): Wraps github.com/rs/zerolog. - [Zap](https://go.loglayer.dev/transports/zap): Wraps go.uber.org/zap. - [log/slog](https://go.loglayer.dev/transports/slog): Wraps the stdlib *slog.Logger. Forwards WithContext to LogAttrs. - [phuslu/log](https://go.loglayer.dev/transports/phuslu): Wraps github.com/phuslu/log. Always exits on Fatal. - [logrus](https://go.loglayer.dev/transports/logrus): Wraps github.com/sirupsen/logrus. - [charmbracelet/log](https://go.loglayer.dev/transports/charmlog): Wraps github.com/charmbracelet/log. - [OpenTelemetry Logs](https://go.loglayer.dev/transports/otellog): Emits each entry as an OTel log.Record on a go.opentelemetry.io/otel/log.Logger. Defaults to global.GetLoggerProvider; accepts an explicit LoggerProvider+Name/Version/SchemaURL or a pre-built Logger. Forwards WithContext to Logger.Emit so SDK processors correlate with the active span. ## Integrations - [HTTP middleware (loghttp)](https://go.loglayer.dev/integrations/loghttp): One-line middleware that derives a per-request logger with requestId/method/path, stores it in the request context, emits a "request completed" log with status, bytes, and duration. Wraps any net/http-compatible router (chi, gorilla, gin, echo, stdlib). - [slog Handler](https://go.loglayer.dev/integrations/sloghandler): A `log/slog.Handler` backed by a loglayer logger. `slog.SetDefault(slog.New(sloghandler.New(log)))` makes every `slog.Info(...)` call (yours and your dependencies') flow through loglayer's plugin pipeline, fan-out, and group routing. Opposite direction from `transports/slog` (which lets loglayer emit through slog as a backend). Levels above slog.Error pin to LogLevelError; slog cannot trigger Fatal exit through this handler. Source forwarded automatically: `slog.Record.PC` becomes a `*loglayer.Source` via `RawLogEntry.Source`, no `Config.Source.Enabled` needed on the loglayer side. ## Plugins - [Plugins overview](https://go.loglayer.dev/plugins/): Six lifecycle hooks (OnFieldsCalled, OnMetadataCalled, OnBeforeDataOut, OnBeforeMessageOut, TransformLogLevel, ShouldSend). Function-field struct API; AddPlugin/RemovePlugin/GetPlugin/PluginCount. Dispatch-time hooks receive context.Context via Params.Ctx. Hook panics are recovered centrally by LogLayer (each hook returns its no-op value on panic; ShouldSend fails open). Set Plugin.OnError to observe recovered panics. - [Creating Plugins](https://go.loglayer.dev/plugins/creating-plugins): Per-hook reference and design patterns. plugins/plugintest exports Install (wires plugin into a logger backed by the testing transport), AssertNoMutation, AssertPanicRecovered. - [Redact](https://go.loglayer.dev/plugins/redact): Built-in redaction plugin. Match by Keys (exact, json-tag aware) or Patterns (regex against string values). Walks nested maps, structs, slices, and pointers at any depth via reflection; preserves the caller's runtime type. Dependency-free. - [Sampling](https://go.loglayer.dev/plugins/sampling): Drop a fraction of emissions to keep volume and cost bounded. FixedRate (per-emission Bernoulli draw), FixedRatePerLevel (per-level rate, levels not in the map kept unconditionally), Burst (rate cap per rolling window). Composes with itself for "1% kept, capped at 100/sec" patterns. SendGate-based, dependency-free. - [Format Strings](https://go.loglayer.dev/plugins/fmtlog): Opt the logger into fmt.Sprintf semantics for multi-arg messages: log.Info("user %d", id) resolves to "user 1234" before downstream MessageHooks run. Single MessageHook; sub-package `go.loglayer.dev/fmtlog` of the main module. - [Datadog APM Trace Injector](https://go.loglayer.dev/plugins/datadogtrace): Inject the active dd-trace-go trace and span IDs (dd.trace_id, dd.span_id, plus optional dd.service/env/version) into every log entry that carries a context. Tracer-agnostic: bring your own dd-trace-go (v1 or v2) via a small Extract function. - [OpenTelemetry Trace Injector](https://go.loglayer.dev/plugins/oteltrace): Inject the active OTel trace_id and span_id (lowercase hex, configurable keys) plus optional trace_flags, W3C trace_state, and W3C baggage members (under a configurable key prefix) into every log entry that carries a context. Baggage rides independently of the span, so contexts with baggage but no span still surface baggage attributes. Use with non-OTel transports for log/trace correlation; transports/otellog does this automatically. ## Utilities - `utils/maputil`: public shared value-conversion and deep-clone helpers. `ToMap(any) map[string]any` normalizes any value to a map via JSON roundtrip. `Cloner{MatchKey, MatchValue, Censor}.Clone(any) any` deep-clones with predicate-based replacement at any depth, preserving the runtime type. Used by structured/pretty/datadog transports and the redact plugin; available for third-party plugin and transport authors. ## Key Patterns ```go import "go.loglayer.dev" // Construct (panics on misconfiguration) log := loglayer.New(loglayer.Config{ Transport: pretty.New(pretty.Config{}), }) // Or with explicit error handling log, err := loglayer.Build(loglayer.Config{ Transport: pretty.New(pretty.Config{}), }) // Persistent fields (returns new logger; assign the result) log = log.WithFields(loglayer.Fields{"requestId": "abc"}) // Per-log metadata, idiomatic alias log.WithMetadata(loglayer.Metadata{"userId": 42}).Info("user login") // Per-log struct metadata (transport decides serialization) log.WithMetadata(MyEvent{...}).Info("event") // Errors log.WithError(err).Error("operation failed") // Per-call Go context (trace IDs, deadlines, etc.) log.WithContext(ctx).Info("request received") // Test mock (silent, fatal does not exit) log := loglayer.NewMock() ```