diff --git a/.changeset/transport-sweep-v3-paths.md b/.changeset/transport-sweep-v3-paths.md
new file mode 100644
index 0000000..ba4bb4d
--- /dev/null
+++ b/.changeset/transport-sweep-v3-paths.md
@@ -0,0 +1,35 @@
+---
+"transports/axiom": major
+"transports/blank": major
+"transports/betterstack": major
+"transports/charmlog": major
+"transports/cli": major
+"transports/console": major
+"transports/datadog": major
+"transports/gcplogging": major
+"transports/http": major
+"transports/logrus": major
+"transports/lumberjack": major
+"transports/newrelic": major
+"transports/otellog": major
+"transports/phuslu": major
+"transports/pretty": major
+"transports/sentry": major
+"transports/slog": major
+"transports/structured": major
+"transports/testing": major
+"transports/zap": major
+"transports/zerolog": major
+"integrations/loghttp": major
+"integrations/sloghandler": major
+"plugins/datadogtrace": major
+"plugins/fmtlog": major
+"plugins/oteltrace": major
+"plugins/plugintest": major
+"plugins/redact": major
+"plugins/sampling": major
+---
+
+**Breaking: module paths bump to the next major.** These transports, plugins, and integrations now depend on `go.loglayer.dev/v3` and re-export v3 core types (or sibling v3 types) in their public API. Per Go convention, each module's path moves to its next major (`/v2` → `/v3`, or unversioned → `/v2`); consumers update imports and `go get` lines.
+
+The `structured` transport also sanitizes ANSI escape sequences, bidi overrides, and CR/LF at the top level of every entry and omits the `msg` key when the message is empty. See the migration guide at https://go.loglayer.dev/migrating.
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 636b6c5..ed60284 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -24,13 +24,6 @@ jobs:
if: github.event_name == 'pull_request' || !startsWith(github.event.head_commit.message, 'chore(release):')
name: Test (Go ${{ matrix.go }})
runs-on: ubuntu-latest
- # Core-only mode: while the core is on its unpublished v3 path, the
- # still-v2 sub-modules cannot build against it (their replace points
- # at the repo root). Restrict the foreach ops to the root module;
- # the sweep PR that moves sub-modules to v3 removes this and the
- # per-module steps below.
- env:
- CORE_ONLY: '1'
strategy:
fail-fast: false
matrix:
@@ -65,40 +58,30 @@ jobs:
# OTel transport and plugin live in their own modules so the OTel
# SDK's Go floor doesn't bind the main module. Test each separately.
- # Skipped in core-only mode (CORE_ONLY=1): these modules still
- # require the v2 core and cannot build while the root is the
- # unpublished v3. The sweep PR removes the skip.
- name: Test (transports/otellog)
- if: env.CORE_ONLY != '1'
working-directory: transports/otellog
run: go test -race -count=1 ./...
- name: Livetest (transports/otellog, real OTel SDK)
- if: env.CORE_ONLY != '1'
working-directory: transports/otellog
run: go test -tags=livetest -race -count=1 ./...
- name: Test (plugins/oteltrace)
- if: env.CORE_ONLY != '1'
working-directory: plugins/oteltrace
run: go test -race -count=1 ./...
- name: Livetest (plugins/oteltrace, real OTel TracerProvider)
- if: env.CORE_ONLY != '1'
working-directory: plugins/oteltrace
run: go test -tags=livetest -race -count=1 ./...
- name: Livetest (Datadog dd-trace-go integration)
- if: env.CORE_ONLY != '1'
working-directory: plugins/datadogtrace/livetest
run: go test -race -count=1 ./...
# Multi-module examples: syntax check only (running them produces
# stdout output we don't need in CI). -o /dev/null avoids writing
- # the example binary into the workspace. Skipped in core-only mode
- # for the same v2-core reason as the OTel modules above.
+ # the example binary into the workspace.
- name: Build (examples/otel-end-to-end)
- if: env.CORE_ONLY != '1'
working-directory: examples/otel-end-to-end
run: go build -o /dev/null ./...
@@ -110,9 +93,6 @@ jobs:
# Pin instead of @latest so a new staticcheck release with new
# checks doesn't surprise CI. Bump deliberately.
STATICCHECK_VERSION: '2026.1'
- # Core-only mode: see the test job's comment. The v2 sub-modules
- # can't be analyzed while the root is the unpublished v3 core.
- CORE_ONLY: '1'
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
@@ -152,9 +132,6 @@ jobs:
runs-on: ubuntu-latest
env:
GOVULNCHECK_VERSION: 'v1.7.0'
- # Core-only mode: same v2-sub-module gate as the test job. The
- # sweep PR removes this.
- CORE_ONLY: '1'
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
diff --git a/README.md b/README.md
index f54c5c3..d2e8c83 100644
--- a/README.md
+++ b/README.md
@@ -8,7 +8,7 @@
-
+
@@ -25,16 +25,15 @@ For full documentation, read the [docs](https://go.loglayer.dev).
import (
"errors"
- "go.loglayer.dev/v2"
- "go.loglayer.dev/transports/structured/v2"
+ "go.loglayer.dev/v3"
+ "go.loglayer.dev/transports/structured/v3"
)
log := loglayer.New(loglayer.Config{
Transport: structured.New(structured.Config{}),
- // Put fields under "context" and metadata under "metadata"
- // (defaults are flattened to the root).
- FieldsKey: "context",
- MetadataFieldName: "metadata",
+ // Put fields under "context", and keep metadata under "metadata"
+ // (its default in v3).
+ FieldsKey: "context",
})
// Persistent fields that appear on every subsequent log
@@ -84,7 +83,7 @@ log.WithPrefix("[my-app]").
## Install
```sh
-go get go.loglayer.dev/v2
+go get go.loglayer.dev/v3
```
## Documentation
@@ -97,7 +96,7 @@ Coming from [loglayer for TypeScript](https://loglayer.dev)? See [For TypeScript
## Contributing
-This is a multi-module repo: the framework core lives at the root (`go.loglayer.dev/v2`); every transport, plugin, and integration ships as its own independently-versioned Go module under `transports/`, `plugins/`, and `integrations/`.
+This is a multi-module repo: the framework core lives at the root (`go.loglayer.dev/v3`); every transport, plugin, and integration ships as its own independently-versioned Go module under `transports/`, `plugins/`, and `integrations/`.
- **Dev-loop on-ramp** (prerequisites, hooks, make targets, commits, tests, docs, releases via [monorel](https://monorel.disaresta.com)): [CONTRIBUTING.md](CONTRIBUTING.md).
- **Architectural context** (multi-module split, thread-safety contract, performance log, release flow internals): [AGENTS.md](AGENTS.md).
diff --git a/doc.go b/doc.go
index f63386b..3df4d78 100644
--- a/doc.go
+++ b/doc.go
@@ -3,7 +3,7 @@
// and Plugin interfaces, and the dispatch pipeline. Concrete transports
// (zap, zerolog, slog, charmlog, OTel, etc.) ship as separately-versioned
// sub-modules under go.loglayer.dev/transports/, with a /vN suffix
-// for sub-modules on their own major version (e.g. transports/structured/v2).
+// for sub-modules on their own major version (e.g. transports/structured/v3).
//
// Full docs: https://go.loglayer.dev
//
@@ -11,7 +11,7 @@
//
// import (
// "go.loglayer.dev/v3"
-// "go.loglayer.dev/transports/structured/v2"
+// "go.loglayer.dev/transports/structured/v3"
// )
//
// log := loglayer.New(loglayer.Config{
@@ -21,10 +21,6 @@
// WithMetadata(loglayer.Metadata{"durationMs": 42}).
// Info("served")
//
-// Note: transports are still at their v2 paths in this release; the combo
-// above compiles once the transport v3 bumps land in the follow-up release.
-// Install go.loglayer.dev/v3 alone first, or wait for those bumps.
-//
// # Three data shapes
//
// LogLayer separates persistent from per-call data on purpose. Pick the
diff --git a/docs/src/benchmarks.md b/docs/src/benchmarks.md
index 0eb85c2..2a47027 100644
--- a/docs/src/benchmarks.md
+++ b/docs/src/benchmarks.md
@@ -73,10 +73,10 @@ With LogLayer, swapping the underlying transport is a one-line change in `New()`
```go
import (
"go.loglayer.dev/v3"
- "go.loglayer.dev/integrations/sloghandler/v2"
- "go.loglayer.dev/transports/structured/v2"
- llzero "go.loglayer.dev/transports/zerolog/v2"
- llzap "go.loglayer.dev/transports/zap/v2"
+ "go.loglayer.dev/integrations/sloghandler/v3"
+ "go.loglayer.dev/transports/structured/v3"
+ llzero "go.loglayer.dev/transports/zerolog/v3"
+ llzap "go.loglayer.dev/transports/zap/v3"
)
// Wrap zerolog (142 ns)
diff --git a/docs/src/cheatsheet.md b/docs/src/cheatsheet.md
index b7cde54..716e779 100644
--- a/docs/src/cheatsheet.md
+++ b/docs/src/cheatsheet.md
@@ -7,14 +7,10 @@ description: One-page quick reference of the LogLayer for Go API.
## At a Glance
-::: warning Interim state: transports are still on v2
-This example pairs the v3 core with the structured transport's `v2` path. The transports move to `/v3` in the follow-up release; until then this exact import combo does not compile. Install the v3 core alone first, or wait for the transport v3 bumps.
-:::
-
```go
import (
"go.loglayer.dev/v3"
- "go.loglayer.dev/transports/structured/v2"
+ "go.loglayer.dev/transports/structured/v3"
)
log := loglayer.New(loglayer.Config{Transport: structured.New(structured.Config{})})
@@ -68,10 +64,10 @@ Metadata nests under `"metadata"` by default; `Config.FlattenMetadata: true` res
Each method takes `...any`, joined with a space.
-For `fmt.Sprintf`-style format strings, register the optional [`fmtlog`](https://pkg.go.dev/go.loglayer.dev/plugins/fmtlog/v2) plugin:
+For `fmt.Sprintf`-style format strings, register the optional [`fmtlog`](https://pkg.go.dev/go.loglayer.dev/plugins/fmtlog/v3) plugin:
```go
-import "go.loglayer.dev/plugins/fmtlog/v2"
+import "go.loglayer.dev/plugins/fmtlog/v3"
log.AddPlugin(fmtlog.New())
@@ -281,7 +277,7 @@ The merged group set is also surfaced to transports via `TransportParams.Groups`
## Plugins
```go
-import "go.loglayer.dev/plugins/redact/v2"
+import "go.loglayer.dev/plugins/redact/v3"
// Inline single-hook plugin via an adapter constructor
log.AddPlugin(loglayer.NewDataHook("tag", func(p loglayer.BeforeDataOutParams) loglayer.Data {
@@ -349,7 +345,7 @@ Off by default. Costs ~600 ns / +5 allocs per emission when on (see [Benchmarks]
## slog Interop
```go
-import "go.loglayer.dev/integrations/sloghandler/v2"
+import "go.loglayer.dev/integrations/sloghandler/v3"
// Make every slog.Info(...) flow through your loglayer pipeline (plugins,
// fan-out, groups, level state). slog.With / WithAttrs become persistent
diff --git a/docs/src/configuration.md b/docs/src/configuration.md
index f417bbe..916cbf4 100644
--- a/docs/src/configuration.md
+++ b/docs/src/configuration.md
@@ -90,7 +90,7 @@ loglayer.New(loglayer.Config{
Plugins to register at construction time. Equivalent to calling `log.AddPlugin` for each entry after `New`; either form is fine.
```go
-import "go.loglayer.dev/plugins/redact/v2"
+import "go.loglayer.dev/plugins/redact/v3"
log := loglayer.New(loglayer.Config{
Transport: structured.New(structured.Config{}),
diff --git a/docs/src/for-typescript-developers.md b/docs/src/for-typescript-developers.md
index 30f2ae5..2576435 100644
--- a/docs/src/for-typescript-developers.md
+++ b/docs/src/for-typescript-developers.md
@@ -117,11 +117,11 @@ TypeScript's `@loglayer/transport-pino`, `@loglayer/plugin-redaction`, etc. are
| TypeScript | Go |
|----------------------------------|-----------------------------------------------|
| `loglayer` | `go.loglayer.dev/v3` (core + stdlib renderers) |
-| `@loglayer/transport-zerolog` | `go.loglayer.dev/transports/zerolog/v2` |
-| `@loglayer/transport-datadog` | `go.loglayer.dev/transports/datadog/v2` |
-| `@loglayer/integration-elysia` | `go.loglayer.dev/integrations/loghttp/v2` (etc.) |
+| `@loglayer/transport-zerolog` | `go.loglayer.dev/transports/zerolog/v3` |
+| `@loglayer/transport-datadog` | `go.loglayer.dev/transports/datadog/v3` |
+| `@loglayer/integration-elysia` | `go.loglayer.dev/integrations/loghttp/v3` (etc.) |
-Transports and integrations keep their current paths until each ships its own v3 bump; check the [Transports overview](/transports/) and the [loghttp](/integrations/loghttp) / [sloghandler](/integrations/sloghandler) pages for each module's current path.
+Sub-modules that re-export core types ship on their own `/v3` paths; check the [Transports overview](/transports/) and the [loghttp](/integrations/loghttp) / [sloghandler](/integrations/sloghandler) pages for each module's path.
`go get` each module you actually need; the dependency graph stays focused on whatever you imported.
@@ -151,7 +151,7 @@ The full set: `NewFieldsHook`, `NewMetadataHook`, `NewDataHook`, `NewMessageHook
`plugins/redact` mirrors `@loglayer/plugin-redaction`. It supports key matching, regex value patterns, and json-tag-aware struct walking, all type-preserving:
```go
-import "go.loglayer.dev/plugins/redact/v2"
+import "go.loglayer.dev/plugins/redact/v3"
log.AddPlugin(redact.New(redact.Config{
Keys: []string{"password", "apiKey"},
@@ -200,7 +200,7 @@ If any of these are blockers for your use case, open an issue at [github.com/log
import (
"go.loglayer.dev/v3"
- "go.loglayer.dev/transports/structured/v2"
+ "go.loglayer.dev/transports/structured/v3"
)
log := loglayer.New(loglayer.Config{
diff --git a/docs/src/getting-started.md b/docs/src/getting-started.md
index ecfb147..ac77c04 100644
--- a/docs/src/getting-started.md
+++ b/docs/src/getting-started.md
@@ -5,7 +5,7 @@ description: Install LogLayer, pick a transport, and write your first structured
# Getting Started
-LogLayer for Go targets **Go 1.25+** for the main module: `go.loglayer.dev/v3`. Most transports are sub-packages of that module, so you only pull in dependencies for the transports you actually use. Individual transports and plugins call out any stricter requirement on their per-page docs.
+LogLayer for Go targets **Go 1.25+** for the main module: `go.loglayer.dev/v3`. Transports and plugins ship as separate modules, so you only pull in dependencies for the ones you actually use. Individual transports and plugins call out any stricter requirement on their per-page docs.
## Installation
@@ -13,15 +13,9 @@ LogLayer ships as a multi-module repo: the core lives at `go.loglayer.dev/v3`, a
```sh
go get go.loglayer.dev/v3
-go get go.loglayer.dev/transports/structured/v2
+go get go.loglayer.dev/transports/structured/v3
```
-::: warning Interim state: transports are still on v2
-This release moves the core to `go.loglayer.dev/v3`; the transports keep their `v2` paths until the follow-up release that bumps them to `v3`. Until then, the examples below that pair the v3 core with a `v2` transport path do not compile together. Install the v3 core alone first, or wait for the transport v3 bumps before copying the full examples.
-:::
-
-Transports and plugins keep their own versioned paths; the structured transport moves to `/v3` in a follow-up release.
-
## Basic Usage with the Structured Transport
The simplest way to start is the [Structured Transport](/transports/structured), which writes one JSON object per log entry to `os.Stdout`:
@@ -34,7 +28,7 @@ import (
"fmt"
"go.loglayer.dev/v3"
- "go.loglayer.dev/transports/structured/v2"
+ "go.loglayer.dev/transports/structured/v3"
)
func main() {
@@ -95,7 +89,7 @@ For stack traces, custom shapes, or other options, see [Error Handling](/logging
If you already have an existing logging stack, LogLayer can wrap it so your call sites use the LogLayer API while emission goes through the underlying logger you've already configured. Here it is for `zerolog`:
```sh
-go get go.loglayer.dev/transports/zerolog/v2 github.com/rs/zerolog
+go get go.loglayer.dev/transports/zerolog/v3 github.com/rs/zerolog
```
```go
@@ -105,7 +99,7 @@ import (
zlog "github.com/rs/zerolog"
"go.loglayer.dev/v3"
- llzero "go.loglayer.dev/transports/zerolog/v2"
+ llzero "go.loglayer.dev/transports/zerolog/v3"
)
z := zlog.New(os.Stderr).With().Timestamp().Logger()
diff --git a/docs/src/index.md b/docs/src/index.md
index 23906bf..9eee5a2 100644
--- a/docs/src/index.md
+++ b/docs/src/index.md
@@ -39,10 +39,6 @@ features:
## Quick Example
-::: warning Interim state: transports are still on v2
-This example pairs the v3 core with the structured transport's `v2` path. The transports move to `/v3` in the follow-up release; until then this exact import combo does not compile. Install the v3 core alone first, or wait for the transport v3 bumps.
-:::
-
```go
package main
@@ -50,7 +46,7 @@ import (
"errors"
"go.loglayer.dev/v3"
- "go.loglayer.dev/transports/structured/v2"
+ "go.loglayer.dev/transports/structured/v3"
)
func main() {
diff --git a/docs/src/integrations/loghttp.md b/docs/src/integrations/loghttp.md
index 0be09f5..2bdc0bd 100644
--- a/docs/src/integrations/loghttp.md
+++ b/docs/src/integrations/loghttp.md
@@ -12,7 +12,7 @@ description: One-line HTTP middleware that derives a per-request logger and stuf
Mirrors the role that `hlog.NewHandler` plays for zerolog. Works with any `net/http`-compatible router: stdlib, chi, gorilla/mux, gin, echo, etc.
```sh
-go get go.loglayer.dev/integrations/loghttp/v2
+go get go.loglayer.dev/integrations/loghttp/v3
```
## Basic Usage
@@ -22,14 +22,13 @@ import (
"net/http"
"go.loglayer.dev/v3"
- "go.loglayer.dev/integrations/loghttp/v2"
- "go.loglayer.dev/transports/structured/v2"
+ "go.loglayer.dev/integrations/loghttp/v3"
+ "go.loglayer.dev/transports/structured/v3"
)
var log = loglayer.New(loglayer.Config{
- Transport: structured.New(structured.Config{}),
- FieldsKey: "context",
- MetadataFieldName: "metadata",
+ Transport: structured.New(structured.Config{}),
+ FieldsKey: "context",
})
func main() {
@@ -138,7 +137,7 @@ loghttp.Middleware(log, loghttp.Config{StartLog: true})
### `ShouldStartLog`
-Per-request callback that decides whether to emit the "request started" line. Use it for sampling or conditional logging — log 1% of requests, only emit when a debug header is set, etc. Returning true emits the start line; false skips it. The "request completed" line still emits regardless. When set, `StartLog` is ignored.
+Per-request callback that decides whether to emit the "request started" line. Use it for sampling or conditional logging: log 1% of requests, only emit when a debug header is set, etc. Returning true emits the start line; false skips it. The "request completed" line still emits regardless. When set, `StartLog` is ignored.
```go
loghttp.Middleware(log, loghttp.Config{
diff --git a/docs/src/integrations/sloghandler.md b/docs/src/integrations/sloghandler.md
index 2890766..6fa0f52 100644
--- a/docs/src/integrations/sloghandler.md
+++ b/docs/src/integrations/sloghandler.md
@@ -10,7 +10,7 @@ description: "slog.Handler that routes every slog.Info(...) call through your lo
`integrations/sloghandler` is a `log/slog.Handler` backed by a loglayer logger. Once installed, every `slog.Info(...)` call (yours or your dependencies') flows through loglayer's plugin pipeline, multi-transport fan-out, group routing, and level filtering.
```sh
-go get go.loglayer.dev/integrations/sloghandler/v2
+go get go.loglayer.dev/integrations/sloghandler/v3
```
This is the **slog → loglayer** direction. If you want the opposite (use loglayer's API and emit through a `*slog.Logger` you've already configured), see the [slog Transport](/transports/slog).
@@ -28,9 +28,9 @@ import (
"log/slog"
"go.loglayer.dev/v3"
- "go.loglayer.dev/integrations/sloghandler/v2"
- "go.loglayer.dev/plugins/redact/v2"
- "go.loglayer.dev/transports/structured/v2"
+ "go.loglayer.dev/integrations/sloghandler/v3"
+ "go.loglayer.dev/plugins/redact/v3"
+ "go.loglayer.dev/transports/structured/v3"
)
log := loglayer.New(loglayer.Config{
diff --git a/docs/src/introduction.md b/docs/src/introduction.md
index d23f105..1df3072 100644
--- a/docs/src/introduction.md
+++ b/docs/src/introduction.md
@@ -23,8 +23,7 @@ If your service is small and you only need "log to stdout in JSON," the stdlib i
```go
log := loglayer.New(loglayer.Config{
- Transport: structured.New(structured.Config{}),
- MetadataFieldName: "metadata",
+ Transport: structured.New(structured.Config{}),
})
log.
@@ -84,9 +83,8 @@ This separation provides several benefits:
```go
log := loglayer.New(loglayer.Config{
- Transport: structured.New(structured.Config{}),
- FieldsKey: "context",
- MetadataFieldName: "metadata",
+ Transport: structured.New(structured.Config{}),
+ FieldsKey: "context",
})
log.
@@ -176,7 +174,7 @@ Send logs directly to any HTTP endpoint without a third-party logging library, w
Built-in mocks make testing painless:
```go
-import lltest "go.loglayer.dev/transports/testing/v2"
+import lltest "go.loglayer.dev/transports/testing/v3"
// Silent mock for tests that don't care about output
log := loglayer.NewMock()
diff --git a/docs/src/log-sanitization.md b/docs/src/log-sanitization.md
index 217755f..444f82b 100644
--- a/docs/src/log-sanitization.md
+++ b/docs/src/log-sanitization.md
@@ -53,7 +53,7 @@ What's preserved:
## What does NOT get sanitized
-`structured` and every wrapper transport (`zerolog`, `zap`, `slog`, `logrus`, `charmlog`, `phuslu`, `sentry`, `otellog`, `gcplogging`, `http`, `datadog`, `testing`) **do not** call `sanitize.Message`. They rely on the JSON encoder downstream to escape control bytes:
+Wrapper transports that hand off to an underlying logger (`zerolog`, `zap`, `slog`, `logrus`, `charmlog`, `phuslu`, `sentry`, `otellog`, `gcplogging`, `http`, `datadog`, `testing`) **do not** call `sanitize.Message`. They rely on the JSON encoder downstream to escape control bytes:
```
"\n" → "\\n"
@@ -63,6 +63,8 @@ What's preserved:
This is safe for the JSON-shaped sinks because the wire output is meant for log pipelines, log aggregators, and tools like `jq` that interpret JSON-encoded escapes as text. None of them re-emit the raw bytes to a TTY.
+The `structured` transport does call `sanitize.Message` on its own output path: the message, top-level metadata/Data keys, and string-typed top-level values are sanitized before the JSON encoder writes them. The encoder still escapes the remaining control bytes (e.g. `\n` in a nested map value) as JSON `\uXXXX` sequences.
+
::: warning Don't `cat` JSON wrapper-transport output to a TTY without escaping
The `` in JSON is text, but if you pipe wrapper-transport output through a tool that *does* interpret JSON escapes back to bytes (e.g., a homemade pretty-printer that calls `json.Unmarshal` and prints raw strings), you reintroduce the smuggling vector. Use `jq -r .msg` carefully on untrusted log content; prefer `jq` without `-r` (which keeps the JSON escaping) for safety.
:::
diff --git a/docs/src/logging-api/groups.md b/docs/src/logging-api/groups.md
index 718a059..64b18bb 100644
--- a/docs/src/logging-api/groups.md
+++ b/docs/src/logging-api/groups.md
@@ -16,8 +16,8 @@ Define groups when creating the logger:
```go
import (
"go.loglayer.dev/v3"
- "go.loglayer.dev/transports/structured/v2"
- "go.loglayer.dev/transports/datadog/v2"
+ "go.loglayer.dev/transports/structured/v3"
+ "go.loglayer.dev/transports/datadog/v3"
"go.loglayer.dev/v3/transport"
)
diff --git a/docs/src/logging-api/metadata.md b/docs/src/logging-api/metadata.md
index 519085e..dec513e 100644
--- a/docs/src/logging-api/metadata.md
+++ b/docs/src/logging-api/metadata.md
@@ -135,7 +135,7 @@ The default level is `Info`. Passing `nil` is a no-op.
`MetadataOnly` is the KV-only idiom: entries with data but no message. The [Structured Transport](/transports/structured) emits them as JSON objects, and for the terminal renderers the [CLI Transport](/transports/cli) renders them as `key=value` pairs only when `Config.ShowFields` is set.
```go
-// structured: {"level":"info","time":"...","msg":"","metadata":{"status":"healthy","memory":"512MB"}}
+// structured: {"level":"info","time":"...","metadata":{"status":"healthy","memory":"512MB"}}
// console (always) / cli (with ShowFields): metadata=... (nested as a value under the metadata key)
log.MetadataOnly(loglayer.Metadata{
"status": "healthy",
@@ -143,7 +143,7 @@ log.MetadataOnly(loglayer.Metadata{
})
```
-`MetadataOnly` emits an entry with no message: the assembled output carries the level and the metadata value, with no message text. The structured transport renders the empty message as `"msg":""` today; omitting `msg` for empty messages ships with the structured transport's v3 release.
+`MetadataOnly` emits an entry with no message: the assembled output carries the level and the metadata value, with no message text. The structured transport omits the `msg` key when the message is empty.
The same shape is available with persistent fields: `log.WithFields(...).Info("")` produces an entry with fields but no message. Prefer `MetadataOnly` for per-event data, `WithFields(...).Info("")` when the keys belong to the logger's persistent bag.
diff --git a/docs/src/logging-api/mocking.md b/docs/src/logging-api/mocking.md
index 7beb57c..b025431 100644
--- a/docs/src/logging-api/mocking.md
+++ b/docs/src/logging-api/mocking.md
@@ -62,7 +62,7 @@ Use this when the test's purpose is to verify *what* was logged. The `transports
```go
import (
"go.loglayer.dev/v3"
- lltest "go.loglayer.dev/transports/testing/v2"
+ lltest "go.loglayer.dev/transports/testing/v3"
)
func TestRequestLogging(t *testing.T) {
diff --git a/docs/src/migrating.md b/docs/src/migrating.md
index c982dac..aaec540 100644
--- a/docs/src/migrating.md
+++ b/docs/src/migrating.md
@@ -16,10 +16,6 @@ LogLayer for Go has shipped two major versions. Each upgrade is a short checklis
This section covers the core (`go.loglayer.dev/v3`); the import-path notes also apply to the core's sub-packages (`/v3/transport`, `/v3/utils/...`).
-::: warning Transports are still on v2 in this release
-The transports keep their `v2` paths until the follow-up release that bumps them to `v3`. Until then, code that pairs the v3 core with a `v2` transport path does not compile together. Migrate the core first, then the transports when their v3 bumps land.
-:::
-
### Do I have to migrate?
Not immediately. v2.x continues to work; the v2 module path (`go.loglayer.dev/v2`) keeps resolving to its last v2 tag and the v2 metadata placement stays intact there. Future feature work and bug fixes ship at v3 (`go.loglayer.dev/v3`), so the migration is the path forward but it's not on a deadline.
@@ -30,7 +26,6 @@ You can migrate one module at a time: a project that uses several `loglayer-go`
- **Import paths bump to `/v3`** for the core and its sub-packages: `go.loglayer.dev/v2` → `go.loglayer.dev/v3`, `go.loglayer.dev/v2/transport` → `go.loglayer.dev/v3/transport`, and so on. The package import names (`loglayer`, `transport`) do not change.
- **Metadata nests under `"metadata"` by default.** When `Config.MetadataFieldName` is empty, the core resolves it to `"metadata"`, so both map and struct metadata render under that single key uniformly across every transport. This closes the asymmetric v2 gap where renderers flattened map metadata at the root while wrappers nested non-map values under a hardcoded key. It applies to every transport, including third-party ones, because the resolved key ships on `TransportParams.Schema`.
-- **`transports/structured` stays on v2 for this release.** The structured transport moves to `/v3` in a follow-up release, along with sanitized output and empty-message handling. The default nesting flip above applies to it today through the schema key.
### The one-line opt-out
@@ -62,7 +57,7 @@ In source files:
)
```
-Then run `go mod tidy`. Transport and plugin sub-modules keep their current paths until each ships its own v3 bump; check each page in the [Transports overview](/transports/) and [Plugins overview](/plugins/) for the current path.
+Then run `go mod tidy`. Every sub-module that ships its own module takes its **next major path bump** in this release: transports and plugins that re-export core types move `/v2 → /v3` (e.g. `transports/structured`), and the wrappers built on the `http` transport move to `/v2` (e.g. `transports/betterstack`, `transports/newrelic`) or `/v3` (`transports/datadog`). Check the [Transports overview](/transports/) and [Plugins overview](/plugins/) for each module's exact path; imports and `go get` lines change accordingly.
### Step 2: decide on metadata placement
@@ -85,6 +80,13 @@ Any consumer of the emitted JSON that parsed map metadata at the root now finds
All of these are addressed by `FlattenMetadata: true` (v2 shape) or by updating the consumer to read the `"metadata"` key.
+The `structured` transport also gained v3 behavior changes on top of the path bump:
+
+- **Top-level sanitization**: the message and top-level keys and string values are run through `sanitize.Message`, so ANSI escapes, CR/LF, and bidi overrides in user-controlled strings are stripped from the output. Multiline messages keep their authored line boundaries.
+- **Empty messages omit `msg`**: `WithFields(...).Info("")` emits no `msg` key instead of `"msg":""` (v2 rendered the empty string).
+
+Consumers that assert on the structured transport's exact JSON shape (tests, dashboards keyed on a `msg` field that is now absent for empty messages) should update those assertions.
+
## Migrating to v2
`loglayer-go` v2 ships two breaking changes: **every import path bumps to `/v2`**, and **the loglayer core no longer mutates `Messages[0]` to fold the `WithPrefix` value into the message text.** The prefix now flows through `TransportParams.Prefix` and each transport decides how to render it.
diff --git a/docs/src/plugins/_partials/plugin-list.md b/docs/src/plugins/_partials/plugin-list.md
index 67b443a..acd691c 100644
--- a/docs/src/plugins/_partials/plugin-list.md
+++ b/docs/src/plugins/_partials/plugin-list.md
@@ -2,10 +2,10 @@
| Name | Version | Go Reference | Description |
|------|---------|--------------|-------------|
-| [Redact](/plugins/redact) | [](https://github.com/loglayer/loglayer-go/releases?q=plugins/redact/&expanded=true) | [](https://pkg.go.dev/go.loglayer.dev/plugins/redact/v2) | 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](/plugins/sampling) | [](https://github.com/loglayer/loglayer-go/releases?q=plugins/sampling/&expanded=true) | [](https://pkg.go.dev/go.loglayer.dev/plugins/sampling/v2) | 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](/plugins/fmtlog) | [](https://github.com/loglayer/loglayer-go/releases?q=plugins/fmtlog/&expanded=true) | [](https://pkg.go.dev/go.loglayer.dev/plugins/fmtlog/v2) | 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](/plugins/datadogtrace) | [](https://github.com/loglayer/loglayer-go/releases?q=plugins/datadogtrace/&expanded=true) | [](https://pkg.go.dev/go.loglayer.dev/plugins/datadogtrace/v2) | 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](/plugins/oteltrace) | [](https://github.com/loglayer/loglayer-go/releases?q=plugins/oteltrace/&expanded=true) | [](https://pkg.go.dev/go.loglayer.dev/plugins/oteltrace/v2) | 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`. |
+| [Redact](/plugins/redact) | [](https://github.com/loglayer/loglayer-go/releases?q=plugins/redact/&expanded=true) | [](https://pkg.go.dev/go.loglayer.dev/plugins/redact/v3) | 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](/plugins/sampling) | [](https://github.com/loglayer/loglayer-go/releases?q=plugins/sampling/&expanded=true) | [](https://pkg.go.dev/go.loglayer.dev/plugins/sampling/v3) | 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](/plugins/fmtlog) | [](https://github.com/loglayer/loglayer-go/releases?q=plugins/fmtlog/&expanded=true) | [](https://pkg.go.dev/go.loglayer.dev/plugins/fmtlog/v3) | 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](/plugins/datadogtrace) | [](https://github.com/loglayer/loglayer-go/releases?q=plugins/datadogtrace/&expanded=true) | [](https://pkg.go.dev/go.loglayer.dev/plugins/datadogtrace/v3) | 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](/plugins/oteltrace) | [](https://github.com/loglayer/loglayer-go/releases?q=plugins/oteltrace/&expanded=true) | [](https://pkg.go.dev/go.loglayer.dev/plugins/oteltrace/v3) | 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`. |
diff --git a/docs/src/plugins/datadogtrace.md b/docs/src/plugins/datadogtrace.md
index ac74e10..f79f51c 100644
--- a/docs/src/plugins/datadogtrace.md
+++ b/docs/src/plugins/datadogtrace.md
@@ -10,7 +10,7 @@ description: "Inject Datadog APM trace and span IDs into log entries for log/tra
`plugins/datadogtrace` adds Datadog's [log/trace correlation](https://docs.datadoghq.com/tracing/other_telemetry/connect_logs_and_traces/) fields to every log entry that carries an active span via `WithContext`. Once your logs ship to Datadog, the UI will link each log line to the trace it originated in.
```sh
-go get go.loglayer.dev/plugins/datadogtrace/v2
+go get go.loglayer.dev/plugins/datadogtrace/v3
```
The plugin is **tracer-agnostic**: you wire up a small extractor function that pulls the trace and span IDs from a `context.Context`. This avoids forcing a specific dd-trace-go version (or any tracer dependency at all) on LogLayer's main module; your service already imports the tracer it uses.
@@ -28,8 +28,8 @@ import (
ddtracer "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer"
"go.loglayer.dev/v3"
- "go.loglayer.dev/plugins/datadogtrace/v2"
- "go.loglayer.dev/transports/structured/v2"
+ "go.loglayer.dev/plugins/datadogtrace/v3"
+ "go.loglayer.dev/transports/structured/v3"
)
func main() {
diff --git a/docs/src/plugins/fmtlog.md b/docs/src/plugins/fmtlog.md
index 27148de..bfd4b7d 100644
--- a/docs/src/plugins/fmtlog.md
+++ b/docs/src/plugins/fmtlog.md
@@ -10,18 +10,18 @@ description: "Opt-in fmt.Sprintf semantics for multi-arg log calls."
`fmtlog` is a one-line plugin that opts a logger into `fmt.Sprintf`-style format strings. After registration, every call where the first message is a string and there are extra arguments is rewritten via `fmt.Sprintf(messages[0], messages[1:]...)` before downstream `MessageHook`s run.
```sh
-go get go.loglayer.dev/plugins/fmtlog/v2
+go get go.loglayer.dev/plugins/fmtlog/v3
```
-`fmtlog` is its own Go module under `go.loglayer.dev/plugins/fmtlog/v2`, with no third-party dependencies beyond the main `go.loglayer.dev/v3` module it implements `Plugin` against.
+`fmtlog` is its own Go module under `go.loglayer.dev/plugins/fmtlog/v3`, with no third-party dependencies beyond the main `go.loglayer.dev/v3` module it implements `Plugin` against.
## Basic Usage
```go
import (
"go.loglayer.dev/v3"
- "go.loglayer.dev/plugins/fmtlog/v2"
- "go.loglayer.dev/transports/structured/v2"
+ "go.loglayer.dev/plugins/fmtlog/v3"
+ "go.loglayer.dev/transports/structured/v3"
)
log := loglayer.New(loglayer.Config{
diff --git a/docs/src/plugins/oteltrace.md b/docs/src/plugins/oteltrace.md
index 5d4f7cb..1178a5c 100644
--- a/docs/src/plugins/oteltrace.md
+++ b/docs/src/plugins/oteltrace.md
@@ -10,11 +10,11 @@ description: "Inject OpenTelemetry trace_id and span_id into log entries for log
`plugins/oteltrace` adds the active OpenTelemetry span's `trace_id` and `span_id` to every log entry that carries a `context.Context`. Use it when your service runs OpenTelemetry tracing but ships logs to a destination other than the OTel logs pipeline (structured JSON to stdout, Datadog HTTP intake, Loki, custom transports).
```sh
-go get go.loglayer.dev/plugins/oteltrace/v2
+go get go.loglayer.dev/plugins/oteltrace/v3
```
::: info Separate module
-`plugins/oteltrace` ships as its own Go module (`go.loglayer.dev/plugins/oteltrace/v2`) so the OpenTelemetry API's Go-version requirement doesn't bind the main `go.loglayer.dev/v3` module. Requires **Go 1.25+** because that's the floor of `go.opentelemetry.io/otel/trace` and `go.opentelemetry.io/otel/baggage` at current versions.
+`plugins/oteltrace` ships as its own Go module (`go.loglayer.dev/plugins/oteltrace/v3`) so the OpenTelemetry API's Go-version requirement doesn't bind the main `go.loglayer.dev/v3` module. Requires **Go 1.25+** because that's the floor of `go.opentelemetry.io/otel/trace` and `go.opentelemetry.io/otel/baggage` at current versions.
:::
::: info When to use this vs `transports/otellog`
@@ -28,8 +28,8 @@ go get go.loglayer.dev/plugins/oteltrace/v2
```go
import (
"go.loglayer.dev/v3"
- "go.loglayer.dev/plugins/oteltrace/v2"
- "go.loglayer.dev/transports/structured/v2"
+ "go.loglayer.dev/plugins/oteltrace/v3"
+ "go.loglayer.dev/transports/structured/v3"
)
log := loglayer.New(loglayer.Config{
diff --git a/docs/src/plugins/redact.md b/docs/src/plugins/redact.md
index 364589f..8d775df 100644
--- a/docs/src/plugins/redact.md
+++ b/docs/src/plugins/redact.md
@@ -10,7 +10,7 @@ description: "Replace values for sensitive keys, value patterns, or struct field
`plugins/redact` replaces sensitive values in metadata and persistent fields before any transport sees them. Useful for keeping secrets, PII, and credentials out of log output without rewriting every call site.
```sh
-go get go.loglayer.dev/plugins/redact/v2
+go get go.loglayer.dev/plugins/redact/v3
```
Dependency-free. Pure Go (only `regexp` from stdlib; the walker uses reflection from the standard library).
@@ -20,13 +20,12 @@ Dependency-free. Pure Go (only `regexp` from stdlib; the walker uses reflection
```go
import (
"go.loglayer.dev/v3"
- "go.loglayer.dev/plugins/redact/v2"
- "go.loglayer.dev/transports/structured/v2"
+ "go.loglayer.dev/plugins/redact/v3"
+ "go.loglayer.dev/transports/structured/v3"
)
log := loglayer.New(loglayer.Config{
- Transport: structured.New(structured.Config{}),
- MetadataFieldName: "metadata",
+ Transport: structured.New(structured.Config{}),
})
log.AddPlugin(redact.New(redact.Config{
diff --git a/docs/src/plugins/sampling.md b/docs/src/plugins/sampling.md
index 58a6363..755fe15 100644
--- a/docs/src/plugins/sampling.md
+++ b/docs/src/plugins/sampling.md
@@ -14,7 +14,7 @@ description: "Drop a fraction of log emissions to keep volume and cost under con
- **`Burst(n, window)`**: keep the first N emissions per rolling window, drop the rest. Best for hard caps like "no more than 100 logs/second."
```sh
-go get go.loglayer.dev/plugins/sampling/v2
+go get go.loglayer.dev/plugins/sampling/v3
```
Pure Go, no dependencies (uses `math/rand/v2` from the stdlib).
@@ -24,8 +24,8 @@ Pure Go, no dependencies (uses `math/rand/v2` from the stdlib).
```go
import (
"go.loglayer.dev/v3"
- "go.loglayer.dev/plugins/sampling/v2"
- "go.loglayer.dev/transports/structured/v2"
+ "go.loglayer.dev/plugins/sampling/v3"
+ "go.loglayer.dev/transports/structured/v3"
)
log := loglayer.New(loglayer.Config{
@@ -83,6 +83,6 @@ The `FixedRate` gate runs first and drops 99% of traffic. The `Burst` gate then
## Caveats
-- Sampling decisions happen **per transport** (the `SendGate` hook is called once per `(entry, transport)` pair). With multiple transports, `FixedRate` and `FixedRatePerLevel` perform an independent Bernoulli draw for each transport — one transport may keep the entry while another drops it. `Burst` shares a single counter across transports, so each per-transport call consumes a slot from the same window (e.g. with two transports and a 100/sec cap, you get ~50 fully-delivered entries per second). If you want per-transport sampling rates, use [Group routing](/logging-api/groups) with different transports per group.
+- Sampling decisions happen **per transport** (the `SendGate` hook is called once per `(entry, transport)` pair). With multiple transports, `FixedRate` and `FixedRatePerLevel` perform an independent Bernoulli draw for each transport: one transport may keep the entry while another drops it. `Burst` shares a single counter across transports, so each per-transport call consumes a slot from the same window (e.g. with two transports and a 100/sec cap, you get ~50 fully-delivered entries per second). If you want per-transport sampling rates, use [Group routing](/logging-api/groups) with different transports per group.
- The `Burst` sampler holds an internal mutex. Lock scope is tiny (a counter check + time comparison) but under extreme contention it can serialize the dispatch path. For most workloads this is negligible compared to the emission cost itself.
- Sampling does **not** affect plugin hooks that ran earlier (`OnFieldsCalled`, `OnMetadataCalled`). It only gates dispatch to transports.
diff --git a/docs/src/plugins/testing-plugins.md b/docs/src/plugins/testing-plugins.md
index 6077666..1f1c230 100644
--- a/docs/src/plugins/testing-plugins.md
+++ b/docs/src/plugins/testing-plugins.md
@@ -14,7 +14,7 @@ import (
"testing"
"go.loglayer.dev/v3"
- "go.loglayer.dev/plugins/plugintest/v2"
+ "go.loglayer.dev/plugins/plugintest/v3"
)
func TestMyPlugin_AddsField(t *testing.T) {
diff --git a/docs/src/public/llms-full.txt b/docs/src/public/llms-full.txt
index 1465a49..510e465 100644
--- a/docs/src/public/llms-full.txt
+++ b/docs/src/public/llms-full.txt
@@ -14,43 +14,43 @@ LogLayer is multi-module: most transports, plugins, and integrations ship as the
```sh
# Renderer transports
-go get go.loglayer.dev/transports/structured/v2
-go get go.loglayer.dev/transports/console/v2
-go get go.loglayer.dev/transports/pretty/v2
-go get go.loglayer.dev/transports/cli/v2
-go get go.loglayer.dev/transports/testing/v2
-go get go.loglayer.dev/transports/blank/v2
+go get go.loglayer.dev/transports/structured/v3
+go get go.loglayer.dev/transports/console/v3
+go get go.loglayer.dev/transports/pretty/v3
+go get go.loglayer.dev/transports/cli/v3
+go get go.loglayer.dev/transports/testing/v3
+go get go.loglayer.dev/transports/blank/v3
# Cloud (managed log services)
-go get go.loglayer.dev/transports/axiom/v2
-go get go.loglayer.dev/transports/betterstack
-go get go.loglayer.dev/transports/datadog/v2
-go get go.loglayer.dev/transports/gcplogging/v2
-go get go.loglayer.dev/transports/sentry/v2
+go get go.loglayer.dev/transports/axiom/v3
+go get go.loglayer.dev/transports/betterstack/v2
+go get go.loglayer.dev/transports/datadog/v3
+go get go.loglayer.dev/transports/gcplogging/v3
+go get go.loglayer.dev/transports/sentry/v3
# Other transports
-go get go.loglayer.dev/transports/http/v2
-go get go.loglayer.dev/transports/lumberjack/v2
-go get go.loglayer.dev/transports/otellog/v2
+go get go.loglayer.dev/transports/http/v3
+go get go.loglayer.dev/transports/lumberjack/v3
+go get go.loglayer.dev/transports/otellog/v3
# Wrappers around third-party loggers
-go get go.loglayer.dev/transports/charmlog/v2
-go get go.loglayer.dev/transports/logrus/v2
-go get go.loglayer.dev/transports/phuslu/v2
-go get go.loglayer.dev/transports/slog/v2
-go get go.loglayer.dev/transports/zap/v2
-go get go.loglayer.dev/transports/zerolog/v2
+go get go.loglayer.dev/transports/charmlog/v3
+go get go.loglayer.dev/transports/logrus/v3
+go get go.loglayer.dev/transports/phuslu/v3
+go get go.loglayer.dev/transports/slog/v3
+go get go.loglayer.dev/transports/zap/v3
+go get go.loglayer.dev/transports/zerolog/v3
# Plugins
-go get go.loglayer.dev/plugins/redact/v2
-go get go.loglayer.dev/plugins/sampling/v2
-go get go.loglayer.dev/plugins/fmtlog/v2
-go get go.loglayer.dev/plugins/datadogtrace/v2
-go get go.loglayer.dev/plugins/oteltrace/v2
+go get go.loglayer.dev/plugins/redact/v3
+go get go.loglayer.dev/plugins/sampling/v3
+go get go.loglayer.dev/plugins/fmtlog/v3
+go get go.loglayer.dev/plugins/datadogtrace/v3
+go get go.loglayer.dev/plugins/oteltrace/v3
# Integrations
-go get go.loglayer.dev/integrations/loghttp/v2
-go get go.loglayer.dev/integrations/sloghandler/v2
+go get go.loglayer.dev/integrations/loghttp/v3
+go get go.loglayer.dev/integrations/sloghandler/v3
```
The full module list is in [`monorel.toml`](https://github.com/loglayer/loglayer-go/blob/main/monorel.toml).
@@ -64,7 +64,7 @@ import (
"errors"
"go.loglayer.dev/v3"
- "go.loglayer.dev/transports/structured/v2"
+ "go.loglayer.dev/transports/structured/v3"
)
func main() {
@@ -636,7 +636,7 @@ structured.New(structured.Config{
Plain text, logfmt-style key/value pairs after the message. Writes to stdout (info/debug) or stderr (warn+) by default.
```go
-import "go.loglayer.dev/transports/console/v2"
+import "go.loglayer.dev/transports/console/v3"
console.New(console.Config{
Writer: os.Stdout, // single writer for all levels
@@ -652,7 +652,7 @@ console.New(console.Config{
One JSON object per entry. Recommended for production.
```go
-import "go.loglayer.dev/transports/structured/v2"
+import "go.loglayer.dev/transports/structured/v3"
structured.New(structured.Config{
Writer: os.Stdout,
@@ -667,7 +667,7 @@ structured.New(structured.Config{
Colorized terminal output. Theme-aware (Moonlight, Sunlight, Neon, Nature, Pastel) with three view modes (inline, message-only, expanded).
```go
-import "go.loglayer.dev/transports/pretty/v2"
+import "go.loglayer.dev/transports/pretty/v3"
pretty.New(pretty.Config{
Writer: os.Stdout,
@@ -682,7 +682,7 @@ pretty.New(pretty.Config{
Tuned for command-line application output rather than diagnostic logging. Short cargo/eslint-style level prefixes (`warning:`, `error:`, `fatal:`), stdout for info/debug/trace and stderr for warn/error/fatal/panic, per-stream TTY-detected ANSI color (info/debug/trace follow stdout, warn/error/fatal/panic follow stderr, pinned at construction), no timestamps. Renders the `WithPrefix` value in dim grey, separate from the level color. Fields/metadata dropped by default; opt in via `Config.ShowFields`. `Config.MessageFn func(loglayer.TransportParams) string` takes over the entire output line: its return value replaces the message plus the logfmt/table body (the level prefix, its color, and the user `WithPrefix` value still apply); an empty return falls back to normal rendering.
```go
-import "go.loglayer.dev/transports/cli/v2"
+import "go.loglayer.dev/transports/cli/v3"
cli.New(cli.Config{
// Writer: os.Stdout, // override (defaults: stdout for info/debug, stderr for warn+)
@@ -700,7 +700,7 @@ For slice-of-map metadata (`[]loglayer.Metadata`, `[]map[string]any`, `[]MyStruc
In-memory capture for assertion tests. Public package is `transports/testing`; `lltest` is the conventional import alias.
```go
-import lltest "go.loglayer.dev/transports/testing/v2"
+import lltest "go.loglayer.dev/transports/testing/v3"
lib := &lltest.TestLoggingLibrary{}
log := loglayer.New(loglayer.Config{
@@ -718,7 +718,7 @@ popped := lib.PopLine()
Delegates dispatch to a user-supplied function. For prototyping, metrics counters, message queues.
```go
-import "go.loglayer.dev/transports/blank/v2"
+import "go.loglayer.dev/transports/blank/v3"
blank.New(blank.Config{
BaseConfig: transport.BaseConfig{ID: "metrics"},
@@ -739,7 +739,7 @@ Managed log services. Async + batched; site-aware where applicable.
Datadog Logs HTTP intake. Site-aware URL, `DD-API-KEY` header, level→status mapping. Built on the HTTP transport.
```go
-import "go.loglayer.dev/transports/datadog/v2"
+import "go.loglayer.dev/transports/datadog/v3"
tr := datadog.New(datadog.Config{
APIKey: os.Getenv("DD_API_KEY"), // required
@@ -762,7 +762,7 @@ Generic shippers and on-disk sinks.
Generic batched HTTP POST to any endpoint. Pluggable Encoder.
```go
-import httptr "go.loglayer.dev/transports/http/v2"
+import httptr "go.loglayer.dev/transports/http/v3"
tr := httptr.New(httptr.Config{
URL: "https://logs.example.com/ingest",
@@ -784,7 +784,7 @@ defer tr.Close() // flush pending entries; bounded by ShutdownTimeout
One JSON object per log entry written to a rotating file. Render path matches `transports/structured`. Rotation delegated to `lumberjack.v2`. The package name `lumberjack` shadows the upstream `gopkg.in/natefinch/lumberjack.v2`; alias one of them when both are imported. The shorter `transports/file` import path is reserved for a future roll-our-own implementation.
```go
-import "go.loglayer.dev/transports/lumberjack/v2"
+import "go.loglayer.dev/transports/lumberjack/v3"
tr := lumberjack.New(lumberjack.Config{
Filename: "/var/log/myapp/app.log", // required
@@ -812,7 +812,7 @@ Wrap an existing third-party logger so loglayer's API sits on top of your establ
```go
import (
zlog "github.com/rs/zerolog"
- llzero "go.loglayer.dev/transports/zerolog/v2"
+ llzero "go.loglayer.dev/transports/zerolog/v3"
)
z := zlog.New(os.Stderr).With().Timestamp().Logger()
@@ -826,7 +826,7 @@ log := loglayer.New(loglayer.Config{
```go
import (
"go.uber.org/zap"
- llzap "go.loglayer.dev/transports/zap/v2"
+ llzap "go.loglayer.dev/transports/zap/v3"
)
z, _ := zap.NewProduction()
@@ -842,7 +842,7 @@ Fatal-level entries are written through a custom hook that prevents zap's defaul
```go
import (
"log/slog"
- llslog "go.loglayer.dev/transports/slog/v2"
+ llslog "go.loglayer.dev/transports/slog/v3"
)
handler := slog.NewJSONHandler(os.Stderr, nil)
@@ -863,7 +863,7 @@ Same wrapping pattern; see [transports overview](https://go.loglayer.dev/transpo
Emits each entry as an OTel `log.Record` on a `go.opentelemetry.io/otel/log.Logger`.
```go
-import "go.loglayer.dev/transports/otellog/v2"
+import "go.loglayer.dev/transports/otellog/v3"
tr := otellog.New(otellog.Config{
Name: "checkout-api", // instrumentation scope name
@@ -884,7 +884,7 @@ import (
"cloud.google.com/go/logging"
- "go.loglayer.dev/transports/gcplogging/v2"
+ "go.loglayer.dev/transports/gcplogging/v3"
)
ctx := context.Background()
@@ -910,7 +910,7 @@ Forwards each entry to a caller-supplied `sentry.Logger` (Sentry's structured-lo
import (
"github.com/getsentry/sentry-go"
- sentrytransport "go.loglayer.dev/transports/sentry/v2"
+ sentrytransport "go.loglayer.dev/transports/sentry/v3"
)
sentry.Init(sentry.ClientOptions{Dsn: "...", EnableLogs: true})
@@ -994,7 +994,7 @@ log.PluginCount()
### Redact Plugin
```go
-import "go.loglayer.dev/plugins/redact/v2"
+import "go.loglayer.dev/plugins/redact/v3"
log.AddPlugin(redact.New(redact.Config{
Keys: []string{"password", "apiKey", "ssn"},
@@ -1008,7 +1008,7 @@ Walks structs, maps, slices, and pointers via reflection at any depth; preserves
### Sampling Plugin
```go
-import "go.loglayer.dev/plugins/sampling/v2"
+import "go.loglayer.dev/plugins/sampling/v3"
// Independent Bernoulli draw per emission
log.AddPlugin(sampling.FixedRate(0.01)) // 1% kept
@@ -1030,7 +1030,7 @@ log.AddPlugin(sampling.Burst(100, time.Second))
Opt the logger into `fmt.Sprintf` semantics for multi-arg messages.
```go
-import "go.loglayer.dev/plugins/fmtlog/v2"
+import "go.loglayer.dev/plugins/fmtlog/v3"
log.AddPlugin(fmtlog.New())
log.Info("user %d logged in from %s", 42, "1.2.3.4")
@@ -1044,7 +1044,7 @@ Tracer-agnostic: supply a small `Extract` function for your tracer (dd-trace-go
```go
import (
ddtracer "github.com/DataDog/dd-trace-go/v2/ddtrace/tracer"
- "go.loglayer.dev/plugins/datadogtrace/v2"
+ "go.loglayer.dev/plugins/datadogtrace/v3"
)
log.AddPlugin(datadogtrace.New(datadogtrace.Config{
@@ -1071,7 +1071,7 @@ log.WithContext(r.Context()).Info("served")
Inject `trace_id`, `span_id`, optional `trace_flags`, W3C `trace_state`, and W3C baggage members.
```go
-import "go.loglayer.dev/plugins/oteltrace/v2"
+import "go.loglayer.dev/plugins/oteltrace/v3"
log.AddPlugin(oteltrace.New(oteltrace.Config{
TraceIDKey: "trace_id", // default
@@ -1089,7 +1089,7 @@ Baggage rides independently of the span: contexts with baggage but no span still
### loghttp (HTTP middleware)
```go
-import "go.loglayer.dev/integrations/loghttp/v2"
+import "go.loglayer.dev/integrations/loghttp/v3"
mux := http.NewServeMux()
handler := loghttp.Middleware(log, loghttp.Config{})(mux)
@@ -1109,7 +1109,7 @@ Auto-emits a "request completed" log on response with status, bytes written, dur
```go
import (
"log/slog"
- "go.loglayer.dev/integrations/sloghandler/v2"
+ "go.loglayer.dev/integrations/sloghandler/v3"
)
slog.SetDefault(slog.New(sloghandler.New(log)))
@@ -1132,7 +1132,7 @@ log.Info("invisible")
### Capture entries with the testing transport
```go
-import lltest "go.loglayer.dev/transports/testing/v2"
+import lltest "go.loglayer.dev/transports/testing/v3"
lib := &lltest.TestLoggingLibrary{}
log := loglayer.New(loglayer.Config{Transport: lltest.New(lltest.Config{Library: lib})})
@@ -1147,7 +1147,7 @@ lines := lib.Lines() // []lltest.LogLine
### Plugin testing helpers
```go
-import "go.loglayer.dev/plugins/plugintest/v2"
+import "go.loglayer.dev/plugins/plugintest/v3"
// Wires plugin into a logger backed by lltest, returns the lib for assertions
log, lib := plugintest.Install(t, redact.New(redact.Config{Keys: []string{"pw"}}))
diff --git a/docs/src/public/llms.txt b/docs/src/public/llms.txt
index 7d0a009..eb4126d 100644
--- a/docs/src/public/llms.txt
+++ b/docs/src/public/llms.txt
@@ -11,9 +11,9 @@ go get go.loglayer.dev/v3
Most transports and plugins ship as their own modules. Install only what you import:
```sh
-go get go.loglayer.dev/transports/structured/v2
-go get go.loglayer.dev/transports/zerolog/v2
-go get go.loglayer.dev/plugins/redact/v2
+go get go.loglayer.dev/transports/structured/v3
+go get go.loglayer.dev/transports/zerolog/v3
+go get go.loglayer.dev/plugins/redact/v3
```
## Quick Start
@@ -23,7 +23,7 @@ package main
import (
"go.loglayer.dev/v3"
- "go.loglayer.dev/transports/structured/v2"
+ "go.loglayer.dev/transports/structured/v3"
)
func main() {
@@ -347,7 +347,7 @@ The slog handler (`integrations/sloghandler`) forwards `slog.Record.PC` automati
log := loglayer.NewMock()
// Capture entries for assertion
-import lltest "go.loglayer.dev/transports/testing/v2"
+import lltest "go.loglayer.dev/transports/testing/v3"
lib := &lltest.TestLoggingLibrary{}
log := loglayer.New(loglayer.Config{
diff --git a/docs/src/transports/_partials/transport-list.md b/docs/src/transports/_partials/transport-list.md
index 580ba08..11372b8 100644
--- a/docs/src/transports/_partials/transport-list.md
+++ b/docs/src/transports/_partials/transport-list.md
@@ -6,12 +6,12 @@ Self-contained transports that format the entry and write it to an `io.Writer`.
| Name | Version | Go Reference | Description |
|------|---------|--------------|-------------|
-| [Blank](/transports/blank) | [](https://github.com/loglayer/loglayer-go/releases?q=transports/blank/&expanded=true) | [](https://pkg.go.dev/go.loglayer.dev/transports/blank/v2) | Delegates dispatch to a user-supplied function. For prototyping or one-off integrations. |
-| [CLI](/transports/cli) | [](https://github.com/loglayer/loglayer-go/releases?q=transports/cli/&expanded=true) | [](https://pkg.go.dev/go.loglayer.dev/transports/cli/v2) | Tuned for CLI apps: short level prefixes, stdout/stderr routing, TTY-detected color, no timestamps. |
-| [Console](/transports/console) | [](https://github.com/loglayer/loglayer-go/releases?q=transports/console/&expanded=true) | [](https://pkg.go.dev/go.loglayer.dev/transports/console/v2) | Plain `fmt.Println`-style output to stdout/stderr; minimal formatting. |
-| [Pretty](/transports/pretty) | [](https://github.com/loglayer/loglayer-go/releases?q=transports/pretty/&expanded=true) | [](https://pkg.go.dev/go.loglayer.dev/transports/pretty/v2) | Colorized, theme-aware terminal output. **Recommended for local dev.** |
-| [Structured](/transports/structured) | [](https://github.com/loglayer/loglayer-go/releases?q=transports/structured/&expanded=true) | [](https://pkg.go.dev/go.loglayer.dev/transports/structured/v2) | One JSON object per log entry. Recommended for production. |
-| [Testing](/transports/testing) | [](https://github.com/loglayer/loglayer-go/releases?q=transports/testing/&expanded=true) | [](https://pkg.go.dev/go.loglayer.dev/transports/testing/v2) | Captures entries in memory for tests. |
+| [Blank](/transports/blank) | [](https://github.com/loglayer/loglayer-go/releases?q=transports/blank/&expanded=true) | [](https://pkg.go.dev/go.loglayer.dev/transports/blank/v3) | Delegates dispatch to a user-supplied function. For prototyping or one-off integrations. |
+| [CLI](/transports/cli) | [](https://github.com/loglayer/loglayer-go/releases?q=transports/cli/&expanded=true) | [](https://pkg.go.dev/go.loglayer.dev/transports/cli/v3) | Tuned for CLI apps: short level prefixes, stdout/stderr routing, TTY-detected color, no timestamps. |
+| [Console](/transports/console) | [](https://github.com/loglayer/loglayer-go/releases?q=transports/console/&expanded=true) | [](https://pkg.go.dev/go.loglayer.dev/transports/console/v3) | Plain `fmt.Println`-style output to stdout/stderr; minimal formatting. |
+| [Pretty](/transports/pretty) | [](https://github.com/loglayer/loglayer-go/releases?q=transports/pretty/&expanded=true) | [](https://pkg.go.dev/go.loglayer.dev/transports/pretty/v3) | Colorized, theme-aware terminal output. **Recommended for local dev.** |
+| [Structured](/transports/structured) | [](https://github.com/loglayer/loglayer-go/releases?q=transports/structured/&expanded=true) | [](https://pkg.go.dev/go.loglayer.dev/transports/structured/v3) | One JSON object per log entry. Recommended for production. |
+| [Testing](/transports/testing) | [](https://github.com/loglayer/loglayer-go/releases?q=transports/testing/&expanded=true) | [](https://pkg.go.dev/go.loglayer.dev/transports/testing/v3) | Captures entries in memory for tests. |
@@ -23,12 +23,12 @@ Managed log services. Async + batched by default; site-aware where applicable.
| Name | Version | Go Reference | Description |
|------|---------|--------------|-------------|
-| [Axiom](/transports/axiom) | [](https://github.com/loglayer/loglayer-go/releases?q=transports/axiom/&expanded=true) | [](https://pkg.go.dev/go.loglayer.dev/transports/axiom/v2) | Ships logs to Axiom via caller-supplied `*axiom.Client`. NDJSON ingestion with configurable message field. |
-| [Better Stack](/transports/betterstack) | [](https://github.com/loglayer/loglayer-go/releases?q=transports/betterstack/&expanded=true) | [](https://pkg.go.dev/go.loglayer.dev/transports/betterstack) | Ships logs to Better Stack via HTTP intake. Source token auth, configurable timestamp field. |
-| [Datadog](/transports/datadog) | [](https://github.com/loglayer/loglayer-go/releases?q=transports/datadog/&expanded=true) | [](https://pkg.go.dev/go.loglayer.dev/transports/datadog/v2) | Datadog Logs HTTP intake. Site-aware URL, DD-API-KEY header, status mapping. |
-| [Google Cloud Logging](/transports/gcplogging) | [](https://github.com/loglayer/loglayer-go/releases?q=transports/gcplogging/&expanded=true) | [](https://pkg.go.dev/go.loglayer.dev/transports/gcplogging/v2) | Forwards entries to a caller-supplied `*logging.Logger` from `cloud.google.com/go/logging`. Severity mapping, root-level Entry skeleton, async + sync dispatch. |
-| [New Relic](/transports/newrelic) | [](https://github.com/loglayer/loglayer-go/releases?q=transports/newrelic/&expanded=true) | [](https://pkg.go.dev/go.loglayer.dev/transports/newrelic) | New Relic Log Ingest API. Site-aware URL, api-key header, LogEvent encoding. |
-| [Sentry](/transports/sentry) | [](https://github.com/loglayer/loglayer-go/releases?q=transports/sentry/&expanded=true) | [](https://pkg.go.dev/go.loglayer.dev/transports/sentry/v2) | Forwards entries to a `sentry.Logger`. Routes fatal/panic through `LFatal` so loglayer's core controls termination. |
+| [Axiom](/transports/axiom) | [](https://github.com/loglayer/loglayer-go/releases?q=transports/axiom/&expanded=true) | [](https://pkg.go.dev/go.loglayer.dev/transports/axiom/v3) | Ships logs to Axiom via caller-supplied `*axiom.Client`. NDJSON ingestion with configurable message field. |
+| [Better Stack](/transports/betterstack) | [](https://github.com/loglayer/loglayer-go/releases?q=transports/betterstack/&expanded=true) | [](https://pkg.go.dev/go.loglayer.dev/transports/betterstack/v2) | Ships logs to Better Stack via HTTP intake. Source token auth, configurable timestamp field. |
+| [Datadog](/transports/datadog) | [](https://github.com/loglayer/loglayer-go/releases?q=transports/datadog/&expanded=true) | [](https://pkg.go.dev/go.loglayer.dev/transports/datadog/v3) | Datadog Logs HTTP intake. Site-aware URL, DD-API-KEY header, status mapping. |
+| [Google Cloud Logging](/transports/gcplogging) | [](https://github.com/loglayer/loglayer-go/releases?q=transports/gcplogging/&expanded=true) | [](https://pkg.go.dev/go.loglayer.dev/transports/gcplogging/v3) | Forwards entries to a caller-supplied `*logging.Logger` from `cloud.google.com/go/logging`. Severity mapping, root-level Entry skeleton, async + sync dispatch. |
+| [New Relic](/transports/newrelic) | [](https://github.com/loglayer/loglayer-go/releases?q=transports/newrelic/&expanded=true) | [](https://pkg.go.dev/go.loglayer.dev/transports/newrelic/v2) | New Relic Log Ingest API. Site-aware URL, api-key header, LogEvent encoding. |
+| [Sentry](/transports/sentry) | [](https://github.com/loglayer/loglayer-go/releases?q=transports/sentry/&expanded=true) | [](https://pkg.go.dev/go.loglayer.dev/transports/sentry/v3) | Forwards entries to a `sentry.Logger`. Routes fatal/panic through `LFatal` so loglayer's core controls termination. |
@@ -40,9 +40,9 @@ Generic shippers and on-disk sinks.
| Name | Version | Go Reference | Description |
|------|---------|--------------|-------------|
-| [File (Lumberjack)](/transports/lumberjack) | [](https://github.com/loglayer/loglayer-go/releases?q=transports/lumberjack/&expanded=true) | [](https://pkg.go.dev/go.loglayer.dev/transports/lumberjack/v2) | One JSON object per line written to a rotating file. Backed by `lumberjack.v2`. |
-| [HTTP](/transports/http) | [](https://github.com/loglayer/loglayer-go/releases?q=transports/http/&expanded=true) | [](https://pkg.go.dev/go.loglayer.dev/transports/http/v2) | Generic batched HTTP POST to any endpoint. Pluggable Encoder. |
-| [OpenTelemetry Logs](/transports/otellog) | [](https://github.com/loglayer/loglayer-go/releases?q=transports/otellog/&expanded=true) | [](https://pkg.go.dev/go.loglayer.dev/transports/otellog/v2) | Emits to an OTel `log.Logger`. Forwards `WithContext` so SDK processors can correlate with the active span. |
+| [File (Lumberjack)](/transports/lumberjack) | [](https://github.com/loglayer/loglayer-go/releases?q=transports/lumberjack/&expanded=true) | [](https://pkg.go.dev/go.loglayer.dev/transports/lumberjack/v3) | One JSON object per line written to a rotating file. Backed by `lumberjack.v2`. |
+| [HTTP](/transports/http) | [](https://github.com/loglayer/loglayer-go/releases?q=transports/http/&expanded=true) | [](https://pkg.go.dev/go.loglayer.dev/transports/http/v3) | Generic batched HTTP POST to any endpoint. Pluggable Encoder. |
+| [OpenTelemetry Logs](/transports/otellog) | [](https://github.com/loglayer/loglayer-go/releases?q=transports/otellog/&expanded=true) | [](https://pkg.go.dev/go.loglayer.dev/transports/otellog/v3) | Emits to an OTel `log.Logger`. Forwards `WithContext` so SDK processors can correlate with the active span. |
@@ -54,11 +54,11 @@ Transports that hand the entry off to an existing third-party logger you already
| Name | Version | Go Reference | Description |
|------|---------|--------------|-------------|
-| [charmbracelet/log](/transports/charmlog) | [](https://github.com/loglayer/loglayer-go/releases?q=transports/charmlog/&expanded=true) | [](https://pkg.go.dev/go.loglayer.dev/transports/charmlog/v2) | Pretty terminal-friendly logger from Charm |
-| [log/slog](/transports/slog) | [](https://github.com/loglayer/loglayer-go/releases?q=transports/slog/&expanded=true) | [](https://pkg.go.dev/go.loglayer.dev/transports/slog/v2) | Wraps a stdlib `*slog.Logger`. Forwards `WithContext` to handlers. |
-| [logrus](/transports/logrus) | [](https://github.com/loglayer/loglayer-go/releases?q=transports/logrus/&expanded=true) | [](https://pkg.go.dev/go.loglayer.dev/transports/logrus/v2) | The classic structured logger |
-| [phuslu/log](/transports/phuslu) | [](https://github.com/loglayer/loglayer-go/releases?q=transports/phuslu/&expanded=true) | [](https://pkg.go.dev/go.loglayer.dev/transports/phuslu/v2) | High-performance zero-alloc JSON logger. Always exits on fatal. |
-| [Zap](/transports/zap) | [](https://github.com/loglayer/loglayer-go/releases?q=transports/zap/&expanded=true) | [](https://pkg.go.dev/go.loglayer.dev/transports/zap/v2) | Wraps a `*zap.Logger` |
-| [Zerolog](/transports/zerolog) | [](https://github.com/loglayer/loglayer-go/releases?q=transports/zerolog/&expanded=true) | [](https://pkg.go.dev/go.loglayer.dev/transports/zerolog/v2) | Wraps a `*zerolog.Logger` |
+| [charmbracelet/log](/transports/charmlog) | [](https://github.com/loglayer/loglayer-go/releases?q=transports/charmlog/&expanded=true) | [](https://pkg.go.dev/go.loglayer.dev/transports/charmlog/v3) | Pretty terminal-friendly logger from Charm |
+| [log/slog](/transports/slog) | [](https://github.com/loglayer/loglayer-go/releases?q=transports/slog/&expanded=true) | [](https://pkg.go.dev/go.loglayer.dev/transports/slog/v3) | Wraps a stdlib `*slog.Logger`. Forwards `WithContext` to handlers. |
+| [logrus](/transports/logrus) | [](https://github.com/loglayer/loglayer-go/releases?q=transports/logrus/&expanded=true) | [](https://pkg.go.dev/go.loglayer.dev/transports/logrus/v3) | The classic structured logger |
+| [phuslu/log](/transports/phuslu) | [](https://github.com/loglayer/loglayer-go/releases?q=transports/phuslu/&expanded=true) | [](https://pkg.go.dev/go.loglayer.dev/transports/phuslu/v3) | High-performance zero-alloc JSON logger. Always exits on fatal. |
+| [Zap](/transports/zap) | [](https://github.com/loglayer/loglayer-go/releases?q=transports/zap/&expanded=true) | [](https://pkg.go.dev/go.loglayer.dev/transports/zap/v3) | Wraps a `*zap.Logger` |
+| [Zerolog](/transports/zerolog) | [](https://github.com/loglayer/loglayer-go/releases?q=transports/zerolog/&expanded=true) | [](https://pkg.go.dev/go.loglayer.dev/transports/zerolog/v3) | Wraps a `*zerolog.Logger` |
diff --git a/docs/src/transports/axiom.md b/docs/src/transports/axiom.md
index 3c6f3ed..ef3fe09 100644
--- a/docs/src/transports/axiom.md
+++ b/docs/src/transports/axiom.md
@@ -9,10 +9,10 @@ description: "Forward LogLayer entries to Axiom via github.com/axiomhq/axiom-go.
Ships structured logs to [Axiom](https://axiom.co) using the official [axiom-go SDK](https://github.com/axiomhq/axiom-go). The transport constructs a JSON object from each entry and sends it via `Client.Ingest()` as NDJSON.
-Import path: `go.loglayer.dev/transports/axiom/v2`. Package name: `axiom`.
+Import path: `go.loglayer.dev/transports/axiom/v3`. Package name: `axiom`.
```sh
-go get go.loglayer.dev/transports/axiom/v2
+go get go.loglayer.dev/transports/axiom/v3
```
## Authenticating
@@ -32,7 +32,7 @@ The dataset is set on the transport via `Config.DatasetName`, not an env var.
import (
axiomgo "github.com/axiomhq/axiom-go/axiom"
"go.loglayer.dev/v3"
- "go.loglayer.dev/transports/axiom/v2"
+ "go.loglayer.dev/transports/axiom/v3"
)
// The client picks up AXIOM_TOKEN (and AXIOM_ORG_ID for personal tokens)
@@ -56,7 +56,7 @@ log := loglayer.New(loglayer.Config{
import (
axiomgo "github.com/axiomhq/axiom-go/axiom"
"go.loglayer.dev/v3"
- "go.loglayer.dev/transports/axiom/v2"
+ "go.loglayer.dev/transports/axiom/v3"
)
client, err := axiomgo.NewClient(
diff --git a/docs/src/transports/betterstack.md b/docs/src/transports/betterstack.md
index 3a3e0bf..394572e 100644
--- a/docs/src/transports/betterstack.md
+++ b/docs/src/transports/betterstack.md
@@ -9,12 +9,8 @@ description: Ship logs to Better Stack's HTTP intake API.
Sends log entries to [Better Stack](https://betterstack.com) via their HTTP Logs Endpoint. Built on the [HTTP transport](/transports/http) with a Better Stack-specific encoder and bearer token authentication.
-::: info Interim state: this transport is still on v2
-This transport keeps its `v2` path and its pre-v3 metadata placement for this release; the placement described below is what the current `v2` transport produces. The v3 core resolves an empty `MetadataFieldName` to `"metadata"`, so pairing this transport with the v3 core passes a non-empty schema key and changes the placement. The transport's own v3 bump ships in a follow-up release.
-:::
-
```sh
-go get go.loglayer.dev/transports/betterstack
+go get go.loglayer.dev/transports/betterstack/v2
```
## Getting a Source Token
@@ -26,7 +22,7 @@ To get the source token:
1. Sign in to Better Stack at [https://betterstack.com](https://betterstack.com).
2. Navigate to **Logs** → **Sources**.
3. Either copy an existing source's token or create a new log source and copy its token.
-4. Store the token securely—treat it like a password.
+4. Store the token securely. Treat it like a password.
The source token is a secret. Load it from an environment variable or secret manager rather than hard-coding it in source.
@@ -36,7 +32,7 @@ The source token is a secret. Load it from an environment variable or secret man
import (
"os"
"go.loglayer.dev/v3"
- "go.loglayer.dev/transports/betterstack"
+ "go.loglayer.dev/transports/betterstack/v2"
)
tr := betterstack.New(betterstack.Config{
@@ -147,7 +143,9 @@ Each log entry becomes one object in a JSON array:
"level": "info",
"message": "served request",
"requestId": "abc",
- "durationMs": 42
+ "metadata": {
+ "durationMs": 42
+ }
}
]
```
@@ -156,9 +154,9 @@ Each log entry becomes one object in a JSON array:
- **Level**: String representation of the log level (trace, debug, info, warn, error, fatal, panic).
- **Message**: The log message string.
- **Fields**: Persistent fields from `WithFields()` and `Child()` are merged at the root level.
-- **Metadata**: Map metadata merges at the root; non-map metadata is serialized as JSON under a `metadata` key (configurable via `loglayer.Config.MetadataFieldName`).
+- **Metadata**: Nests under `MetadataFieldName` (default `"metadata"`, configurable via `loglayer.Config.MetadataFieldName`).
-Persistent fields (`WithFields`) and metadata (`WithMetadata`) follow the [core placement rules](/configuration#fieldskey): when `FieldsKey` is empty, fields merge at the root of each log object; when `MetadataFieldName` is empty, map metadata merges at the root and non-map metadata nests under `metadata`. Set either knob on `loglayer.Config` to nest under a configured key instead.
+Persistent fields (`WithFields`) and metadata (`WithMetadata`) follow the [core placement rules](/configuration#fieldskey): when `FieldsKey` is empty, fields merge at the root of each log object; metadata nests under `MetadataFieldName` (default `"metadata"`). Set either knob on `loglayer.Config` to nest under a configured key instead.
## Level Mapping
diff --git a/docs/src/transports/blank.md b/docs/src/transports/blank.md
index 00d1352..bb4a201 100644
--- a/docs/src/transports/blank.md
+++ b/docs/src/transports/blank.md
@@ -16,7 +16,7 @@ The `blank` transport delegates `SendToLogger` to a function you supply inline.
If you find yourself repeating the same `blank.Config` across the codebase, promote it to its own transport package using the [Creating Transports](/transports/creating-transports) template.
```sh
-go get go.loglayer.dev/transports/blank/v2
+go get go.loglayer.dev/transports/blank/v3
```
## Basic Usage
@@ -25,7 +25,7 @@ go get go.loglayer.dev/transports/blank/v2
import (
"go.loglayer.dev/v3"
"go.loglayer.dev/v3/transport"
- "go.loglayer.dev/transports/blank/v2"
+ "go.loglayer.dev/transports/blank/v3"
)
log := loglayer.New(loglayer.Config{
diff --git a/docs/src/transports/charmlog.md b/docs/src/transports/charmlog.md
index 9039e87..086c675 100644
--- a/docs/src/transports/charmlog.md
+++ b/docs/src/transports/charmlog.md
@@ -7,10 +7,10 @@ description: Wrap a github.com/charmbracelet/log logger with LogLayer.
-Wraps an existing `*charmbracelet/log.Logger`. Map metadata flattens to alternating key/value pairs; struct metadata lands under a configurable key.
+Wraps an existing `*charmbracelet/log.Logger`. Metadata nests under a single configurable key.
```sh
-go get go.loglayer.dev/transports/charmlog/v2
+go get go.loglayer.dev/transports/charmlog/v3
go get github.com/charmbracelet/log
```
@@ -25,7 +25,7 @@ import (
clog "github.com/charmbracelet/log"
"go.loglayer.dev/v3"
- llcharm "go.loglayer.dev/transports/charmlog/v2"
+ llcharm "go.loglayer.dev/transports/charmlog/v3"
)
cl := clog.NewWithOptions(os.Stderr, clog.Options{
@@ -62,27 +62,18 @@ charmbracelet's `Logger.Fatal()` calls `os.Exit(1)`, but `Logger.Log(FatalLevel,
-### Map metadata → individual key/value pairs
+### Metadata nests under the metadata key
```go
log.WithMetadata(loglayer.Metadata{"requestId": "xyz", "n": 42}).Info("served")
-// INFO served requestId=xyz n=42
-```
-
-Each map entry becomes a `(key, value)` pair in the variadic `keyvals` argument to `Log`.
-
-### Struct metadata nests under the metadata key
-
-```go
-type User struct {
- ID int `json:"id"`
- Name string `json:"name"`
-}
+// INFO served metadata="{\"requestId\":\"xyz\",\"n\":42}"
log.WithMetadata(User{ID: 7, Name: "Alice"}).Info("user")
// INFO user metadata="{ID:7 Name:Alice}"
```
+With `Config.FlattenMetadata: true`, map metadata instead becomes `(key, value)` pairs in the variadic `keyvals` argument to `Log`.
+
charmbracelet renders the struct via its default formatter. Exact output shape depends on whether you've configured `JSONFormatter`, `TextFormatter`, or the default colored output.
To use a different key per call, wrap in a map:
diff --git a/docs/src/transports/cli.md b/docs/src/transports/cli.md
index d84cfd0..996cbc5 100644
--- a/docs/src/transports/cli.md
+++ b/docs/src/transports/cli.md
@@ -17,7 +17,7 @@ The `cli` transport renders log entries as plain user-facing CLI output. The clo
- **Table rendering for slice metadata.** Pass `[]loglayer.Metadata`, `[]SomeStruct`, or any other slice of map-shaped or struct-shaped values to `WithMetadata` / `MetadataOnly` and the transport renders a tabwriter-aligned table after the message. Same call site emits a proper JSON array when paired with the [structured](/transports/structured) transport. See [Table Rendering for Slice-of-Map Metadata](#table-rendering-for-slice-of-map-metadata) below.
```sh
-go get go.loglayer.dev/transports/cli/v2
+go get go.loglayer.dev/transports/cli/v3
```
## Basic Usage
@@ -25,7 +25,7 @@ go get go.loglayer.dev/transports/cli/v2
```go
import (
"go.loglayer.dev/v3"
- cli "go.loglayer.dev/transports/cli/v2"
+ cli "go.loglayer.dev/transports/cli/v3"
)
log := loglayer.New(loglayer.Config{
@@ -170,7 +170,7 @@ The standard CLI shape is to wire `-v` flags to loglayer's level state and `-vv`
```go
import (
"go.loglayer.dev/v3"
- cli "go.loglayer.dev/transports/cli/v2"
+ cli "go.loglayer.dev/transports/cli/v3"
)
func newLogger(verbosity int) *loglayer.LogLayer {
@@ -230,7 +230,7 @@ import (
"go.loglayer.dev/v3"
"go.loglayer.dev/v3/transport"
- cli "go.loglayer.dev/transports/cli/v2"
+ cli "go.loglayer.dev/transports/cli/v3"
)
log := loglayer.New(loglayer.Config{
@@ -264,7 +264,7 @@ The contrast with the [Console Transport](/transports/console): console's `Messa
Without a plugin, multi-argument log calls are space-joined: `log.Info("count:", n)` renders as `"count: 1234"`. CLI output usually wants format-string semantics:
```go
-import "go.loglayer.dev/plugins/fmtlog/v2"
+import "go.loglayer.dev/plugins/fmtlog/v3"
log := loglayer.New(loglayer.Config{
Transport: cli.New(cli.Config{}),
@@ -275,11 +275,11 @@ log.Info("Applied %d release(s) at %s:", count, sha)
log.Error("connecting to %s: %v", host, err)
```
-The plugin registers a single MessageHook that rewrites `[]any{format, args...}` to `[]any{fmt.Sprintf(format, args...)}`. Zero hot-path cost when a call has a single message; one Sprintf when there are extras. See [fmtlog](https://pkg.go.dev/go.loglayer.dev/plugins/fmtlog/v2) for the full API.
+The plugin registers a single MessageHook that rewrites `[]any{format, args...}` to `[]any{fmt.Sprintf(format, args...)}`. Zero hot-path cost when a call has a single message; one Sprintf when there are extras. See [fmtlog](https://pkg.go.dev/go.loglayer.dev/plugins/fmtlog/v3) for the full API.
### `redact` for token scrubbing
-If your CLI ever logs values that might include `GITHUB_TOKEN`, `GITLAB_TOKEN`, or other secrets via `WithMetadata`, pair with the [redact](https://pkg.go.dev/go.loglayer.dev/plugins/redact/v2) plugin. ANSI / CRLF sanitization is already on the table-cell and logfmt-value paths in this transport; the redact plugin closes the value-content side (token patterns, key allow / deny lists).
+If your CLI ever logs values that might include `GITHUB_TOKEN`, `GITLAB_TOKEN`, or other secrets via `WithMetadata`, pair with the [redact](https://pkg.go.dev/go.loglayer.dev/plugins/redact/v3) plugin. ANSI / CRLF sanitization is already on the table-cell and logfmt-value paths in this transport; the redact plugin closes the value-content side (token patterns, key allow / deny lists).
## Switching to JSON for `--json`
@@ -288,8 +288,8 @@ For machine-readable output, swap `cli` for [structured](/transports/structured)
```go
import (
"go.loglayer.dev/v3"
- cli "go.loglayer.dev/transports/cli/v2"
- "go.loglayer.dev/transports/structured/v2"
+ cli "go.loglayer.dev/transports/cli/v3"
+ "go.loglayer.dev/transports/structured/v3"
)
var t loglayer.Transport
diff --git a/docs/src/transports/console.md b/docs/src/transports/console.md
index a8b0b99..3c8408b 100644
--- a/docs/src/transports/console.md
+++ b/docs/src/transports/console.md
@@ -16,7 +16,7 @@ The console transport is intentionally minimal. For day-to-day local development
For production logging, use the [structured](/transports/structured) transport or one of the [logger wrappers](/transports/zerolog).
```sh
-go get go.loglayer.dev/transports/console/v2
+go get go.loglayer.dev/transports/console/v3
```
## Basic Usage
@@ -24,12 +24,11 @@ go get go.loglayer.dev/transports/console/v2
```go
import (
"go.loglayer.dev/v3"
- "go.loglayer.dev/transports/console/v2"
+ "go.loglayer.dev/transports/console/v3"
)
log := loglayer.New(loglayer.Config{
- Transport: console.New(console.Config{}),
- MetadataFieldName: "metadata",
+ Transport: console.New(console.Config{}),
})
log.Info("hello world")
@@ -79,13 +78,13 @@ type Config struct {
### Logfmt Rendering
-Fields and metadata render as `key=value` pairs after the message, in sorted-by-key order:
+Fields and metadata render as `key=value` pairs after the message, in sorted-by-key order. Metadata nests under its key (`"metadata"` by default):
```go
log.WithFields(loglayer.Fields{"requestId": "abc"}).
WithMetadata(loglayer.Metadata{"status": 200, "bytes": 1024}).
Info("request served")
-// "request served bytes=1024 requestId=abc status=200"
+// "request served requestId=abc metadata="{\"bytes\":1024,\"status\":200}""
```
Values render based on type:
@@ -164,7 +163,7 @@ When `MessageFn` is set, the logfmt tail still appends after its return value (u
## Metadata Handling
-Map metadata is merged into the same data bag as fields and errors. Struct metadata is JSON-roundtripped into root fields before logfmt rendering. See [Metadata](/logging-api/metadata) for the design.
+Metadata nests under [`MetadataFieldName`](/configuration#metadatafieldname) (default `"metadata"`), then renders as a JSON string in logfmt. See [Metadata](/logging-api/metadata) for the design.
## Threat Model: Plaintext (Not for Pipelines)
diff --git a/docs/src/transports/creating-transports.md b/docs/src/transports/creating-transports.md
index d14873c..1232f9f 100644
--- a/docs/src/transports/creating-transports.md
+++ b/docs/src/transports/creating-transports.md
@@ -130,7 +130,7 @@ Worked example: [`examples/custom-transport-attribute`](https://github.com/logla
#### Why two policies, not one
-The built-in transports settle on whichever of the two matches their backend, so callers see consistent behavior: map metadata always flattens to root keys; struct metadata always renders idiomatically per transport. That contract is what the [Metadata page](/logging-api/metadata) advertises to users, and the helpers above are how it's enforced.
+The built-in transports settle on whichever of the two matches their backend, so callers see consistent behavior. With the v3 core's default config, the schema key is `"metadata"`, so the whole metadata value nests under it on every transport. The flatten branch only runs when the config opts into it (`FlattenMetadata: true`), which restores the v2 shapes: map metadata flattens to root keys, struct metadata renders idiomatically per transport. That contract is what the [Metadata page](/logging-api/metadata) advertises to users, and the helpers above are how it's enforced.
### Don't reinvent
@@ -312,7 +312,7 @@ For testing a custom transport, see [Testing Transports](/transports/testing-tra
### Live Tests
-Transports that ship to third-party services (HTTP endpoints, cloud APIs) include build-tagged live tests (`//go:build livetest`) that hit the real API. These are **never documented** in the transport's VitePress docs — they're internal developer documentation only.
+Transports that ship to third-party services (HTTP endpoints, cloud APIs) include build-tagged live tests (`//go:build livetest`) that hit the real API. These are **never documented** in the transport's VitePress docs; they're internal developer documentation only.
Run them locally with the required credentials:
diff --git a/docs/src/transports/datadog.md b/docs/src/transports/datadog.md
index 8331b95..f625d17 100644
--- a/docs/src/transports/datadog.md
+++ b/docs/src/transports/datadog.md
@@ -9,12 +9,8 @@ description: Ship logs to the Datadog Logs HTTP intake API.
Sends log entries to Datadog's [Logs HTTP intake API](https://docs.datadoghq.com/api/latest/logs/#send-logs). Built on the [HTTP transport](/transports/http) with a Datadog-specific encoder, site-aware URL, and `DD-API-KEY` header.
-::: info Interim state: this transport is still on v2
-This transport keeps its `v2` path and its pre-v3 metadata placement for this release; the placement described below is what the current `v2` transport produces. The v3 core resolves an empty `MetadataFieldName` to `"metadata"`, so pairing this transport with the v3 core passes a non-empty schema key and changes the placement. The transport's own v3 bump ships in a follow-up release.
-:::
-
```sh
-go get go.loglayer.dev/transports/datadog/v2
+go get go.loglayer.dev/transports/datadog/v3
```
## Getting an API Key and Site
@@ -48,7 +44,7 @@ import (
"os"
"go.loglayer.dev/v3"
- "go.loglayer.dev/transports/datadog/v2"
+ "go.loglayer.dev/transports/datadog/v3"
)
hostname, _ := os.Hostname()
@@ -179,12 +175,14 @@ Each log entry becomes one object in a JSON array:
"message": "served request",
"date": "2026-04-26T12:00:00.123Z",
"requestId": "abc",
- "durationMs": 42
+ "metadata": {
+ "durationMs": 42
+ }
}
]
```
-Persistent fields (`WithFields`) and metadata (`WithMetadata`) follow the [core placement rules](/configuration#fieldskey): when `FieldsKey` is empty, fields merge at the root of each Datadog log object; when `MetadataFieldName` is empty, map metadata merges at the root and non-map metadata nests under `metadata`. Set either knob on `loglayer.Config` to nest under a configured key instead.
+Persistent fields (`WithFields`) and metadata (`WithMetadata`) follow the [core placement rules](/configuration#fieldskey): when `FieldsKey` is empty, fields merge at the root of each Datadog log object; metadata nests under `MetadataFieldName` (default `"metadata"`). Set either knob on `loglayer.Config` to nest under a configured key instead.
## Level → Status Mapping
diff --git a/docs/src/transports/gcplogging.md b/docs/src/transports/gcplogging.md
index fa7a717..3bc05ec 100644
--- a/docs/src/transports/gcplogging.md
+++ b/docs/src/transports/gcplogging.md
@@ -9,10 +9,10 @@ description: "Forward LogLayer entries to Google Cloud Logging via cloud.google.
Forwards each entry to a caller-supplied [`*logging.Logger`](https://pkg.go.dev/cloud.google.com/go/logging#Logger) from the official Google Cloud client library. Use this on Google Cloud Run, GKE, App Engine, Cloud Functions, or any environment where you want logs to land in Google Cloud Logging (formerly Stackdriver).
-Import path: `go.loglayer.dev/transports/gcplogging/v2`. Package name: `gcplogging` (no collision with the SDK's `logging` package).
+Import path: `go.loglayer.dev/transports/gcplogging/v3`. Package name: `gcplogging` (no collision with the SDK's `logging` package).
```sh
-go get go.loglayer.dev/transports/gcplogging/v2
+go get go.loglayer.dev/transports/gcplogging/v3
go get cloud.google.com/go/logging
```
@@ -67,7 +67,7 @@ import (
"cloud.google.com/go/logging"
"go.loglayer.dev/v3"
- "go.loglayer.dev/transports/gcplogging/v2"
+ "go.loglayer.dev/transports/gcplogging/v3"
)
ctx := context.Background()
@@ -194,11 +194,13 @@ results in a `LogEntry.jsonPayload` of:
{
"message": "served",
"requestId": "abc",
- "durationMs": 42
+ "metadata": {
+ "durationMs": 42
+ }
}
```
-Map metadata merges at the payload root. Non-map metadata (structs, scalars) nest under the `metadata` key. Set [`Config.MetadataFieldName` on the core](/configuration#metadatafieldname) to nest map metadata under a fixed key uniformly.
+Metadata nests under [`MetadataFieldName` on the core](/configuration#metadatafieldname) (default `"metadata"`), for maps and non-maps alike. Set `Config.FlattenMetadata: true` to restore the v2 shape, where map metadata merges at the payload root and non-map values nest under `metadata`.
## Level Mapping
diff --git a/docs/src/transports/http.md b/docs/src/transports/http.md
index 739f793..3bc8d65 100644
--- a/docs/src/transports/http.md
+++ b/docs/src/transports/http.md
@@ -9,12 +9,8 @@ description: Generic batched HTTP POST transport with a pluggable encoder.
The `http` transport ships log entries to an HTTP endpoint as JSON in async batches. Use it directly to talk to any log-ingestion API, or as the foundation for a service-specific wrapper (the [Datadog transport](/transports/datadog) is built on it).
-::: info Interim state: this transport is still on v2
-This transport keeps its `v2` path and its pre-v3 metadata placement for this release; the placement described below is what the current `v2` transport produces. The v3 core resolves an empty `MetadataFieldName` to `"metadata"`, so pairing this transport with the v3 core passes a non-empty schema key and changes the placement. The transport's own v3 bump ships in a follow-up release.
-:::
-
```sh
-go get go.loglayer.dev/transports/http/v2
+go get go.loglayer.dev/transports/http/v3
```
The directory is `transports/http`; the package name is `httptransport` to avoid colliding with `net/http`.
@@ -26,7 +22,7 @@ import (
"os"
"go.loglayer.dev/v3"
- httptr "go.loglayer.dev/transports/http/v2"
+ httptr "go.loglayer.dev/transports/http/v3"
)
tr := httptr.New(httptr.Config{
@@ -126,7 +122,7 @@ The default `JSONArrayEncoder` produces:
]
```
-Persistent fields (`WithFields`) and metadata (`WithMetadata`) follow the [core placement rules](/configuration#fieldskey): when `FieldsKey` is empty, fields merge at the root; when `MetadataFieldName` is empty, map metadata merges at the root and non-map metadata nests under `metadata`. Set either knob on `loglayer.Config` to nest under a configured key instead.
+Persistent fields (`WithFields`) and metadata (`WithMetadata`) follow the [core placement rules](/configuration#fieldskey): when `FieldsKey` is empty, fields merge at the root; metadata nests under `MetadataFieldName` (default `"metadata"`). Set either knob on `loglayer.Config` to nest under a configured key instead.
### `Client`
diff --git a/docs/src/transports/logrus.md b/docs/src/transports/logrus.md
index 6c24930..9cfc63e 100644
--- a/docs/src/transports/logrus.md
+++ b/docs/src/transports/logrus.md
@@ -7,10 +7,10 @@ description: Wrap a github.com/sirupsen/logrus logger with LogLayer.
-Wraps an existing `*logrus.Logger`. Map metadata flattens via `Entry.WithFields`; struct metadata lands under a configurable key.
+Wraps an existing `*logrus.Logger`. Metadata nests under a single configurable key.
```sh
-go get go.loglayer.dev/transports/logrus/v2
+go get go.loglayer.dev/transports/logrus/v3
go get github.com/sirupsen/logrus
```
@@ -23,7 +23,7 @@ import (
"github.com/sirupsen/logrus"
"go.loglayer.dev/v3"
- lllogrus "go.loglayer.dev/transports/logrus/v2"
+ lllogrus "go.loglayer.dev/transports/logrus/v3"
)
l := logrus.New()
@@ -66,34 +66,19 @@ The core then decides whether `os.Exit(1)` is called after dispatch. See [Fatal
-### Map metadata → individual fields
+### Metadata nests under the metadata key
```go
log.WithMetadata(loglayer.Metadata{"requestId": "xyz", "n": 42}).Info("served")
-// {"level":"info","msg":"served","n":42,"requestId":"xyz","time":"..."}
-```
-
-Each map entry becomes a key in `logrus.Fields` passed to `WithFields`.
-
-### Struct metadata nests under the metadata key
-
-```go
-type User struct {
- ID int `json:"id"`
- Name string `json:"name"`
-}
+// {"level":"info","msg":"served","metadata":{"requestId":"xyz","n":42},"time":"..."}
log.WithMetadata(User{ID: 7, Name: "Alice"}).Info("user")
// {"level":"info","metadata":{"id":7,"name":"Alice"},"msg":"user",...}
```
-To use a different key per call, wrap in a map:
-
-```go
-log.WithMetadata(loglayer.Metadata{"user": User{ID: 7, Name: "Alice"}}).Info("user")
-```
+With `Config.FlattenMetadata: true`, map metadata instead becomes keys in a `logrus.Fields` bag passed to `WithFields`.
-Or globally via the core's `MetadataFieldName` (which also nests map metadata under the same key):
+Change the nesting key on the core's config:
```go
loglayer.New(loglayer.Config{
diff --git a/docs/src/transports/lumberjack.md b/docs/src/transports/lumberjack.md
index ee6e11b..9e801cd 100644
--- a/docs/src/transports/lumberjack.md
+++ b/docs/src/transports/lumberjack.md
@@ -10,7 +10,7 @@ description: "One JSON object per log entry written to a rotating file. Backed b
The `lumberjack` transport writes one JSON object per log entry to a rotating file on disk. Rotation is handled by [lumberjack.v2](https://github.com/natefinch/lumberjack): size-triggered rollover, configurable backup retention, age-based cleanup, and optional gzip compression. The on-disk format matches [`transports/structured`](/transports/structured) exactly.
```sh
-go get go.loglayer.dev/transports/lumberjack/v2
+go get go.loglayer.dev/transports/lumberjack/v3
```
## Basic Usage
@@ -18,7 +18,7 @@ go get go.loglayer.dev/transports/lumberjack/v2
```go
import (
"go.loglayer.dev/v3"
- "go.loglayer.dev/transports/lumberjack/v2"
+ "go.loglayer.dev/transports/lumberjack/v3"
)
log := loglayer.New(loglayer.Config{
@@ -162,7 +162,7 @@ If you keep a reference to the transport, get it directly. The upstream library
import (
lj "gopkg.in/natefinch/lumberjack.v2"
- "go.loglayer.dev/transports/lumberjack/v2"
+ "go.loglayer.dev/transports/lumberjack/v3"
)
tr := lumberjack.New(lumberjack.Config{
@@ -181,7 +181,7 @@ import (
"go.loglayer.dev/v3"
"go.loglayer.dev/v3/transport"
- "go.loglayer.dev/transports/lumberjack/v2"
+ "go.loglayer.dev/transports/lumberjack/v3"
)
log := loglayer.New(loglayer.Config{
@@ -207,8 +207,8 @@ Render colorized output to the terminal during interactive runs (via the [Pretty
```go
import (
"go.loglayer.dev/v3"
- "go.loglayer.dev/transports/lumberjack/v2"
- "go.loglayer.dev/transports/pretty/v2"
+ "go.loglayer.dev/transports/lumberjack/v3"
+ "go.loglayer.dev/transports/pretty/v3"
)
log := loglayer.New(loglayer.Config{
@@ -236,7 +236,7 @@ A common ops pattern: ship everything to `info.log` and ship errors-only to a sm
import (
"go.loglayer.dev/v3"
"go.loglayer.dev/v3/transport"
- "go.loglayer.dev/transports/lumberjack/v2"
+ "go.loglayer.dev/transports/lumberjack/v3"
)
infoLog := lumberjack.New(lumberjack.Config{
@@ -269,7 +269,7 @@ lumberjack rotates on size, not on the clock. For a calendar-aligned roll-over (
```go
import (
"time"
- "go.loglayer.dev/transports/lumberjack/v2"
+ "go.loglayer.dev/transports/lumberjack/v3"
)
tr := lumberjack.New(lumberjack.Config{
diff --git a/docs/src/transports/multiple-transports.md b/docs/src/transports/multiple-transports.md
index 80e350c..6a4c256 100644
--- a/docs/src/transports/multiple-transports.md
+++ b/docs/src/transports/multiple-transports.md
@@ -72,7 +72,7 @@ import (
"go.loglayer.dev/v3"
"go.loglayer.dev/v3/transport"
- "go.loglayer.dev/transports/structured/v2"
+ "go.loglayer.dev/transports/structured/v3"
)
logFile, err := os.OpenFile("app.log", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0o644)
@@ -123,9 +123,9 @@ import (
"go.loglayer.dev/v3"
"go.loglayer.dev/v3/transport"
- "go.loglayer.dev/transports/datadog/v2"
- "go.loglayer.dev/transports/pretty/v2"
- "go.loglayer.dev/transports/structured/v2"
+ "go.loglayer.dev/transports/datadog/v3"
+ "go.loglayer.dev/transports/pretty/v3"
+ "go.loglayer.dev/transports/structured/v3"
)
logFile, err := os.OpenFile("app.log", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0o644)
diff --git a/docs/src/transports/newrelic.md b/docs/src/transports/newrelic.md
index 63dd0f4..acde1f6 100644
--- a/docs/src/transports/newrelic.md
+++ b/docs/src/transports/newrelic.md
@@ -10,7 +10,7 @@ description: Ship logs to the New Relic Log Ingest API.
Sends log entries to the [New Relic Log Ingest API](https://docs.newrelic.com/docs/logs/log-api/introduction-log-api/). Built on the [HTTP transport](/transports/http) with a New Relic-specific encoder (timestamp, level, log, attributes), site-aware URL, and `Api-Key` header. Includes attribute validation enforced at encode time (max 255 attributes, 255-char names, 4,094-char values). Log format matches the [TypeScript transport](https://loglayer.dev/transports/new-relic).
```sh
-go get go.loglayer.dev/transports/newrelic
+go get go.loglayer.dev/transports/newrelic/v2
```
## Getting a License Key and Site
@@ -43,7 +43,7 @@ import (
"os"
"go.loglayer.dev/v3"
- "go.loglayer.dev/transports/newrelic"
+ "go.loglayer.dev/transports/newrelic/v2"
)
tr := newrelic.New(newrelic.Config{
diff --git a/docs/src/transports/otellog.md b/docs/src/transports/otellog.md
index 7fd7983..6494ef2 100644
--- a/docs/src/transports/otellog.md
+++ b/docs/src/transports/otellog.md
@@ -9,14 +9,14 @@ description: Emit LogLayer entries to an OpenTelemetry log.Logger.
Emits each entry as an OpenTelemetry [`log.Record`](https://pkg.go.dev/go.opentelemetry.io/otel/log#Record) on a `log.Logger`. Use this when your service is wired against the OpenTelemetry SDK and you want LogLayer entries to flow through the same pipeline (OTLP exporter, Collector, observability backend) as your traces and metrics.
-The package name is `otellog` to avoid colliding with `go.opentelemetry.io/otel`. Import path: `go.loglayer.dev/transports/otellog/v2`.
+The package name is `otellog` to avoid colliding with `go.opentelemetry.io/otel`. Import path: `go.loglayer.dev/transports/otellog/v3`.
```sh
-go get go.loglayer.dev/transports/otellog/v2
+go get go.loglayer.dev/transports/otellog/v3
```
::: info Separate module
-`transports/otellog` ships as its own Go module (`go.loglayer.dev/transports/otellog/v2`) so the OpenTelemetry SDK's transitive Go-version requirement doesn't bind the main `go.loglayer.dev/v2` module. Users who don't import the OTel transport never see its dependency graph.
+`transports/otellog` ships as its own Go module (`go.loglayer.dev/transports/otellog/v3`) so the OpenTelemetry SDK's transitive Go-version requirement doesn't bind the main `go.loglayer.dev/v3` module. Users who don't import the OTel transport never see its dependency graph.
Requires **Go 1.25+** because that's the floor of the upstream `go.opentelemetry.io/otel/sdk/log` packages this transport binds against.
:::
@@ -28,7 +28,7 @@ If your app has already registered an OTel `LoggerProvider` globally (the common
```go
import (
"go.loglayer.dev/v3"
- "go.loglayer.dev/transports/otellog/v2"
+ "go.loglayer.dev/transports/otellog/v3"
)
tr := otellog.New(otellog.Config{Name: "checkout-api"})
@@ -54,7 +54,7 @@ import (
"go.opentelemetry.io/otel/sdk/resource"
semconv "go.opentelemetry.io/otel/semconv/v1.26.0"
- "go.loglayer.dev/transports/otellog/v2"
+ "go.loglayer.dev/transports/otellog/v3"
)
res := resource.NewSchemaless(
@@ -164,30 +164,19 @@ The original LogLayer level name (`"info"`, `"error"`, etc.) is also set as `Sev
-### Map metadata flattens to attributes
+### Metadata nests under the metadata attribute
```go
log.WithMetadata(loglayer.Metadata{"requestId": "abc", "n": 42}).Info("served")
-// log.Record attributes: requestId="abc", n=42
-```
-
-Each map entry becomes a typed `log.KeyValue`: strings → `StringValue`, ints → `Int64Value`, bools → `BoolValue`, and so on. Nested maps and slices recurse into `MapValue` / `SliceValue`.
-
-### Struct metadata nests under the metadata key
-
-```go
-type User struct {
- ID int `json:"id"`
- Name string `json:"name"`
-}
+// log.Record attribute: metadata=Map{requestId="abc", n=42}
log.WithMetadata(User{ID: 7, Name: "Alice"}).Info("user")
// log.Record attribute: metadata=Map{id=7, name="Alice"}
```
-The struct is JSON-roundtripped (so `json:` tags apply) then converted to a nested `MapValue` under the metadata attribute (default `"metadata"`).
+The whole metadata value (map or struct) becomes one typed `log.KeyValue` under the metadata attribute (default `"metadata"`). Maps convert directly; structs are JSON-roundtripped (so `json:` tags apply) then converted to a nested `MapValue`.
-Override the key globally via the core's `MetadataFieldName` (which also nests map metadata under the same key):
+Change the attribute key via the core's `MetadataFieldName`:
```go
loglayer.New(loglayer.Config{
@@ -196,9 +185,11 @@ loglayer.New(loglayer.Config{
})
log.WithMetadata(User{ID: 9}).Info("hi")
-// log.Record attribute: user=Map{id=9, name=""}
+// log.Record attribute: user=Map{id=9}
```
+With `Config.FlattenMetadata: true`, each map entry instead becomes its own attribute: strings → `StringValue`, ints → `Int64Value`, bools → `BoolValue`, and so on. Nested maps and slices recurse into `MapValue` / `SliceValue`.
+
## context.Context Pass-through
The `context.Context` attached via `WithContext` is forwarded to `log.Logger.Emit`. OTel SDK processors (and the `BatchProcessor` in particular) read the active span from the context to populate the record's `TraceID` / `SpanID`, giving you log/trace correlation automatically:
diff --git a/docs/src/transports/phuslu.md b/docs/src/transports/phuslu.md
index d6e5802..10c8bb9 100644
--- a/docs/src/transports/phuslu.md
+++ b/docs/src/transports/phuslu.md
@@ -7,10 +7,10 @@ description: Wrap a github.com/phuslu/log logger with LogLayer.
-Wraps an existing `*phuslu/log.Logger`. Map metadata flattens to fields via `Entry.Any(k, v)`; struct metadata lands under a configurable key.
+Wraps an existing `*phuslu/log.Logger`. Metadata nests under a single configurable key.
```sh
-go get go.loglayer.dev/transports/phuslu/v2
+go get go.loglayer.dev/transports/phuslu/v3
go get github.com/phuslu/log
```
@@ -23,7 +23,7 @@ import (
plog "github.com/phuslu/log"
"go.loglayer.dev/v3"
- llphuslu "go.loglayer.dev/transports/phuslu/v2"
+ llphuslu "go.loglayer.dev/transports/phuslu/v3"
)
p := &plog.Logger{
@@ -66,32 +66,17 @@ For non-fatal levels, the wrapper dispatches via `Logger.WithLevel(level).Msg(..
-### Map metadata → individual fields
+### Metadata nests under the metadata key
```go
log.WithMetadata(loglayer.Metadata{"requestId": "xyz", "n": 42}).Info("served")
-// {"time":"...","level":"info","requestId":"xyz","n":42,"message":"served"}
-```
-
-Each map entry becomes an `Entry.Any(k, v)` call.
-
-### Struct metadata nests under the metadata key
-
-```go
-type User struct {
- ID int `json:"id"`
- Name string `json:"name"`
-}
+// {"time":"...","level":"info","message":"served","metadata":{"requestId":"xyz","n":42}}
log.WithMetadata(User{ID: 7, Name: "Alice"}).Info("user")
// {"time":"...","level":"info","metadata":{"id":7,"name":"Alice"},"message":"user"}
```
-To use a different key per call, wrap in a map:
-
-```go
-log.WithMetadata(loglayer.Metadata{"user": User{ID: 7, Name: "Alice"}}).Info("user")
-```
+The nested value is written with `Entry.Any(k, v)`. With `Config.FlattenMetadata: true`, each map entry instead becomes its own `Entry.Any(k, v)` call at the root.
Or globally via the core's `MetadataFieldName` (which also nests map metadata under the same key):
diff --git a/docs/src/transports/pretty.md b/docs/src/transports/pretty.md
index c9400ed..9e27536 100644
--- a/docs/src/transports/pretty.md
+++ b/docs/src/transports/pretty.md
@@ -12,7 +12,7 @@ The `pretty` transport renders log entries with ANSI color, theme support, and t
**This is the recommended transport for local development and any human-readable terminal output.** For production logging, switch to [structured](/transports/structured), [zerolog](/transports/zerolog), or [zap](/transports/zap).
```sh
-go get go.loglayer.dev/transports/pretty/v2
+go get go.loglayer.dev/transports/pretty/v3
```
This transport pulls in `github.com/fatih/color` for ANSI handling.
@@ -22,12 +22,11 @@ This transport pulls in `github.com/fatih/color` for ANSI handling.
```go
import (
"go.loglayer.dev/v3"
- "go.loglayer.dev/transports/pretty/v2"
+ "go.loglayer.dev/transports/pretty/v3"
)
log := loglayer.New(loglayer.Config{
- Transport: pretty.New(pretty.Config{}),
- MetadataFieldName: "metadata",
+ Transport: pretty.New(pretty.Config{}),
})
log.WithMetadata(loglayer.Metadata{"user": "alice", "n": 42}).Info("served")
@@ -114,7 +113,7 @@ A `*pretty.Theme` is just a struct of `Style` functions (`func(string) string`).
import (
"github.com/fatih/color"
- "go.loglayer.dev/transports/pretty/v2"
+ "go.loglayer.dev/transports/pretty/v3"
)
// Style is func(string) string; fatih/color's SprintFunc returns
@@ -214,17 +213,12 @@ Doesn't affect expanded mode; it always shows the full tree.
## Metadata Handling
-::: info Interim state: this transport is still on v2
-This transport keeps its `v2` path and its pre-v3 metadata placement for this release; the shape below is what the current `v2` transport produces. The v3 core resolves an empty `MetadataFieldName` to `"metadata"`, so pairing this transport with the v3 core passes a non-empty schema key and changes the placement. The transport's own v3 bump ships in a follow-up release.
-:::
-
-When [`MetadataFieldName`](/configuration#metadatafieldname) is empty (the default), the breakdown by metadata shape is:
-
-- **Maps** merge at the root, alongside fields and error fields.
-- **Structs** are JSON-roundtripped into a map (so `json:"foo"` tags determine the rendered key) and merged at the root.
-- **Scalars / unknown types** fall back to `_metadata` as the key.
+Every metadata value (map, struct, scalar) nests under [`MetadataFieldName`](/configuration#metadatafieldname), which the v3 core defaults to `"metadata"`. Set a different key on `loglayer.Config`, or set `FlattenMetadata: true` to restore the v2 shape: map and struct metadata roundtrip to root fields, and non-roundtrippable values fall back to `_metadata`.
-When `MetadataFieldName` is set on `loglayer.Config`, every metadata value (map, struct, scalar) nests under the configured key instead.
+```go
+log.WithMetadata(loglayer.Metadata{"user": "alice", "n": 42}).Info("served")
+// 12:34:56.789 ▶ INFO served metadata={n=42 user=alice}
+```
```go
type User struct {
@@ -233,7 +227,7 @@ type User struct {
}
log.WithMetadata(User{ID: 7, Name: "Alice"}).Info("user")
-// 12:34:56.789 ▶ INFO user id=7 name=Alice
+// 12:34:56.789 ▶ INFO user metadata={id=7 name=Alice}
```
## Fatal Behavior
diff --git a/docs/src/transports/sentry.md b/docs/src/transports/sentry.md
index aade5c4..d3bdb6f 100644
--- a/docs/src/transports/sentry.md
+++ b/docs/src/transports/sentry.md
@@ -9,10 +9,10 @@ description: Forward LogLayer entries to a sentry.Logger.
Forwards each entry to a caller-supplied [`sentry.Logger`](https://pkg.go.dev/github.com/getsentry/sentry-go#Logger), Sentry's structured-logs API. Use this when you've already wired Sentry into your service for error reporting and want LogLayer entries to flow through the same pipeline.
-The package directory is `transports/sentry`; the package name is `sentrytransport` to avoid colliding with the imported `sentry` identifier. Import path: `go.loglayer.dev/transports/sentry/v2`.
+The package directory is `transports/sentry`; the package name is `sentrytransport` to avoid colliding with the imported `sentry` identifier. Import path: `go.loglayer.dev/transports/sentry/v3`.
```sh
-go get go.loglayer.dev/transports/sentry/v2
+go get go.loglayer.dev/transports/sentry/v3
go get github.com/getsentry/sentry-go
```
@@ -40,7 +40,7 @@ import (
"github.com/getsentry/sentry-go"
"go.loglayer.dev/v3"
- sentrytransport "go.loglayer.dev/transports/sentry/v2"
+ sentrytransport "go.loglayer.dev/transports/sentry/v3"
)
// Initialize Sentry as you normally would.
@@ -81,26 +81,18 @@ The transport routes `LogLevelFatal` and `LogLevelPanic` through `sentry.Logger.
-### Map metadata flattens to attributes
+### Metadata nests under the metadata attribute
```go
-log.WithMetadata(loglayer.Metadata{"requestId": "abc", "n": 42}).Info("served")
-// Sentry attributes: requestId="abc", n=42
-```
-
-Each map entry becomes a typed Sentry attribute via the matching `LogEntry` setter (`String`, `Int`, `Int64`, `Float64`, `Bool`, plus the slice variants). Values that don't match a typed setter (nested maps, structs, mixed-type slices) are JSON-encoded into a single `String` attribute, so the structure is preserved in Sentry's UI.
-
-### Struct metadata nests under the metadata key
-
-Non-map metadata (structs, scalars, slices) is JSON-encoded and stored under a single configurable attribute key. The default key is `"metadata"`:
-
-```go
-import sentrytransport "go.loglayer.dev/transports/sentry/v2"
+import sentrytransport "go.loglayer.dev/transports/sentry/v3"
tr := sentrytransport.New(sentrytransport.Config{
Logger: sentry.NewLogger(ctx),
})
+log.WithMetadata(loglayer.Metadata{"requestId": "abc", "n": 42}).Info("served")
+// Sentry attribute: metadata=`{"n":42,"requestId":"abc"}`
+
type User struct {
ID int `json:"id"`
Name string `json:"name"`
@@ -110,7 +102,7 @@ log.WithMetadata(User{ID: 7, Name: "Alice"}).Info("user")
// Sentry attribute: metadata=`{"id":7,"name":"Alice"}`
```
-Override the key globally via the core's `MetadataFieldName` (which also nests map metadata under the same key):
+The whole metadata value nests under one attribute (default `"metadata"`). Change the key via the core's `MetadataFieldName`:
```go
loglayer.New(loglayer.Config{
@@ -122,7 +114,7 @@ log.WithMetadata(User{ID: 7, Name: "Alice"}).Info("user")
// Sentry attribute: payload=`{"id":7,"name":"Alice"}`
```
-The Sentry `LogEntry` API only accepts a fixed set of typed attribute shapes (no `Map` / `Any` / `Object` setter), so non-scalar values land as a JSON-encoded string. `json:` tags on struct fields apply normally.
+The Sentry `LogEntry` API only accepts a fixed set of typed attribute shapes (no `Map` / `Any` / `Object` setter), so the value lands as a JSON-encoded string. `json:` tags on struct fields apply normally. With `Config.FlattenMetadata: true`, each map entry instead becomes a typed Sentry attribute via the matching `LogEntry` setter (`String`, `Int`, `Int64`, `Float64`, `Bool`, plus the slice variants). Values that don't match a typed setter (nested maps, structs, mixed-type slices) are JSON-encoded into a single `String` attribute, so the structure is preserved in Sentry's UI.
## Fields
diff --git a/docs/src/transports/slog.md b/docs/src/transports/slog.md
index f35b408..5fa6876 100644
--- a/docs/src/transports/slog.md
+++ b/docs/src/transports/slog.md
@@ -7,10 +7,10 @@ description: Wrap a *slog.Logger with LogLayer.
-Wraps a stdlib `*log/slog.Logger`. Map metadata flattens to `slog.Attr`s; struct metadata lands under a configurable key. Per-call `context.Context` attached via `WithContext` is passed through to `slog.Logger.LogAttrs` so handlers downstream (OpenTelemetry, structured shippers) can extract trace context.
+Wraps a stdlib `*log/slog.Logger`. Metadata nests under a single key via one `slog.Attr`. Per-call `context.Context` attached via `WithContext` is passed through to `slog.Logger.LogAttrs` so handlers downstream (OpenTelemetry, structured shippers) can extract trace context.
```sh
-go get go.loglayer.dev/transports/slog/v2
+go get go.loglayer.dev/transports/slog/v3
```
## Basic Usage
@@ -21,7 +21,7 @@ import (
"os"
"go.loglayer.dev/v3"
- llslog "go.loglayer.dev/transports/slog/v2"
+ llslog "go.loglayer.dev/transports/slog/v3"
)
handler := slog.NewJSONHandler(os.Stderr, nil)
@@ -56,28 +56,17 @@ slog has no fatal level. This transport maps `LogLevelFatal` to `slog.LevelError
-### Map metadata → individual `slog.Attr`s
+### Metadata nests under the metadata key
```go
log.WithMetadata(loglayer.Metadata{"requestId": "abc", "n": 42}).Info("served")
-// {"time":"...","level":"INFO","msg":"served","requestId":"abc","n":42}
-```
-
-Each map entry becomes a `slog.Any(k, v)` attribute, so slog renders it via the configured handler (JSON, text, or anything custom).
-
-### Struct metadata nests under the metadata key
-
-```go
-type User struct {
- ID int `json:"id"`
- Name string `json:"name"`
-}
+// {"time":"...","level":"INFO","msg":"served","metadata":{"requestId":"abc","n":42}}
log.WithMetadata(User{ID: 7, Name: "Alice"}).Info("user")
// {"time":"...","level":"INFO","msg":"user","metadata":{"id":7,"name":"Alice"}}
```
-The JSON handler honors `json:` tags; other handlers may render fields differently.
+The nested value becomes a single `slog.Any(key, value)` attribute, so slog renders it via the configured handler (JSON, text, or anything custom). The JSON handler honors `json:` tags; other handlers may render fields differently.
## context.Context Pass-through
diff --git a/docs/src/transports/structured.md b/docs/src/transports/structured.md
index 2990a31..205362f 100644
--- a/docs/src/transports/structured.md
+++ b/docs/src/transports/structured.md
@@ -10,7 +10,7 @@ description: One JSON object per log entry. The default for production logging.
The `structured` transport always writes one JSON object per log entry. By default each entry has `level`, `time`, and `msg` fields, with fields merged at the root.
```sh
-go get go.loglayer.dev/transports/structured/v2
+go get go.loglayer.dev/transports/structured/v3
```
## Basic Usage
@@ -18,13 +18,12 @@ go get go.loglayer.dev/transports/structured/v2
```go
import (
"go.loglayer.dev/v3"
- "go.loglayer.dev/transports/structured/v2"
+ "go.loglayer.dev/transports/structured/v3"
)
log := loglayer.New(loglayer.Config{
- Transport: structured.New(structured.Config{}),
- FieldsKey: "context",
- MetadataFieldName: "metadata",
+ Transport: structured.New(structured.Config{}),
+ FieldsKey: "context",
})
log.Info("hello")
@@ -85,7 +84,7 @@ The `Writer` field accepts any `io.Writer`. See [Writers](/transports/writers) f
## Struct Metadata
-When you pass a struct to `WithMetadata`, the transport JSON-marshals + unmarshals it into a `map[string]any` and merges the fields at the root:
+Metadata (map or struct) nests under [`MetadataFieldName`](/configuration#metadatafieldname), which the v3 core defaults to `"metadata"`:
```go
type User struct {
@@ -94,18 +93,14 @@ type User struct {
}
log.WithMetadata(User{ID: 7, Email: "alice@example.com"}).Info("user")
-// {"level":"info","time":"...","msg":"user","id":7,"email":"alice@example.com"}
-```
-
-The roundtrip happens once, in the transport. The core LogLayer does not touch the value, see [Metadata](/logging-api/metadata).
-
-If you want struct payloads under a single key instead of merged at the root, wrap them in a map yourself:
+// {"level":"info","time":"...","msg":"user","metadata":{"id":7,"email":"alice@example.com"}}
-```go
-log.WithMetadata(loglayer.Metadata{"user": User{ID: 7, Email: "..."}}).Info("user")
-// {"level":"info","time":"...","msg":"user","user":{"id":7,"email":"..."}}
+log.WithMetadata(loglayer.Metadata{"total": 3}).Info("cart")
+// {"level":"info","time":"...","msg":"cart","metadata":{"total":3}}
```
+A struct is JSON-marshaled + unmarshaled into a `map[string]any` before encoding, so `json:` tags apply. The core LogLayer does not touch the value, see [Metadata](/logging-api/metadata). With `Config.FlattenMetadata: true`, the v2 shape returns: struct fields and map entries merge at the root instead of nesting.
+
## Errors
Errors are serialized via the logger's `ErrorSerializer` (default `{"message": err.Error()}`) and placed under `ErrorFieldName` (default `err`):
diff --git a/docs/src/transports/testing.md b/docs/src/transports/testing.md
index 0aaf617..57f6ba7 100644
--- a/docs/src/transports/testing.md
+++ b/docs/src/transports/testing.md
@@ -12,11 +12,11 @@ The `transports/testing` package is the transport you want in test code: it capt
For a usage walkthrough see the [Mocking](/logging-api/mocking) page in the logging API section. This page covers the package surface.
```sh
-go get go.loglayer.dev/transports/testing/v2
+go get go.loglayer.dev/transports/testing/v3
```
```go
-import lltest "go.loglayer.dev/transports/testing/v2"
+import lltest "go.loglayer.dev/transports/testing/v3"
```
(The package name is `testing`, which collides with the standard `testing` package, most users alias the import as `lltest`.)
diff --git a/docs/src/transports/writers.md b/docs/src/transports/writers.md
index 8039793..f04a19c 100644
--- a/docs/src/transports/writers.md
+++ b/docs/src/transports/writers.md
@@ -32,7 +32,7 @@ log := loglayer.New(loglayer.Config{
import (
"os"
"go.loglayer.dev/v3"
- "go.loglayer.dev/transports/structured/v2"
+ "go.loglayer.dev/transports/structured/v3"
)
f, err := os.OpenFile("/var/log/app.log",
@@ -57,7 +57,7 @@ The dedicated [lumberjack](/transports/lumberjack) transport already does this f
import (
"gopkg.in/natefinch/lumberjack.v2"
"go.loglayer.dev/v3"
- "go.loglayer.dev/transports/pretty/v2"
+ "go.loglayer.dev/transports/pretty/v3"
)
rotator := &lumberjack.Logger{
@@ -104,7 +104,7 @@ A `*bytes.Buffer` works but isn't safe for concurrent writes. For real test asse
import (
"bytes"
"go.loglayer.dev/v3"
- "go.loglayer.dev/transports/structured/v2"
+ "go.loglayer.dev/transports/structured/v3"
)
var buf bytes.Buffer
@@ -125,7 +125,7 @@ Anything that satisfies `io.Writer` works, including `net.Conn`. The example bel
import (
"net"
"go.loglayer.dev/v3"
- "go.loglayer.dev/transports/structured/v2"
+ "go.loglayer.dev/transports/structured/v3"
)
conn, err := net.Dial("tcp", "logsink.local:514")
@@ -202,7 +202,7 @@ Wrapper transports ([Zerolog](/transports/zerolog), [Zap](/transports/zap), [log
import (
"github.com/rs/zerolog"
"go.loglayer.dev/v3"
- llzero "go.loglayer.dev/transports/zerolog/v2"
+ llzero "go.loglayer.dev/transports/zerolog/v3"
)
// zerolog: configure on the *zerolog.Logger you build
diff --git a/docs/src/transports/zap.md b/docs/src/transports/zap.md
index 362fd8f..0044da6 100644
--- a/docs/src/transports/zap.md
+++ b/docs/src/transports/zap.md
@@ -7,10 +7,10 @@ description: Wrap a go.uber.org/zap logger with LogLayer.
-Wraps a `*zap.Logger`. Map metadata becomes individual zap fields; struct metadata lands under a configurable key. Fatal-level entries are written via a custom `CheckWriteHook` so the process is **not** terminated, regardless of zap's defaults.
+Wraps a `*zap.Logger`. Metadata nests under a configurable key (`"metadata"` by default); with `Config.FlattenMetadata: true`, map metadata becomes individual zap fields. Fatal-level entries are written via a custom `CheckWriteHook` so the process is **not** terminated, regardless of zap's defaults.
```sh
-go get go.loglayer.dev/transports/zap/v2
+go get go.loglayer.dev/transports/zap/v3
go get go.uber.org/zap
```
@@ -21,7 +21,7 @@ import (
"go.uber.org/zap"
"go.loglayer.dev/v3"
- llzap "go.loglayer.dev/transports/zap/v2"
+ llzap "go.loglayer.dev/transports/zap/v3"
)
z, _ := zap.NewProduction()
@@ -51,36 +51,19 @@ type Config struct {
-### Map metadata → individual fields
+### Metadata nests under the metadata key
```go
log.WithMetadata(loglayer.Metadata{"requestId": "abc", "n": 42}).Info("served")
-// {"level":"info","ts":...,"caller":"...","msg":"served","requestId":"abc","n":42}
-```
-
-Each map entry becomes a `zap.Any(k, v)` call, so zap renders it however its encoder is configured.
-
-### Struct metadata nests under the metadata key
-
-```go
-type User struct {
- ID int `json:"id"`
- Name string `json:"name"`
-}
+// {"level":"info","ts":...,"caller":"...","msg":"served","metadata":{"requestId":"abc","n":42}}
log.WithMetadata(User{ID: 7, Name: "Alice"}).Info("user")
// {"level":"info","ts":...,"caller":"...","msg":"user","metadata":{"id":7,"name":"Alice"}}
```
-zap reflects into the struct via `zap.Any`, which is faster than a JSON roundtrip.
-
-To use a different key per call, wrap in a map:
-
-```go
-log.WithMetadata(loglayer.Metadata{"user": User{ID: 7, Name: "Alice"}}).Info("user")
-```
+zap reflects into the map or struct via `zap.Any`, which is faster than a JSON roundtrip.
-Or globally via the core's `MetadataFieldName` (which also nests map metadata under the same key):
+Change the nesting key on the core's config:
```go
loglayer.New(loglayer.Config{
diff --git a/docs/src/transports/zerolog.md b/docs/src/transports/zerolog.md
index 5da90ae..ebf2140 100644
--- a/docs/src/transports/zerolog.md
+++ b/docs/src/transports/zerolog.md
@@ -7,10 +7,10 @@ description: Wrap a github.com/rs/zerolog logger with LogLayer.
-Wraps an existing `*zerolog.Logger`. Map metadata merges as fields; struct metadata lands under a configurable key. Fatal-level entries are written via `WithLevel` so the process is **not** terminated, regardless of zerolog's defaults.
+Wraps an existing `*zerolog.Logger`. Metadata nests under a configurable key (`"metadata"` by default); with `Config.FlattenMetadata: true`, map metadata merges as fields. Fatal-level entries are written via `WithLevel` so the process is **not** terminated, regardless of zerolog's defaults.
```sh
-go get go.loglayer.dev/transports/zerolog/v2
+go get go.loglayer.dev/transports/zerolog/v3
go get github.com/rs/zerolog
```
@@ -22,7 +22,7 @@ import (
"os"
"go.loglayer.dev/v3"
- llzero "go.loglayer.dev/transports/zerolog/v2"
+ llzero "go.loglayer.dev/transports/zerolog/v3"
)
z := zlog.New(os.Stderr).With().Timestamp().Logger()
@@ -52,34 +52,19 @@ type Config struct {
-### Map metadata → fields at root
+### Metadata nests under the metadata key
```go
log.WithMetadata(loglayer.Metadata{"requestId": "abc", "n": 42}).Info("served")
-// {"level":"info","n":42,"requestId":"abc","time":"...","message":"served"}
-```
-
-### Struct metadata nests under the metadata key
-
-```go
-type User struct {
- ID int `json:"id"`
- Name string `json:"name"`
-}
+// {"level":"info","metadata":{"requestId":"abc","n":42},"time":"...","message":"served"}
log.WithMetadata(User{ID: 7, Name: "Alice"}).Info("user")
// {"level":"info","metadata":{"id":7,"name":"Alice"},"time":"...","message":"user"}
```
-Zerolog's `Interface` field handler reflects directly into the struct, so the value is encoded once at write time without an extra JSON roundtrip.
-
-Use a different key per call by wrapping in a map:
-
-```go
-log.WithMetadata(loglayer.Metadata{"user": User{ID: 7, Name: "Alice"}}).Info("user")
-```
+Zerolog's `Interface` field handler reflects directly into the map or struct, so the value is encoded once at write time without an extra JSON roundtrip.
-Or globally via the core's `MetadataFieldName` (which also nests map metadata under the same key):
+Change the nesting key on the core's config:
```go
loglayer.New(loglayer.Config{
diff --git a/docs/src/whats-new.md b/docs/src/whats-new.md
index 287fe82..287f60e 100644
--- a/docs/src/whats-new.md
+++ b/docs/src/whats-new.md
@@ -13,7 +13,16 @@ description: Latest features and improvements in LogLayer for Go.
**Metadata now nests by default.** The core is now `go.loglayer.dev/v3` (with `/v3/transport` and `/v3/utils/*` sub-packages), and `Config.MetadataFieldName` resolves to `"metadata"` when empty, so map and struct metadata render uniformly under that key across every transport. Restore the v2 root-flattening shape with `Config.FlattenMetadata: true`. See [Migrating to v3](/migrating#migrating-to-v3).
-`transports/structured` stays on `go.loglayer.dev/transports/structured/v2` for this release; its own v3 bump ships in a follow-up.
+## Aug 22, 2026
+
+`v3 sub-module sweep`:
+
+**Every released transport, plugin, and integration takes its next major module-path bump.** All of them re-export v3 core types (or sibling `http/v3` types) in their public API, so moving onto `go.loglayer.dev/v3` is breaking: 24 modules move `/v2 → /v3` (transports, `plugins/oteltrace`, `plugins/datadogtrace`, `plugins/fmtlog`, `plugins/plugintest`, `plugins/redact`, `plugins/sampling`, `transports/datadog`, `integrations/loghttp`, `integrations/sloghandler`) and 2 move unversioned → `/v2` (`transports/betterstack`, `transports/newrelic`). Update imports and `go get` lines. See [Migrating to v3](/migrating#migrating-to-v3).
+
+`transports/structured`:
+
+- **Top-level sanitization**: the message and top-level keys and string values are run through `utils/sanitize.Message`, stripping ANSI escape sequences, CR/LF, and bidi overrides so a log line can't smuggle terminal control sequences. Authored [`Multiline`](/logging-api/multiline) messages keep their line boundaries.
+- **Empty messages omit `msg`**: `WithFields(...).Info("")` renders no `msg` key instead of `"msg":""`.
## Aug 19, 2026
diff --git a/examples/custom-plugin/go.mod b/examples/custom-plugin/go.mod
index 98a2291..2e14ec1 100644
--- a/examples/custom-plugin/go.mod
+++ b/examples/custom-plugin/go.mod
@@ -3,13 +3,13 @@ module go.loglayer.dev/examples/custom-plugin/v2
go 1.25.0
replace (
- go.loglayer.dev/transports/pretty/v2 => ../../transports/pretty
- go.loglayer.dev/v2 => ../..
+ go.loglayer.dev/transports/pretty/v3 => ../../transports/pretty
+ go.loglayer.dev/v3 => ../..
)
require (
- go.loglayer.dev/transports/pretty/v2 v2.0.0-00010101000000-000000000000
- go.loglayer.dev/v2 v2.1.0
+ go.loglayer.dev/transports/pretty/v3 v3.0.0-00010101000000-000000000000
+ go.loglayer.dev/v3 v3.0.0
)
require (
@@ -17,5 +17,5 @@ require (
github.com/goccy/go-json v0.10.6 // indirect
github.com/mattn/go-colorable v0.1.14 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
- golang.org/x/sys v0.42.0 // indirect
+ golang.org/x/sys v0.43.0 // indirect
)
diff --git a/examples/custom-plugin/go.sum b/examples/custom-plugin/go.sum
index 43acb76..b525da0 100644
--- a/examples/custom-plugin/go.sum
+++ b/examples/custom-plugin/go.sum
@@ -9,5 +9,5 @@ github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo=
-golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
+golang.org/x/sys v0.43.0 h1:Rlag2XtaFTxp19wS8MXlJwTvoh8ArU6ezoyFsMyCTNI=
+golang.org/x/sys v0.43.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
diff --git a/examples/custom-plugin/main.go b/examples/custom-plugin/main.go
index 099efbd..93466cc 100644
--- a/examples/custom-plugin/main.go
+++ b/examples/custom-plugin/main.go
@@ -25,9 +25,9 @@ import (
"strings"
"sync/atomic"
- "go.loglayer.dev/transports/pretty/v2"
- "go.loglayer.dev/v2"
- "go.loglayer.dev/v2/transport"
+ "go.loglayer.dev/transports/pretty/v3"
+ "go.loglayer.dev/v3"
+ "go.loglayer.dev/v3/transport"
)
// hostInfoPlugin tags every entry with the host name and PID, and
diff --git a/examples/datadog-shipping/go.mod b/examples/datadog-shipping/go.mod
index f95500a..efc9007 100644
--- a/examples/datadog-shipping/go.mod
+++ b/examples/datadog-shipping/go.mod
@@ -3,17 +3,17 @@ module go.loglayer.dev/examples/datadog-shipping/v2
go 1.25.0
replace (
- go.loglayer.dev/transports/datadog/v2 => ../../transports/datadog
- go.loglayer.dev/transports/http/v2 => ../../transports/http
- go.loglayer.dev/transports/pretty/v2 => ../../transports/pretty
- go.loglayer.dev/v2 => ../..
+ go.loglayer.dev/transports/datadog/v3 => ../../transports/datadog
+ go.loglayer.dev/transports/http/v3 => ../../transports/http
+ go.loglayer.dev/transports/pretty/v3 => ../../transports/pretty
+ go.loglayer.dev/v3 => ../..
)
require (
- go.loglayer.dev/transports/datadog/v2 v2.0.0-00010101000000-000000000000
- go.loglayer.dev/transports/http/v2 v2.0.1
- go.loglayer.dev/transports/pretty/v2 v2.0.0-00010101000000-000000000000
- go.loglayer.dev/v2 v2.1.0
+ go.loglayer.dev/transports/datadog/v3 v3.0.0-00010101000000-000000000000
+ go.loglayer.dev/transports/http/v3 v3.0.0-00010101000000-000000000000
+ go.loglayer.dev/transports/pretty/v3 v3.0.0-00010101000000-000000000000
+ go.loglayer.dev/v3 v3.0.0
)
require (
@@ -21,5 +21,5 @@ require (
github.com/goccy/go-json v0.10.6 // indirect
github.com/mattn/go-colorable v0.1.14 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
- golang.org/x/sys v0.42.0 // indirect
+ golang.org/x/sys v0.43.0 // indirect
)
diff --git a/examples/datadog-shipping/go.sum b/examples/datadog-shipping/go.sum
index 43acb76..b525da0 100644
--- a/examples/datadog-shipping/go.sum
+++ b/examples/datadog-shipping/go.sum
@@ -9,5 +9,5 @@ github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo=
-golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
+golang.org/x/sys v0.43.0 h1:Rlag2XtaFTxp19wS8MXlJwTvoh8ArU6ezoyFsMyCTNI=
+golang.org/x/sys v0.43.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
diff --git a/examples/datadog-shipping/main.go b/examples/datadog-shipping/main.go
index 3f8271b..69c5f2b 100644
--- a/examples/datadog-shipping/main.go
+++ b/examples/datadog-shipping/main.go
@@ -17,11 +17,11 @@ import (
"os"
"time"
- "go.loglayer.dev/transports/datadog/v2"
- httptr "go.loglayer.dev/transports/http/v2"
- "go.loglayer.dev/transports/pretty/v2"
- "go.loglayer.dev/v2"
- "go.loglayer.dev/v2/transport"
+ "go.loglayer.dev/transports/datadog/v3"
+ httptr "go.loglayer.dev/transports/http/v3"
+ "go.loglayer.dev/transports/pretty/v3"
+ "go.loglayer.dev/v3"
+ "go.loglayer.dev/v3/transport"
)
func main() {
diff --git a/examples/http-server/go.mod b/examples/http-server/go.mod
index 69ac5c2..479fa1c 100644
--- a/examples/http-server/go.mod
+++ b/examples/http-server/go.mod
@@ -3,16 +3,16 @@ module go.loglayer.dev/examples/http-server/v2
go 1.25.0
replace (
- go.loglayer.dev/integrations/loghttp/v2 => ../../integrations/loghttp
- go.loglayer.dev/transports/structured/v2 => ../../transports/structured
- go.loglayer.dev/transports/testing/v2 => ../../transports/testing
- go.loglayer.dev/v2 => ../..
+ go.loglayer.dev/integrations/loghttp/v3 => ../../integrations/loghttp
+ go.loglayer.dev/transports/structured/v3 => ../../transports/structured
+ go.loglayer.dev/transports/testing/v3 => ../../transports/testing
+ go.loglayer.dev/v3 => ../..
)
require (
- go.loglayer.dev/integrations/loghttp/v2 v2.0.0-00010101000000-000000000000
- go.loglayer.dev/transports/structured/v2 v2.0.0-00010101000000-000000000000
- go.loglayer.dev/v2 v2.0.1
+ go.loglayer.dev/integrations/loghttp/v3 v3.0.0-00010101000000-000000000000
+ go.loglayer.dev/transports/structured/v3 v3.0.0-00010101000000-000000000000
+ go.loglayer.dev/v3 v3.0.0
)
require github.com/goccy/go-json v0.10.6 // indirect
diff --git a/examples/http-server/main.go b/examples/http-server/main.go
index bd896b4..a943868 100644
--- a/examples/http-server/main.go
+++ b/examples/http-server/main.go
@@ -13,9 +13,9 @@ package main
import (
"net/http"
- "go.loglayer.dev/integrations/loghttp/v2"
- "go.loglayer.dev/transports/structured/v2"
- "go.loglayer.dev/v2"
+ "go.loglayer.dev/integrations/loghttp/v3"
+ "go.loglayer.dev/transports/structured/v3"
+ "go.loglayer.dev/v3"
)
var serverLog = loglayer.New(loglayer.Config{
diff --git a/examples/multi-transport/go.mod b/examples/multi-transport/go.mod
index 28df32b..ab73a39 100644
--- a/examples/multi-transport/go.mod
+++ b/examples/multi-transport/go.mod
@@ -3,15 +3,15 @@ module go.loglayer.dev/examples/multi-transport/v2
go 1.25.0
replace (
- go.loglayer.dev/transports/pretty/v2 => ../../transports/pretty
- go.loglayer.dev/transports/structured/v2 => ../../transports/structured
- go.loglayer.dev/v2 => ../..
+ go.loglayer.dev/transports/pretty/v3 => ../../transports/pretty
+ go.loglayer.dev/transports/structured/v3 => ../../transports/structured
+ go.loglayer.dev/v3 => ../..
)
require (
- go.loglayer.dev/transports/pretty/v2 v2.0.0-00010101000000-000000000000
- go.loglayer.dev/transports/structured/v2 v2.0.0-00010101000000-000000000000
- go.loglayer.dev/v2 v2.1.0
+ go.loglayer.dev/transports/pretty/v3 v3.0.0-00010101000000-000000000000
+ go.loglayer.dev/transports/structured/v3 v3.0.0-00010101000000-000000000000
+ go.loglayer.dev/v3 v3.0.0
)
require (
@@ -19,5 +19,5 @@ require (
github.com/goccy/go-json v0.10.6 // indirect
github.com/mattn/go-colorable v0.1.14 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
- golang.org/x/sys v0.42.0 // indirect
+ golang.org/x/sys v0.43.0 // indirect
)
diff --git a/examples/multi-transport/go.sum b/examples/multi-transport/go.sum
index 43acb76..b525da0 100644
--- a/examples/multi-transport/go.sum
+++ b/examples/multi-transport/go.sum
@@ -9,5 +9,5 @@ github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo=
-golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
+golang.org/x/sys v0.43.0 h1:Rlag2XtaFTxp19wS8MXlJwTvoh8ArU6ezoyFsMyCTNI=
+golang.org/x/sys v0.43.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
diff --git a/examples/multi-transport/main.go b/examples/multi-transport/main.go
index 415efd1..b3e8049 100644
--- a/examples/multi-transport/main.go
+++ b/examples/multi-transport/main.go
@@ -15,10 +15,10 @@ import (
"errors"
"os"
- "go.loglayer.dev/transports/pretty/v2"
- "go.loglayer.dev/transports/structured/v2"
- "go.loglayer.dev/v2"
- "go.loglayer.dev/v2/transport"
+ "go.loglayer.dev/transports/pretty/v3"
+ "go.loglayer.dev/transports/structured/v3"
+ "go.loglayer.dev/v3"
+ "go.loglayer.dev/v3/transport"
)
func main() {
diff --git a/examples/otel-end-to-end/go.mod b/examples/otel-end-to-end/go.mod
index 6fe69b0..e6ed387 100644
--- a/examples/otel-end-to-end/go.mod
+++ b/examples/otel-end-to-end/go.mod
@@ -3,17 +3,17 @@ module go.loglayer.dev/examples/otel-end-to-end/v2
go 1.25.0
replace (
- go.loglayer.dev/plugins/oteltrace/v2 => ../../plugins/oteltrace
- go.loglayer.dev/plugins/plugintest/v2 => ../../plugins/plugintest
- go.loglayer.dev/transports/otellog/v2 => ../../transports/otellog
- go.loglayer.dev/transports/testing/v2 => ../../transports/testing
- go.loglayer.dev/v2 => ../..
+ go.loglayer.dev/plugins/oteltrace/v3 => ../../plugins/oteltrace
+ go.loglayer.dev/plugins/plugintest/v3 => ../../plugins/plugintest
+ go.loglayer.dev/transports/otellog/v3 => ../../transports/otellog
+ go.loglayer.dev/transports/testing/v3 => ../../transports/testing
+ go.loglayer.dev/v3 => ../..
)
require (
- go.loglayer.dev/plugins/oteltrace/v2 v2.0.0-00010101000000-000000000000
- go.loglayer.dev/transports/otellog/v2 v2.0.0-00010101000000-000000000000
- go.loglayer.dev/v2 v2.0.1
+ go.loglayer.dev/plugins/oteltrace/v3 v3.0.0-00010101000000-000000000000
+ go.loglayer.dev/transports/otellog/v3 v3.0.0-00010101000000-000000000000
+ go.loglayer.dev/v3 v3.0.0
go.opentelemetry.io/otel v1.43.0
go.opentelemetry.io/otel/exporters/stdout/stdoutlog v0.19.0
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.43.0
@@ -31,5 +31,5 @@ require (
go.opentelemetry.io/otel/log v0.19.0 // indirect
go.opentelemetry.io/otel/metric v1.43.0 // indirect
go.opentelemetry.io/otel/trace v1.43.0 // indirect
- golang.org/x/sys v0.42.0 // indirect
+ golang.org/x/sys v0.43.0 // indirect
)
diff --git a/examples/otel-end-to-end/go.sum b/examples/otel-end-to-end/go.sum
index a9a0af6..911176b 100644
--- a/examples/otel-end-to-end/go.sum
+++ b/examples/otel-end-to-end/go.sum
@@ -43,7 +43,7 @@ go.opentelemetry.io/otel/trace v1.43.0 h1:BkNrHpup+4k4w+ZZ86CZoHHEkohws8AY+WTX09
go.opentelemetry.io/otel/trace v1.43.0/go.mod h1:/QJhyVBUUswCphDVxq+8mld+AvhXZLhe+8WVFxiFff0=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
-golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo=
-golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
+golang.org/x/sys v0.43.0 h1:Rlag2XtaFTxp19wS8MXlJwTvoh8ArU6ezoyFsMyCTNI=
+golang.org/x/sys v0.43.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
diff --git a/examples/otel-end-to-end/main.go b/examples/otel-end-to-end/main.go
index 7ea71a9..06628f8 100644
--- a/examples/otel-end-to-end/main.go
+++ b/examples/otel-end-to-end/main.go
@@ -24,9 +24,9 @@ import (
"fmt"
"os"
- "go.loglayer.dev/plugins/oteltrace/v2"
- "go.loglayer.dev/transports/otellog/v2"
- "go.loglayer.dev/v2"
+ "go.loglayer.dev/plugins/oteltrace/v3"
+ "go.loglayer.dev/transports/otellog/v3"
+ "go.loglayer.dev/v3"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/exporters/stdout/stdoutlog"
"go.opentelemetry.io/otel/exporters/stdout/stdouttrace"
diff --git a/examples/pretty-modes/go.mod b/examples/pretty-modes/go.mod
index a899a38..76db27d 100644
--- a/examples/pretty-modes/go.mod
+++ b/examples/pretty-modes/go.mod
@@ -3,13 +3,13 @@ module go.loglayer.dev/examples/pretty-modes/v2
go 1.25.0
replace (
- go.loglayer.dev/transports/pretty/v2 => ../../transports/pretty
- go.loglayer.dev/v2 => ../..
+ go.loglayer.dev/transports/pretty/v3 => ../../transports/pretty
+ go.loglayer.dev/v3 => ../..
)
require (
- go.loglayer.dev/transports/pretty/v2 v2.0.0-00010101000000-000000000000
- go.loglayer.dev/v2 v2.1.0
+ go.loglayer.dev/transports/pretty/v3 v3.0.0-00010101000000-000000000000
+ go.loglayer.dev/v3 v3.0.0
)
require (
@@ -17,5 +17,5 @@ require (
github.com/goccy/go-json v0.10.6 // indirect
github.com/mattn/go-colorable v0.1.14 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
- golang.org/x/sys v0.42.0 // indirect
+ golang.org/x/sys v0.43.0 // indirect
)
diff --git a/examples/pretty-modes/go.sum b/examples/pretty-modes/go.sum
index 43acb76..b525da0 100644
--- a/examples/pretty-modes/go.sum
+++ b/examples/pretty-modes/go.sum
@@ -9,5 +9,5 @@ github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo=
-golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
+golang.org/x/sys v0.43.0 h1:Rlag2XtaFTxp19wS8MXlJwTvoh8ArU6ezoyFsMyCTNI=
+golang.org/x/sys v0.43.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
diff --git a/examples/pretty-modes/main.go b/examples/pretty-modes/main.go
index 4d26d92..31b124e 100644
--- a/examples/pretty-modes/main.go
+++ b/examples/pretty-modes/main.go
@@ -7,7 +7,7 @@
//
// Run:
//
-// go run go.loglayer.dev/v2/examples/pretty-modes
+// go run go.loglayer.dev/v3/examples/pretty-modes
//
// Or from a clone:
//
@@ -25,9 +25,9 @@ import (
"log"
"os"
- "go.loglayer.dev/transports/pretty/v2"
- "go.loglayer.dev/v2"
- "go.loglayer.dev/v2/transport/transporttest"
+ "go.loglayer.dev/transports/pretty/v3"
+ "go.loglayer.dev/v3"
+ "go.loglayer.dev/v3/transport/transporttest"
)
func main() {
diff --git a/go.work b/go.work
index ad5d210..5cbdff1 100644
--- a/go.work
+++ b/go.work
@@ -3,9 +3,27 @@ go 1.25.0
use (
.
+ ./examples/custom-plugin
+ ./examples/datadog-shipping
+ ./examples/http-server
+ ./examples/multi-transport
+ ./examples/otel-end-to-end
+ ./examples/pretty-modes
+
+ ./integrations/loghttp
+ ./integrations/sloghandler
+
+ ./plugins/datadogtrace
+ ./plugins/datadogtrace/livetest
+ ./plugins/fmtlog
+ ./plugins/oteltrace
+ ./plugins/plugintest
+ ./plugins/redact
+ ./plugins/sampling
+
./transports/axiom
- ./transports/blank
./transports/betterstack
+ ./transports/blank
./transports/central
./transports/charmlog
./transports/cli
@@ -13,8 +31,8 @@ use (
./transports/datadog
./transports/gcplogging
./transports/http
- ./transports/lumberjack
./transports/logrus
+ ./transports/lumberjack
./transports/newrelic
./transports/otellog
./transports/phuslu
@@ -25,22 +43,4 @@ use (
./transports/testing
./transports/zap
./transports/zerolog
-
- ./plugins/datadogtrace
- ./plugins/datadogtrace/livetest
- ./plugins/fmtlog
- ./plugins/oteltrace
- ./plugins/plugintest
- ./plugins/redact
- ./plugins/sampling
-
- ./integrations/loghttp
- ./integrations/sloghandler
-
- ./examples/custom-plugin
- ./examples/datadog-shipping
- ./examples/http-server
- ./examples/multi-transport
- ./examples/otel-end-to-end
- ./examples/pretty-modes
)
diff --git a/integrations/loghttp/go.mod b/integrations/loghttp/go.mod
index 22db4a0..86857be 100644
--- a/integrations/loghttp/go.mod
+++ b/integrations/loghttp/go.mod
@@ -1,10 +1,12 @@
-module go.loglayer.dev/integrations/loghttp/v2
+module go.loglayer.dev/integrations/loghttp/v3
go 1.25.0
require (
- go.loglayer.dev/transports/testing/v2 v2.0.1
- go.loglayer.dev/v2 v2.0.1
+ go.loglayer.dev/transports/testing/v3 v3.0.0-00010101000000-000000000000
+ go.loglayer.dev/v3 v3.0.0
)
require github.com/goccy/go-json v0.10.6 // indirect
+
+replace go.loglayer.dev/transports/testing/v3 => ../../transports/testing
diff --git a/integrations/loghttp/go.sum b/integrations/loghttp/go.sum
index 8cdc9aa..42ec334 100644
--- a/integrations/loghttp/go.sum
+++ b/integrations/loghttp/go.sum
@@ -1,8 +1,6 @@
github.com/goccy/go-json v0.10.6 h1:p8HrPJzOakx/mn/bQtjgNjdTcN+/S6FcG2CTtQOrHVU=
github.com/goccy/go-json v0.10.6/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
-go.loglayer.dev/transports/testing/v2 v2.0.1 h1:+xjONuRjjP2Z+CNa+wzfwoTCAn0VexRtd/d48DHCb/Q=
-go.loglayer.dev/transports/testing/v2 v2.0.1/go.mod h1:12kl35hwKAZctpUrkGiV67O2ZF41Drh21Rhad4lul6M=
-go.loglayer.dev/v2 v2.0.1 h1:B7oYpkfMky0UG/N8IdKeIiiTi6h7eyj5NvRyEth9DHI=
-go.loglayer.dev/v2 v2.0.1/go.mod h1:+BWhs5AyICvCLBz07qHnCE12W34tArUWfXOba0Ct/QI=
+go.loglayer.dev/v3 v3.0.0 h1:TEkgoAO21SAE0vKzrNa53G3VG88TJCKs/fMo177uY8s=
+go.loglayer.dev/v3 v3.0.0/go.mod h1:ghbgQp7Y/Vo4/jPCLiokMZTy2DrA3X8dfqtgcWhy3pg=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
diff --git a/integrations/loghttp/loghttp.go b/integrations/loghttp/loghttp.go
index 7ec1014..b4263fa 100644
--- a/integrations/loghttp/loghttp.go
+++ b/integrations/loghttp/loghttp.go
@@ -23,9 +23,9 @@ import (
"strings"
"time"
- "go.loglayer.dev/v2"
- "go.loglayer.dev/v2/utils/idgen"
- "go.loglayer.dev/v2/utils/sanitize"
+ "go.loglayer.dev/v3"
+ "go.loglayer.dev/v3/utils/idgen"
+ "go.loglayer.dev/v3/utils/sanitize"
)
// FieldNames customizes the keys emitted by the middleware. Empty values fall
diff --git a/integrations/loghttp/loghttp_test.go b/integrations/loghttp/loghttp_test.go
index b3970a6..96d545d 100644
--- a/integrations/loghttp/loghttp_test.go
+++ b/integrations/loghttp/loghttp_test.go
@@ -7,11 +7,11 @@ import (
"strings"
"testing"
- "go.loglayer.dev/integrations/loghttp/v2"
- lltest "go.loglayer.dev/transports/testing/v2"
- "go.loglayer.dev/v2"
- "go.loglayer.dev/v2/transport"
- "go.loglayer.dev/v2/transport/transporttest"
+ "go.loglayer.dev/integrations/loghttp/v3"
+ lltest "go.loglayer.dev/transports/testing/v3"
+ "go.loglayer.dev/v3"
+ "go.loglayer.dev/v3/transport"
+ "go.loglayer.dev/v3/transport/transporttest"
)
func setupLogger(t *testing.T) (*loglayer.LogLayer, *lltest.TestLoggingLibrary) {
diff --git a/integrations/sloghandler/bench_test.go b/integrations/sloghandler/bench_test.go
index 175c00f..3fd7bac 100644
--- a/integrations/sloghandler/bench_test.go
+++ b/integrations/sloghandler/bench_test.go
@@ -6,8 +6,8 @@ import (
"log/slog"
"testing"
- "go.loglayer.dev/integrations/sloghandler/v2"
- "go.loglayer.dev/v2"
+ "go.loglayer.dev/integrations/sloghandler/v3"
+ "go.loglayer.dev/v3"
)
// noopTransport drops everything; used to isolate handler dispatch cost
diff --git a/integrations/sloghandler/go.mod b/integrations/sloghandler/go.mod
index 19f6935..427d6c3 100644
--- a/integrations/sloghandler/go.mod
+++ b/integrations/sloghandler/go.mod
@@ -1,10 +1,12 @@
-module go.loglayer.dev/integrations/sloghandler/v2
+module go.loglayer.dev/integrations/sloghandler/v3
go 1.25.0
require (
- go.loglayer.dev/transports/testing/v2 v2.0.1
- go.loglayer.dev/v2 v2.0.1
+ go.loglayer.dev/transports/testing/v3 v3.0.0-00010101000000-000000000000
+ go.loglayer.dev/v3 v3.0.0
)
require github.com/goccy/go-json v0.10.6 // indirect
+
+replace go.loglayer.dev/transports/testing/v3 => ../../transports/testing
diff --git a/integrations/sloghandler/go.sum b/integrations/sloghandler/go.sum
index 8cdc9aa..42ec334 100644
--- a/integrations/sloghandler/go.sum
+++ b/integrations/sloghandler/go.sum
@@ -1,8 +1,6 @@
github.com/goccy/go-json v0.10.6 h1:p8HrPJzOakx/mn/bQtjgNjdTcN+/S6FcG2CTtQOrHVU=
github.com/goccy/go-json v0.10.6/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
-go.loglayer.dev/transports/testing/v2 v2.0.1 h1:+xjONuRjjP2Z+CNa+wzfwoTCAn0VexRtd/d48DHCb/Q=
-go.loglayer.dev/transports/testing/v2 v2.0.1/go.mod h1:12kl35hwKAZctpUrkGiV67O2ZF41Drh21Rhad4lul6M=
-go.loglayer.dev/v2 v2.0.1 h1:B7oYpkfMky0UG/N8IdKeIiiTi6h7eyj5NvRyEth9DHI=
-go.loglayer.dev/v2 v2.0.1/go.mod h1:+BWhs5AyICvCLBz07qHnCE12W34tArUWfXOba0Ct/QI=
+go.loglayer.dev/v3 v3.0.0 h1:TEkgoAO21SAE0vKzrNa53G3VG88TJCKs/fMo177uY8s=
+go.loglayer.dev/v3 v3.0.0/go.mod h1:ghbgQp7Y/Vo4/jPCLiokMZTy2DrA3X8dfqtgcWhy3pg=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
diff --git a/integrations/sloghandler/sloghandler.go b/integrations/sloghandler/sloghandler.go
index a0c4be6..e300123 100644
--- a/integrations/sloghandler/sloghandler.go
+++ b/integrations/sloghandler/sloghandler.go
@@ -45,7 +45,7 @@ import (
"log/slog"
"slices"
- "go.loglayer.dev/v2"
+ "go.loglayer.dev/v3"
)
// Handler is a slog.Handler that emits records through a loglayer logger.
diff --git a/integrations/sloghandler/sloghandler_test.go b/integrations/sloghandler/sloghandler_test.go
index edb8b81..db77e76 100644
--- a/integrations/sloghandler/sloghandler_test.go
+++ b/integrations/sloghandler/sloghandler_test.go
@@ -9,9 +9,9 @@ import (
"testing"
"time"
- "go.loglayer.dev/integrations/sloghandler/v2"
- lltest "go.loglayer.dev/transports/testing/v2"
- "go.loglayer.dev/v2"
+ "go.loglayer.dev/integrations/sloghandler/v3"
+ lltest "go.loglayer.dev/transports/testing/v3"
+ "go.loglayer.dev/v3"
)
func newSlog(t *testing.T) (*slog.Logger, *lltest.TestLoggingLibrary, *loglayer.LogLayer) {
diff --git a/plugins/datadogtrace/datadogtrace.go b/plugins/datadogtrace/datadogtrace.go
index 819242b..cb9d37a 100644
--- a/plugins/datadogtrace/datadogtrace.go
+++ b/plugins/datadogtrace/datadogtrace.go
@@ -18,9 +18,9 @@
// import (
// "context"
// ddtracer "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer"
-// "go.loglayer.dev/v2"
-// "go.loglayer.dev/plugins/datadogtrace/v2"
-// "go.loglayer.dev/transports/structured/v2"
+// "go.loglayer.dev/v3"
+// "go.loglayer.dev/plugins/datadogtrace/v3"
+// "go.loglayer.dev/transports/structured/v3"
// )
//
// ddtracer.Start(ddtracer.WithService("checkout-api"))
@@ -56,7 +56,7 @@ import (
"context"
"strconv"
- "go.loglayer.dev/v2"
+ "go.loglayer.dev/v3"
)
// Config holds plugin configuration.
diff --git a/plugins/datadogtrace/datadogtrace_test.go b/plugins/datadogtrace/datadogtrace_test.go
index d372c57..3dec690 100644
--- a/plugins/datadogtrace/datadogtrace_test.go
+++ b/plugins/datadogtrace/datadogtrace_test.go
@@ -6,9 +6,9 @@ import (
"strings"
"testing"
- "go.loglayer.dev/plugins/datadogtrace/v2"
- "go.loglayer.dev/plugins/plugintest/v2"
- "go.loglayer.dev/v2"
+ "go.loglayer.dev/plugins/datadogtrace/v3"
+ "go.loglayer.dev/plugins/plugintest/v3"
+ "go.loglayer.dev/v3"
)
// fakeExtract returns a deterministic extractor for tests.
diff --git a/plugins/datadogtrace/example_test.go b/plugins/datadogtrace/example_test.go
index 21f789b..127e80d 100644
--- a/plugins/datadogtrace/example_test.go
+++ b/plugins/datadogtrace/example_test.go
@@ -3,9 +3,9 @@ package datadogtrace_test
import (
"context"
- "go.loglayer.dev/plugins/datadogtrace/v2"
- lltesting "go.loglayer.dev/transports/testing/v2"
- "go.loglayer.dev/v2"
+ "go.loglayer.dev/plugins/datadogtrace/v3"
+ lltesting "go.loglayer.dev/transports/testing/v3"
+ "go.loglayer.dev/v3"
)
// New returns a plugin that injects dd.trace_id and dd.span_id onto
diff --git a/plugins/datadogtrace/go.mod b/plugins/datadogtrace/go.mod
index 6702068..8f9b92c 100644
--- a/plugins/datadogtrace/go.mod
+++ b/plugins/datadogtrace/go.mod
@@ -1,11 +1,15 @@
-module go.loglayer.dev/plugins/datadogtrace/v2
+module go.loglayer.dev/plugins/datadogtrace/v3
go 1.25.0
require (
- go.loglayer.dev/plugins/plugintest/v2 v2.0.1
- go.loglayer.dev/transports/testing/v2 v2.0.1
- go.loglayer.dev/v2 v2.0.1
+ go.loglayer.dev/plugins/plugintest/v3 v3.0.0-00010101000000-000000000000
+ go.loglayer.dev/transports/testing/v3 v3.0.0-00010101000000-000000000000
+ go.loglayer.dev/v3 v3.0.0
)
require github.com/goccy/go-json v0.10.6 // indirect
+
+replace go.loglayer.dev/transports/testing/v3 => ../../transports/testing
+
+replace go.loglayer.dev/plugins/plugintest/v3 => ../plugintest
diff --git a/plugins/datadogtrace/go.sum b/plugins/datadogtrace/go.sum
index d4e9f50..42ec334 100644
--- a/plugins/datadogtrace/go.sum
+++ b/plugins/datadogtrace/go.sum
@@ -1,10 +1,6 @@
github.com/goccy/go-json v0.10.6 h1:p8HrPJzOakx/mn/bQtjgNjdTcN+/S6FcG2CTtQOrHVU=
github.com/goccy/go-json v0.10.6/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
-go.loglayer.dev/plugins/plugintest/v2 v2.0.1 h1:LirZvR4XdbsnduUkjn72C0Ni1A9TDnpjMXNK59qRhCw=
-go.loglayer.dev/plugins/plugintest/v2 v2.0.1/go.mod h1:i+iSoeHFVhNeLdbpHIZCXVzse4dmGcucJ5yKnpbEHlU=
-go.loglayer.dev/transports/testing/v2 v2.0.1 h1:+xjONuRjjP2Z+CNa+wzfwoTCAn0VexRtd/d48DHCb/Q=
-go.loglayer.dev/transports/testing/v2 v2.0.1/go.mod h1:12kl35hwKAZctpUrkGiV67O2ZF41Drh21Rhad4lul6M=
-go.loglayer.dev/v2 v2.0.1 h1:B7oYpkfMky0UG/N8IdKeIiiTi6h7eyj5NvRyEth9DHI=
-go.loglayer.dev/v2 v2.0.1/go.mod h1:+BWhs5AyICvCLBz07qHnCE12W34tArUWfXOba0Ct/QI=
+go.loglayer.dev/v3 v3.0.0 h1:TEkgoAO21SAE0vKzrNa53G3VG88TJCKs/fMo177uY8s=
+go.loglayer.dev/v3 v3.0.0/go.mod h1:ghbgQp7Y/Vo4/jPCLiokMZTy2DrA3X8dfqtgcWhy3pg=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
diff --git a/plugins/datadogtrace/livetest/go.mod b/plugins/datadogtrace/livetest/go.mod
index 6fdd96f..9de9586 100644
--- a/plugins/datadogtrace/livetest/go.mod
+++ b/plugins/datadogtrace/livetest/go.mod
@@ -3,17 +3,17 @@ module go.loglayer.dev/plugins/datadogtrace/livetest/v2
go 1.25.0
replace (
- go.loglayer.dev/plugins/datadogtrace/v2 => ..
- go.loglayer.dev/plugins/plugintest/v2 => ../../plugintest
- go.loglayer.dev/transports/testing/v2 => ../../../transports/testing
- go.loglayer.dev/v2 => ../../..
+ go.loglayer.dev/plugins/datadogtrace/v3 => ..
+ go.loglayer.dev/plugins/plugintest/v3 => ../../plugintest
+ go.loglayer.dev/transports/testing/v3 => ../../../transports/testing
+ go.loglayer.dev/v3 => ../../..
)
require (
github.com/DataDog/dd-trace-go/v2 v2.7.3
- go.loglayer.dev/plugins/datadogtrace/v2 v2.0.0-00010101000000-000000000000
- go.loglayer.dev/transports/testing/v2 v2.0.1
- go.loglayer.dev/v2 v2.0.1
+ go.loglayer.dev/plugins/datadogtrace/v3 v3.0.0-00010101000000-000000000000
+ go.loglayer.dev/transports/testing/v3 v3.0.0-00010101000000-000000000000
+ go.loglayer.dev/v3 v3.0.0
)
require (
@@ -49,7 +49,7 @@ require (
github.com/google/uuid v1.6.0 // indirect
github.com/hashicorp/go-version v1.8.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
- github.com/klauspost/compress v1.18.4 // indirect
+ github.com/klauspost/compress v1.18.5 // indirect
github.com/klauspost/cpuid/v2 v2.3.0 // indirect
github.com/linkdata/deadlock v0.5.5 // indirect
github.com/lufia/plan9stats v0.0.0-20260216142805-b3301c5f2a88 // indirect
@@ -84,12 +84,14 @@ require (
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.27.1 // indirect
go.yaml.in/yaml/v3 v3.0.4 // indirect
+ golang.org/x/crypto v0.50.0 // indirect
golang.org/x/exp v0.0.0-20260209203927-2842357ff358 // indirect
- golang.org/x/mod v0.33.0 // indirect
- golang.org/x/sys v0.42.0 // indirect
+ golang.org/x/mod v0.34.0 // indirect
+ golang.org/x/net v0.53.0 // indirect
+ golang.org/x/sys v0.43.0 // indirect
golang.org/x/time v0.15.0 // indirect
golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da // indirect
- google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57 // indirect
+ google.golang.org/genproto/googleapis/rpc v0.0.0-20260414002931-afd174a4e478 // indirect
google.golang.org/protobuf v1.36.11 // indirect
gopkg.in/ini.v1 v1.67.1 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
diff --git a/plugins/datadogtrace/livetest/go.sum b/plugins/datadogtrace/livetest/go.sum
index a450222..3c29077 100644
--- a/plugins/datadogtrace/livetest/go.sum
+++ b/plugins/datadogtrace/livetest/go.sum
@@ -91,8 +91,8 @@ github.com/hashicorp/go-version v1.8.0 h1:KAkNb1HAiZd1ukkxDFGmokVZe1Xy9HG6NUp+bP
github.com/hashicorp/go-version v1.8.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
-github.com/klauspost/compress v1.18.4 h1:RPhnKRAQ4Fh8zU2FY/6ZFDwTVTxgJ/EMydqSTzE9a2c=
-github.com/klauspost/compress v1.18.4/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4=
+github.com/klauspost/compress v1.18.5 h1:/h1gH5Ce+VWNLSWqPzOVn6XBO+vJbCNGvjoaGBFW2IE=
+github.com/klauspost/compress v1.18.5/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ=
github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y=
github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
@@ -238,18 +238,18 @@ go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
-golang.org/x/crypto v0.49.0 h1:+Ng2ULVvLHnJ/ZFEq4KdcDd/cfjrrjjNSXNzxg0Y4U4=
-golang.org/x/crypto v0.49.0/go.mod h1:ErX4dUh2UM+CFYiXZRTcMpEcN8b/1gxEuv3nODoYtCA=
+golang.org/x/crypto v0.50.0 h1:zO47/JPrL6vsNkINmLoo/PH1gcxpls50DNogFvB5ZGI=
+golang.org/x/crypto v0.50.0/go.mod h1:3muZ7vA7PBCE6xgPX7nkzzjiUq87kRItoJQM1Yo8S+Q=
golang.org/x/exp v0.0.0-20260209203927-2842357ff358 h1:kpfSV7uLwKJbFSEgNhWzGSL47NDSF/5pYYQw1V0ub6c=
golang.org/x/exp v0.0.0-20260209203927-2842357ff358/go.mod h1:R3t0oliuryB5eenPWl3rrQxwnNM3WTwnsRZZiXLAAW8=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
-golang.org/x/mod v0.33.0 h1:tHFzIWbBifEmbwtGz65eaWyGiGZatSrT9prnU8DbVL8=
-golang.org/x/mod v0.33.0/go.mod h1:swjeQEj+6r7fODbD2cqrnje9PnziFuw4bmLbBZFrQ5w=
+golang.org/x/mod v0.34.0 h1:xIHgNUUnW6sYkcM5Jleh05DvLOtwc6RitGHbDk4akRI=
+golang.org/x/mod v0.34.0/go.mod h1:ykgH52iCZe79kzLLMhyCUzhMci+nQj+0XkbXpNYtVjY=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
-golang.org/x/net v0.51.0 h1:94R/GTO7mt3/4wIKpcR5gkGmRLOuE/2hNGeWq/GBIFo=
-golang.org/x/net v0.51.0/go.mod h1:aamm+2QF5ogm02fjy5Bb7CQ0WMt1/WVM7FtyaTLlA9Y=
+golang.org/x/net v0.53.0 h1:d+qAbo5L0orcWAr0a9JweQpjXF19LMXJE8Ey7hwOdUA=
+golang.org/x/net v0.53.0/go.mod h1:JvMuJH7rrdiCfbeHoo3fCQU24Lf5JJwT9W3sJFulfgs=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -263,8 +263,8 @@ golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220627191245-f75cf1eec38b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo=
-golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
+golang.org/x/sys v0.43.0 h1:Rlag2XtaFTxp19wS8MXlJwTvoh8ArU6ezoyFsMyCTNI=
+golang.org/x/sys v0.43.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
@@ -280,8 +280,8 @@ golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da h1:noIWHXmPHxILtqtCOPIhS
golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90=
google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM=
google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57 h1:mWPCjDEyshlQYzBpMNHaEof6UX1PmHcaUODUywQ0uac=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20260414002931-afd174a4e478 h1:RmoJA1ujG+/lRGNfUnOMfhCy5EipVMyvUE+KNbPbTlw=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20260414002931-afd174a4e478/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
diff --git a/plugins/datadogtrace/livetest/livetest_test.go b/plugins/datadogtrace/livetest/livetest_test.go
index 0a6ef34..1b01804 100644
--- a/plugins/datadogtrace/livetest/livetest_test.go
+++ b/plugins/datadogtrace/livetest/livetest_test.go
@@ -18,10 +18,10 @@ import (
"strconv"
"testing"
- "go.loglayer.dev/plugins/datadogtrace/v2"
- lltest "go.loglayer.dev/transports/testing/v2"
- "go.loglayer.dev/v2"
- "go.loglayer.dev/v2/transport"
+ "go.loglayer.dev/plugins/datadogtrace/v3"
+ lltest "go.loglayer.dev/transports/testing/v3"
+ "go.loglayer.dev/v3"
+ "go.loglayer.dev/v3/transport"
ddmocktracer "github.com/DataDog/dd-trace-go/v2/ddtrace/mocktracer"
ddtracer "github.com/DataDog/dd-trace-go/v2/ddtrace/tracer"
diff --git a/plugins/fmtlog/example_test.go b/plugins/fmtlog/example_test.go
index 2c80e8c..c1be221 100644
--- a/plugins/fmtlog/example_test.go
+++ b/plugins/fmtlog/example_test.go
@@ -3,9 +3,9 @@ package fmtlog_test
import (
"fmt"
- "go.loglayer.dev/plugins/fmtlog/v2"
- lltesting "go.loglayer.dev/transports/testing/v2"
- "go.loglayer.dev/v2"
+ "go.loglayer.dev/plugins/fmtlog/v3"
+ lltesting "go.loglayer.dev/transports/testing/v3"
+ "go.loglayer.dev/v3"
)
// New returns a plugin that rewrites multi-argument log messages via
diff --git a/plugins/fmtlog/fmtlog.go b/plugins/fmtlog/fmtlog.go
index d54b71f..7765791 100644
--- a/plugins/fmtlog/fmtlog.go
+++ b/plugins/fmtlog/fmtlog.go
@@ -21,7 +21,7 @@ package fmtlog
import (
"fmt"
- "go.loglayer.dev/v2"
+ "go.loglayer.dev/v3"
)
// New returns a plugin that resolves multi-argument log messages via
diff --git a/plugins/fmtlog/fmtlog_test.go b/plugins/fmtlog/fmtlog_test.go
index de226c6..e3a58ae 100644
--- a/plugins/fmtlog/fmtlog_test.go
+++ b/plugins/fmtlog/fmtlog_test.go
@@ -4,10 +4,10 @@ import (
"errors"
"testing"
- "go.loglayer.dev/plugins/fmtlog/v2"
- lltest "go.loglayer.dev/transports/testing/v2"
- "go.loglayer.dev/v2"
- "go.loglayer.dev/v2/transport"
+ "go.loglayer.dev/plugins/fmtlog/v3"
+ lltest "go.loglayer.dev/transports/testing/v3"
+ "go.loglayer.dev/v3"
+ "go.loglayer.dev/v3/transport"
)
func newLogger(t *testing.T) (*loglayer.LogLayer, *lltest.TestLoggingLibrary) {
diff --git a/plugins/fmtlog/go.mod b/plugins/fmtlog/go.mod
index d5f8fc5..ee94ae4 100644
--- a/plugins/fmtlog/go.mod
+++ b/plugins/fmtlog/go.mod
@@ -1,10 +1,12 @@
-module go.loglayer.dev/plugins/fmtlog/v2
+module go.loglayer.dev/plugins/fmtlog/v3
go 1.25.0
require (
- go.loglayer.dev/transports/testing/v2 v2.0.1
- go.loglayer.dev/v2 v2.0.1
+ go.loglayer.dev/transports/testing/v3 v3.0.0-00010101000000-000000000000
+ go.loglayer.dev/v3 v3.0.0
)
require github.com/goccy/go-json v0.10.6 // indirect
+
+replace go.loglayer.dev/transports/testing/v3 => ../../transports/testing
diff --git a/plugins/fmtlog/go.sum b/plugins/fmtlog/go.sum
index 8cdc9aa..42ec334 100644
--- a/plugins/fmtlog/go.sum
+++ b/plugins/fmtlog/go.sum
@@ -1,8 +1,6 @@
github.com/goccy/go-json v0.10.6 h1:p8HrPJzOakx/mn/bQtjgNjdTcN+/S6FcG2CTtQOrHVU=
github.com/goccy/go-json v0.10.6/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
-go.loglayer.dev/transports/testing/v2 v2.0.1 h1:+xjONuRjjP2Z+CNa+wzfwoTCAn0VexRtd/d48DHCb/Q=
-go.loglayer.dev/transports/testing/v2 v2.0.1/go.mod h1:12kl35hwKAZctpUrkGiV67O2ZF41Drh21Rhad4lul6M=
-go.loglayer.dev/v2 v2.0.1 h1:B7oYpkfMky0UG/N8IdKeIiiTi6h7eyj5NvRyEth9DHI=
-go.loglayer.dev/v2 v2.0.1/go.mod h1:+BWhs5AyICvCLBz07qHnCE12W34tArUWfXOba0Ct/QI=
+go.loglayer.dev/v3 v3.0.0 h1:TEkgoAO21SAE0vKzrNa53G3VG88TJCKs/fMo177uY8s=
+go.loglayer.dev/v3 v3.0.0/go.mod h1:ghbgQp7Y/Vo4/jPCLiokMZTy2DrA3X8dfqtgcWhy3pg=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
diff --git a/plugins/oteltrace/example_test.go b/plugins/oteltrace/example_test.go
index 02bb6fd..d37e470 100644
--- a/plugins/oteltrace/example_test.go
+++ b/plugins/oteltrace/example_test.go
@@ -1,9 +1,9 @@
package oteltrace_test
import (
- "go.loglayer.dev/plugins/oteltrace/v2"
- lltesting "go.loglayer.dev/transports/testing/v2"
- "go.loglayer.dev/v2"
+ "go.loglayer.dev/plugins/oteltrace/v3"
+ lltesting "go.loglayer.dev/transports/testing/v3"
+ "go.loglayer.dev/v3"
)
// New returns a plugin that injects the active OTel span's trace_id
diff --git a/plugins/oteltrace/go.mod b/plugins/oteltrace/go.mod
index 366495f..ab6ec0e 100644
--- a/plugins/oteltrace/go.mod
+++ b/plugins/oteltrace/go.mod
@@ -1,11 +1,11 @@
-module go.loglayer.dev/plugins/oteltrace/v2
+module go.loglayer.dev/plugins/oteltrace/v3
go 1.25.0
require (
- go.loglayer.dev/plugins/plugintest/v2 v2.0.1
- go.loglayer.dev/transports/testing/v2 v2.0.1
- go.loglayer.dev/v2 v2.0.1
+ go.loglayer.dev/plugins/plugintest/v3 v3.0.0-00010101000000-000000000000
+ go.loglayer.dev/transports/testing/v3 v3.0.0-00010101000000-000000000000
+ go.loglayer.dev/v3 v3.0.0
go.opentelemetry.io/otel v1.43.0
go.opentelemetry.io/otel/sdk v1.43.0
go.opentelemetry.io/otel/trace v1.43.0
@@ -19,5 +19,9 @@ require (
github.com/google/uuid v1.6.0 // indirect
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
go.opentelemetry.io/otel/metric v1.43.0 // indirect
- golang.org/x/sys v0.42.0 // indirect
+ golang.org/x/sys v0.43.0 // indirect
)
+
+replace go.loglayer.dev/transports/testing/v3 => ../../transports/testing
+
+replace go.loglayer.dev/plugins/plugintest/v3 => ../plugintest
diff --git a/plugins/oteltrace/go.sum b/plugins/oteltrace/go.sum
index 90eb271..f645c7b 100644
--- a/plugins/oteltrace/go.sum
+++ b/plugins/oteltrace/go.sum
@@ -17,12 +17,8 @@ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRI
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
-go.loglayer.dev/plugins/plugintest/v2 v2.0.1 h1:LirZvR4XdbsnduUkjn72C0Ni1A9TDnpjMXNK59qRhCw=
-go.loglayer.dev/plugins/plugintest/v2 v2.0.1/go.mod h1:i+iSoeHFVhNeLdbpHIZCXVzse4dmGcucJ5yKnpbEHlU=
-go.loglayer.dev/transports/testing/v2 v2.0.1 h1:+xjONuRjjP2Z+CNa+wzfwoTCAn0VexRtd/d48DHCb/Q=
-go.loglayer.dev/transports/testing/v2 v2.0.1/go.mod h1:12kl35hwKAZctpUrkGiV67O2ZF41Drh21Rhad4lul6M=
-go.loglayer.dev/v2 v2.0.1 h1:B7oYpkfMky0UG/N8IdKeIiiTi6h7eyj5NvRyEth9DHI=
-go.loglayer.dev/v2 v2.0.1/go.mod h1:+BWhs5AyICvCLBz07qHnCE12W34tArUWfXOba0Ct/QI=
+go.loglayer.dev/v3 v3.0.0 h1:TEkgoAO21SAE0vKzrNa53G3VG88TJCKs/fMo177uY8s=
+go.loglayer.dev/v3 v3.0.0/go.mod h1:ghbgQp7Y/Vo4/jPCLiokMZTy2DrA3X8dfqtgcWhy3pg=
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
go.opentelemetry.io/otel v1.43.0 h1:mYIM03dnh5zfN7HautFE4ieIig9amkNANT+xcVxAj9I=
@@ -37,7 +33,7 @@ go.opentelemetry.io/otel/trace v1.43.0 h1:BkNrHpup+4k4w+ZZ86CZoHHEkohws8AY+WTX09
go.opentelemetry.io/otel/trace v1.43.0/go.mod h1:/QJhyVBUUswCphDVxq+8mld+AvhXZLhe+8WVFxiFff0=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
-golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo=
-golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
+golang.org/x/sys v0.43.0 h1:Rlag2XtaFTxp19wS8MXlJwTvoh8ArU6ezoyFsMyCTNI=
+golang.org/x/sys v0.43.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
diff --git a/plugins/oteltrace/livetest_test.go b/plugins/oteltrace/livetest_test.go
index 904a418..c32c0f8 100644
--- a/plugins/oteltrace/livetest_test.go
+++ b/plugins/oteltrace/livetest_test.go
@@ -14,10 +14,10 @@ import (
"context"
"testing"
- "go.loglayer.dev/plugins/oteltrace/v2"
- "go.loglayer.dev/plugins/plugintest/v2"
- lltest "go.loglayer.dev/transports/testing/v2"
- "go.loglayer.dev/v2"
+ "go.loglayer.dev/plugins/oteltrace/v3"
+ "go.loglayer.dev/plugins/plugintest/v3"
+ lltest "go.loglayer.dev/transports/testing/v3"
+ "go.loglayer.dev/v3"
"go.opentelemetry.io/otel/baggage"
otrace "go.opentelemetry.io/otel/trace"
diff --git a/plugins/oteltrace/oteltrace.go b/plugins/oteltrace/oteltrace.go
index 1df8f29..5715dce 100644
--- a/plugins/oteltrace/oteltrace.go
+++ b/plugins/oteltrace/oteltrace.go
@@ -18,9 +18,9 @@
// Usage:
//
// import (
-// "go.loglayer.dev/v2"
-// "go.loglayer.dev/plugins/oteltrace/v2"
-// "go.loglayer.dev/transports/structured/v2"
+// "go.loglayer.dev/v3"
+// "go.loglayer.dev/plugins/oteltrace/v3"
+// "go.loglayer.dev/transports/structured/v3"
// )
//
// log := loglayer.New(loglayer.Config{
@@ -37,7 +37,7 @@
package oteltrace
import (
- "go.loglayer.dev/v2"
+ "go.loglayer.dev/v3"
"go.opentelemetry.io/otel/baggage"
"go.opentelemetry.io/otel/trace"
)
diff --git a/plugins/oteltrace/oteltrace_test.go b/plugins/oteltrace/oteltrace_test.go
index 54ee2ec..d12230b 100644
--- a/plugins/oteltrace/oteltrace_test.go
+++ b/plugins/oteltrace/oteltrace_test.go
@@ -4,9 +4,9 @@ import (
"context"
"testing"
- "go.loglayer.dev/plugins/oteltrace/v2"
- "go.loglayer.dev/plugins/plugintest/v2"
- "go.loglayer.dev/v2"
+ "go.loglayer.dev/plugins/oteltrace/v3"
+ "go.loglayer.dev/plugins/plugintest/v3"
+ "go.loglayer.dev/v3"
"go.opentelemetry.io/otel/baggage"
otrace "go.opentelemetry.io/otel/trace"
)
diff --git a/plugins/plugintest/go.mod b/plugins/plugintest/go.mod
index b51c3cf..bb58beb 100644
--- a/plugins/plugintest/go.mod
+++ b/plugins/plugintest/go.mod
@@ -1,10 +1,12 @@
-module go.loglayer.dev/plugins/plugintest/v2
+module go.loglayer.dev/plugins/plugintest/v3
go 1.25.0
require (
- go.loglayer.dev/transports/testing/v2 v2.0.1
- go.loglayer.dev/v2 v2.0.1
+ go.loglayer.dev/transports/testing/v3 v3.0.0-00010101000000-000000000000
+ go.loglayer.dev/v3 v3.0.0
)
require github.com/goccy/go-json v0.10.6 // indirect
+
+replace go.loglayer.dev/transports/testing/v3 => ../../transports/testing
diff --git a/plugins/plugintest/go.sum b/plugins/plugintest/go.sum
index 8cdc9aa..42ec334 100644
--- a/plugins/plugintest/go.sum
+++ b/plugins/plugintest/go.sum
@@ -1,8 +1,6 @@
github.com/goccy/go-json v0.10.6 h1:p8HrPJzOakx/mn/bQtjgNjdTcN+/S6FcG2CTtQOrHVU=
github.com/goccy/go-json v0.10.6/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
-go.loglayer.dev/transports/testing/v2 v2.0.1 h1:+xjONuRjjP2Z+CNa+wzfwoTCAn0VexRtd/d48DHCb/Q=
-go.loglayer.dev/transports/testing/v2 v2.0.1/go.mod h1:12kl35hwKAZctpUrkGiV67O2ZF41Drh21Rhad4lul6M=
-go.loglayer.dev/v2 v2.0.1 h1:B7oYpkfMky0UG/N8IdKeIiiTi6h7eyj5NvRyEth9DHI=
-go.loglayer.dev/v2 v2.0.1/go.mod h1:+BWhs5AyICvCLBz07qHnCE12W34tArUWfXOba0Ct/QI=
+go.loglayer.dev/v3 v3.0.0 h1:TEkgoAO21SAE0vKzrNa53G3VG88TJCKs/fMo177uY8s=
+go.loglayer.dev/v3 v3.0.0/go.mod h1:ghbgQp7Y/Vo4/jPCLiokMZTy2DrA3X8dfqtgcWhy3pg=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
diff --git a/plugins/plugintest/plugintest.go b/plugins/plugintest/plugintest.go
index bbcab9a..0b74baa 100644
--- a/plugins/plugintest/plugintest.go
+++ b/plugins/plugintest/plugintest.go
@@ -18,10 +18,10 @@ import (
"reflect"
"testing"
- lltest "go.loglayer.dev/transports/testing/v2"
- "go.loglayer.dev/v2"
- "go.loglayer.dev/v2/transport"
- "go.loglayer.dev/v2/utils/maputil"
+ lltest "go.loglayer.dev/transports/testing/v3"
+ "go.loglayer.dev/v3"
+ "go.loglayer.dev/v3/transport"
+ "go.loglayer.dev/v3/utils/maputil"
)
// Install builds a fresh *loglayer.LogLayer with p installed and the
diff --git a/plugins/plugintest/plugintest_test.go b/plugins/plugintest/plugintest_test.go
index 9debd4c..4b03596 100644
--- a/plugins/plugintest/plugintest_test.go
+++ b/plugins/plugintest/plugintest_test.go
@@ -5,8 +5,8 @@ import (
"strings"
"testing"
- "go.loglayer.dev/plugins/plugintest/v2"
- "go.loglayer.dev/v2"
+ "go.loglayer.dev/plugins/plugintest/v3"
+ "go.loglayer.dev/v3"
)
func TestInstall_PluginRunsAndCaptures(t *testing.T) {
diff --git a/plugins/redact/example_test.go b/plugins/redact/example_test.go
index b35ebaa..2cf2d73 100644
--- a/plugins/redact/example_test.go
+++ b/plugins/redact/example_test.go
@@ -3,9 +3,9 @@ package redact_test
import (
"fmt"
- "go.loglayer.dev/plugins/redact/v2"
- lltesting "go.loglayer.dev/transports/testing/v2"
- "go.loglayer.dev/v2"
+ "go.loglayer.dev/plugins/redact/v3"
+ lltesting "go.loglayer.dev/transports/testing/v3"
+ "go.loglayer.dev/v3"
)
// New returns a plugin that replaces values for keys listed in
diff --git a/plugins/redact/go.mod b/plugins/redact/go.mod
index cb73f44..4f90e7a 100644
--- a/plugins/redact/go.mod
+++ b/plugins/redact/go.mod
@@ -1,11 +1,15 @@
-module go.loglayer.dev/plugins/redact/v2
+module go.loglayer.dev/plugins/redact/v3
go 1.25.0
require (
- go.loglayer.dev/plugins/plugintest/v2 v2.0.1
- go.loglayer.dev/transports/testing/v2 v2.0.1
- go.loglayer.dev/v2 v2.0.1
+ go.loglayer.dev/plugins/plugintest/v3 v3.0.0-00010101000000-000000000000
+ go.loglayer.dev/transports/testing/v3 v3.0.0-00010101000000-000000000000
+ go.loglayer.dev/v3 v3.0.0
)
require github.com/goccy/go-json v0.10.6 // indirect
+
+replace go.loglayer.dev/transports/testing/v3 => ../../transports/testing
+
+replace go.loglayer.dev/plugins/plugintest/v3 => ../plugintest
diff --git a/plugins/redact/go.sum b/plugins/redact/go.sum
index d4e9f50..42ec334 100644
--- a/plugins/redact/go.sum
+++ b/plugins/redact/go.sum
@@ -1,10 +1,6 @@
github.com/goccy/go-json v0.10.6 h1:p8HrPJzOakx/mn/bQtjgNjdTcN+/S6FcG2CTtQOrHVU=
github.com/goccy/go-json v0.10.6/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
-go.loglayer.dev/plugins/plugintest/v2 v2.0.1 h1:LirZvR4XdbsnduUkjn72C0Ni1A9TDnpjMXNK59qRhCw=
-go.loglayer.dev/plugins/plugintest/v2 v2.0.1/go.mod h1:i+iSoeHFVhNeLdbpHIZCXVzse4dmGcucJ5yKnpbEHlU=
-go.loglayer.dev/transports/testing/v2 v2.0.1 h1:+xjONuRjjP2Z+CNa+wzfwoTCAn0VexRtd/d48DHCb/Q=
-go.loglayer.dev/transports/testing/v2 v2.0.1/go.mod h1:12kl35hwKAZctpUrkGiV67O2ZF41Drh21Rhad4lul6M=
-go.loglayer.dev/v2 v2.0.1 h1:B7oYpkfMky0UG/N8IdKeIiiTi6h7eyj5NvRyEth9DHI=
-go.loglayer.dev/v2 v2.0.1/go.mod h1:+BWhs5AyICvCLBz07qHnCE12W34tArUWfXOba0Ct/QI=
+go.loglayer.dev/v3 v3.0.0 h1:TEkgoAO21SAE0vKzrNa53G3VG88TJCKs/fMo177uY8s=
+go.loglayer.dev/v3 v3.0.0/go.mod h1:ghbgQp7Y/Vo4/jPCLiokMZTy2DrA3X8dfqtgcWhy3pg=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
diff --git a/plugins/redact/redact.go b/plugins/redact/redact.go
index 5618eb2..cce06ce 100644
--- a/plugins/redact/redact.go
+++ b/plugins/redact/redact.go
@@ -28,8 +28,8 @@ package redact
import (
"regexp"
- "go.loglayer.dev/v2"
- "go.loglayer.dev/v2/utils/maputil"
+ "go.loglayer.dev/v3"
+ "go.loglayer.dev/v3/utils/maputil"
)
// Config holds redactor configuration.
diff --git a/plugins/redact/redact_test.go b/plugins/redact/redact_test.go
index a88f8a0..cc47fa0 100644
--- a/plugins/redact/redact_test.go
+++ b/plugins/redact/redact_test.go
@@ -5,9 +5,9 @@ import (
"regexp"
"testing"
- "go.loglayer.dev/plugins/plugintest/v2"
- "go.loglayer.dev/plugins/redact/v2"
- "go.loglayer.dev/v2"
+ "go.loglayer.dev/plugins/plugintest/v3"
+ "go.loglayer.dev/plugins/redact/v3"
+ "go.loglayer.dev/v3"
)
func TestRedact_MetadataMapKey(t *testing.T) {
diff --git a/plugins/sampling/example_test.go b/plugins/sampling/example_test.go
index 6a94a37..f17933c 100644
--- a/plugins/sampling/example_test.go
+++ b/plugins/sampling/example_test.go
@@ -3,9 +3,9 @@ package sampling_test
import (
"fmt"
- "go.loglayer.dev/plugins/sampling/v2"
- lltesting "go.loglayer.dev/transports/testing/v2"
- "go.loglayer.dev/v2"
+ "go.loglayer.dev/plugins/sampling/v3"
+ lltesting "go.loglayer.dev/transports/testing/v3"
+ "go.loglayer.dev/v3"
)
// FixedRate keeps the given fraction of emissions. rate >= 1 keeps
diff --git a/plugins/sampling/go.mod b/plugins/sampling/go.mod
index 5cf2d99..ab8f114 100644
--- a/plugins/sampling/go.mod
+++ b/plugins/sampling/go.mod
@@ -1,11 +1,15 @@
-module go.loglayer.dev/plugins/sampling/v2
+module go.loglayer.dev/plugins/sampling/v3
go 1.25.0
require (
- go.loglayer.dev/plugins/plugintest/v2 v2.0.1
- go.loglayer.dev/transports/testing/v2 v2.0.1
- go.loglayer.dev/v2 v2.0.1
+ go.loglayer.dev/plugins/plugintest/v3 v3.0.0-00010101000000-000000000000
+ go.loglayer.dev/transports/testing/v3 v3.0.0-00010101000000-000000000000
+ go.loglayer.dev/v3 v3.0.0
)
require github.com/goccy/go-json v0.10.6 // indirect
+
+replace go.loglayer.dev/transports/testing/v3 => ../../transports/testing
+
+replace go.loglayer.dev/plugins/plugintest/v3 => ../plugintest
diff --git a/plugins/sampling/go.sum b/plugins/sampling/go.sum
index d4e9f50..42ec334 100644
--- a/plugins/sampling/go.sum
+++ b/plugins/sampling/go.sum
@@ -1,10 +1,6 @@
github.com/goccy/go-json v0.10.6 h1:p8HrPJzOakx/mn/bQtjgNjdTcN+/S6FcG2CTtQOrHVU=
github.com/goccy/go-json v0.10.6/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
-go.loglayer.dev/plugins/plugintest/v2 v2.0.1 h1:LirZvR4XdbsnduUkjn72C0Ni1A9TDnpjMXNK59qRhCw=
-go.loglayer.dev/plugins/plugintest/v2 v2.0.1/go.mod h1:i+iSoeHFVhNeLdbpHIZCXVzse4dmGcucJ5yKnpbEHlU=
-go.loglayer.dev/transports/testing/v2 v2.0.1 h1:+xjONuRjjP2Z+CNa+wzfwoTCAn0VexRtd/d48DHCb/Q=
-go.loglayer.dev/transports/testing/v2 v2.0.1/go.mod h1:12kl35hwKAZctpUrkGiV67O2ZF41Drh21Rhad4lul6M=
-go.loglayer.dev/v2 v2.0.1 h1:B7oYpkfMky0UG/N8IdKeIiiTi6h7eyj5NvRyEth9DHI=
-go.loglayer.dev/v2 v2.0.1/go.mod h1:+BWhs5AyICvCLBz07qHnCE12W34tArUWfXOba0Ct/QI=
+go.loglayer.dev/v3 v3.0.0 h1:TEkgoAO21SAE0vKzrNa53G3VG88TJCKs/fMo177uY8s=
+go.loglayer.dev/v3 v3.0.0/go.mod h1:ghbgQp7Y/Vo4/jPCLiokMZTy2DrA3X8dfqtgcWhy3pg=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
diff --git a/plugins/sampling/sampling.go b/plugins/sampling/sampling.go
index f5d789f..d125f2d 100644
--- a/plugins/sampling/sampling.go
+++ b/plugins/sampling/sampling.go
@@ -22,7 +22,7 @@ import (
"sync"
"time"
- "go.loglayer.dev/v2"
+ "go.loglayer.dev/v3"
)
// FixedRate keeps the given fraction of emissions and drops the rest.
diff --git a/plugins/sampling/sampling_test.go b/plugins/sampling/sampling_test.go
index 328899e..e219c47 100644
--- a/plugins/sampling/sampling_test.go
+++ b/plugins/sampling/sampling_test.go
@@ -5,9 +5,9 @@ import (
"testing"
"time"
- "go.loglayer.dev/plugins/plugintest/v2"
- "go.loglayer.dev/plugins/sampling/v2"
- "go.loglayer.dev/v2"
+ "go.loglayer.dev/plugins/plugintest/v3"
+ "go.loglayer.dev/plugins/sampling/v3"
+ "go.loglayer.dev/v3"
)
func TestFixedRate_KeepAll(t *testing.T) {
diff --git a/scripts/foreach-module.sh b/scripts/foreach-module.sh
index bbbb474..178dde4 100755
--- a/scripts/foreach-module.sh
+++ b/scripts/foreach-module.sh
@@ -106,18 +106,6 @@ SHIPPED_MODULES=(
integrations/sloghandler
)
-# Core-only mode: restrict every op to the root module. Used during a
-# major-version transition of the root (e.g. core moves to v3 while
-# sub-modules are still on v2), when sub-modules cannot build against
-# the unpublished root. Set CORE_ONLY=1 in CI for such PRs; unset once
-# the sweep PR moves the sub-modules onto the new root version.
-# TEST_MODULES is defined in the test branch (not here), so that branch
-# applies the CORE_ONLY override itself.
-if [ "${CORE_ONLY:-}" = "1" ]; then
- ALL_MODULES=(.)
- SHIPPED_MODULES=(.)
-fi
-
op="${1:-}"
if [ -z "$op" ]; then
cat >&2 <&2
exit 1
fi
- # plugins/datadogtrace/livetest is a separate test module that
- # imports the v2-core-dependent plugin; skip it in core-only mode
- # for the same reason the shipped modules are collapsed.
- if [ "${CORE_ONLY:-}" = "1" ]; then
- MODS=("${SHIPPED_MODULES[@]}")
- else
- MODS=("${SHIPPED_MODULES[@]}" plugins/datadogtrace/livetest)
- fi
- for mod in "${MODS[@]}"; do
+ for mod in "${SHIPPED_MODULES[@]}" plugins/datadogtrace/livetest; do
echo "==> $mod (staticcheck)"
(cd "$mod" && staticcheck ./...)
done
@@ -266,12 +241,7 @@ case "$op" in
echo "Install: go install golang.org/x/vuln/cmd/govulncheck@latest" >&2
exit 1
fi
- if [ "${CORE_ONLY:-}" = "1" ]; then
- MODS=("${SHIPPED_MODULES[@]}")
- else
- MODS=("${SHIPPED_MODULES[@]}" plugins/datadogtrace/livetest)
- fi
- for mod in "${MODS[@]}"; do
+ for mod in "${SHIPPED_MODULES[@]}" plugins/datadogtrace/livetest; do
echo "==> $mod (vuln)"
(cd "$mod" && govulncheck ./...)
done
diff --git a/transport/helpers.go b/transport/helpers.go
index 1e4ab3e..ef60c12 100644
--- a/transport/helpers.go
+++ b/transport/helpers.go
@@ -240,6 +240,12 @@ func AssembleMessage(messages []any, sanitize func(string) string) string {
if len(messages) == 0 {
return ""
}
+ // Fast path for the dominant single-string shape: no slice, no join.
+ if len(messages) == 1 {
+ if s, ok := messages[0].(string); ok {
+ return sanitize(s)
+ }
+ }
parts := make([]string, len(messages))
for i, m := range messages {
parts[i] = assembleElement(m, sanitize)
diff --git a/transports/axiom/axiom.go b/transports/axiom/axiom.go
index 6452b53..2307327 100644
--- a/transports/axiom/axiom.go
+++ b/transports/axiom/axiom.go
@@ -28,8 +28,8 @@ import (
"github.com/axiomhq/axiom-go/axiom"
- "go.loglayer.dev/v2"
- "go.loglayer.dev/v2/transport"
+ "go.loglayer.dev/v3"
+ "go.loglayer.dev/v3/transport"
)
// Config holds configuration options for the Axiom transport.
diff --git a/transports/axiom/axiom_test.go b/transports/axiom/axiom_test.go
index aa26fa3..b9c5102 100644
--- a/transports/axiom/axiom_test.go
+++ b/transports/axiom/axiom_test.go
@@ -6,8 +6,8 @@ import (
"github.com/axiomhq/axiom-go/axiom"
- "go.loglayer.dev/v2"
- "go.loglayer.dev/v2/transport"
+ "go.loglayer.dev/v3"
+ "go.loglayer.dev/v3/transport"
)
func TestBuild_NilClientReturnsError(t *testing.T) {
diff --git a/transports/axiom/example_test.go b/transports/axiom/example_test.go
index ed1da7a..9823423 100644
--- a/transports/axiom/example_test.go
+++ b/transports/axiom/example_test.go
@@ -1,7 +1,7 @@
package axiom
import (
- "go.loglayer.dev/v2"
+ "go.loglayer.dev/v3"
)
// ExampleNew shows a basic usage pattern with the Axiom transport. The client
diff --git a/transports/axiom/go.mod b/transports/axiom/go.mod
index 69b34b9..f2827cf 100644
--- a/transports/axiom/go.mod
+++ b/transports/axiom/go.mod
@@ -1,4 +1,4 @@
-module go.loglayer.dev/transports/axiom/v2
+module go.loglayer.dev/transports/axiom/v3
go 1.25.0
@@ -6,7 +6,7 @@ replace go.loglayer.dev => ../..
require (
github.com/axiomhq/axiom-go v0.32.0
- go.loglayer.dev/v2 v2.1.0
+ go.loglayer.dev/v3 v3.0.0
)
require (
@@ -22,5 +22,8 @@ require (
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.67.0 // indirect
go.opentelemetry.io/otel v1.43.0 // indirect
go.opentelemetry.io/otel/metric v1.43.0 // indirect
+ go.opentelemetry.io/otel/sdk/metric v1.43.0 // indirect
go.opentelemetry.io/otel/trace v1.43.0 // indirect
+ golang.org/x/sys v0.43.0 // indirect
+ google.golang.org/grpc v1.82.1 // indirect
)
diff --git a/transports/axiom/go.sum b/transports/axiom/go.sum
index 02003ff..735f0ac 100644
--- a/transports/axiom/go.sum
+++ b/transports/axiom/go.sum
@@ -40,8 +40,8 @@ github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4=
github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY=
github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28=
-go.loglayer.dev/v2 v2.1.0 h1:8/fq8Z1NNLtjfKgaPn6S0Hy7zWPnkjn9Gj3YgEDFk4w=
-go.loglayer.dev/v2 v2.1.0/go.mod h1:+BWhs5AyICvCLBz07qHnCE12W34tArUWfXOba0Ct/QI=
+go.loglayer.dev/v3 v3.0.0 h1:TEkgoAO21SAE0vKzrNa53G3VG88TJCKs/fMo177uY8s=
+go.loglayer.dev/v3 v3.0.0/go.mod h1:ghbgQp7Y/Vo4/jPCLiokMZTy2DrA3X8dfqtgcWhy3pg=
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.67.0 h1:OyrsyzuttWTSur2qN/Lm0m2a8yqyIjUVBZcxFPuXq2o=
@@ -56,26 +56,26 @@ go.opentelemetry.io/otel/metric v1.43.0 h1:d7638QeInOnuwOONPp4JAOGfbCEpYb+K6DVWv
go.opentelemetry.io/otel/metric v1.43.0/go.mod h1:RDnPtIxvqlgO8GRW18W6Z/4P462ldprJtfxHxyKd2PY=
go.opentelemetry.io/otel/sdk v1.43.0 h1:pi5mE86i5rTeLXqoF/hhiBtUNcrAGHLKQdhg4h4V9Dg=
go.opentelemetry.io/otel/sdk v1.43.0/go.mod h1:P+IkVU3iWukmiit/Yf9AWvpyRDlUeBaRg6Y+C58QHzg=
-go.opentelemetry.io/otel/sdk/metric v1.42.0 h1:D/1QR46Clz6ajyZ3G8SgNlTJKBdGp84q9RKCAZ3YGuA=
-go.opentelemetry.io/otel/sdk/metric v1.42.0/go.mod h1:Ua6AAlDKdZ7tdvaQKfSmnFTdHx37+J4ba8MwVCYM5hc=
+go.opentelemetry.io/otel/sdk/metric v1.43.0 h1:S88dyqXjJkuBNLeMcVPRFXpRw2fuwdvfCGLEo89fDkw=
+go.opentelemetry.io/otel/sdk/metric v1.43.0/go.mod h1:C/RJtwSEJ5hzTiUz5pXF1kILHStzb9zFlIEe85bhj6A=
go.opentelemetry.io/otel/trace v1.43.0 h1:BkNrHpup+4k4w+ZZ86CZoHHEkohws8AY+WTX09nk+3A=
go.opentelemetry.io/otel/trace v1.43.0/go.mod h1:/QJhyVBUUswCphDVxq+8mld+AvhXZLhe+8WVFxiFff0=
go.opentelemetry.io/proto/otlp v1.10.0 h1:IQRWgT5srOCYfiWnpqUYz9CVmbO8bFmKcwYxpuCSL2g=
go.opentelemetry.io/proto/otlp v1.10.0/go.mod h1:/CV4QoCR/S9yaPj8utp3lvQPoqMtxXdzn7ozvvozVqk=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
-golang.org/x/net v0.52.0 h1:He/TN1l0e4mmR3QqHMT2Xab3Aj3L9qjbhRm78/6jrW0=
-golang.org/x/net v0.52.0/go.mod h1:R1MAz7uMZxVMualyPXb+VaqGSa3LIaUqk0eEt3w36Sw=
-golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo=
-golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
-golang.org/x/text v0.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8=
-golang.org/x/text v0.35.0/go.mod h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA=
-google.golang.org/genproto/googleapis/api v0.0.0-20260401024825-9d38bb4040a9 h1:VPWxll4HlMw1Vs/qXtN7BvhZqsS9cdAittCNvVENElA=
-google.golang.org/genproto/googleapis/api v0.0.0-20260401024825-9d38bb4040a9/go.mod h1:7QBABkRtR8z+TEnmXTqIqwJLlzrZKVfAUm7tY3yGv0M=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20260401024825-9d38bb4040a9 h1:m8qni9SQFH0tJc1X0vmnpw/0t+AImlSvp30sEupozUg=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20260401024825-9d38bb4040a9/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
-google.golang.org/grpc v1.80.0 h1:Xr6m2WmWZLETvUNvIUmeD5OAagMw3FiKmMlTdViWsHM=
-google.golang.org/grpc v1.80.0/go.mod h1:ho/dLnxwi3EDJA4Zghp7k2Ec1+c2jqup0bFkw07bwF4=
+golang.org/x/net v0.53.0 h1:d+qAbo5L0orcWAr0a9JweQpjXF19LMXJE8Ey7hwOdUA=
+golang.org/x/net v0.53.0/go.mod h1:JvMuJH7rrdiCfbeHoo3fCQU24Lf5JJwT9W3sJFulfgs=
+golang.org/x/sys v0.43.0 h1:Rlag2XtaFTxp19wS8MXlJwTvoh8ArU6ezoyFsMyCTNI=
+golang.org/x/sys v0.43.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
+golang.org/x/text v0.36.0 h1:JfKh3XmcRPqZPKevfXVpI1wXPTqbkE5f7JA92a55Yxg=
+golang.org/x/text v0.36.0/go.mod h1:NIdBknypM8iqVmPiuco0Dh6P5Jcdk8lJL0CUebqK164=
+google.golang.org/genproto/googleapis/api v0.0.0-20260414002931-afd174a4e478 h1:yQugLulqltosq0B/f8l4w9VryjV+N/5gcW0jQ3N8Qec=
+google.golang.org/genproto/googleapis/api v0.0.0-20260414002931-afd174a4e478/go.mod h1:C6ADNqOxbgdUUeRTU+LCHDPB9ttAMCTff6auwCVa4uc=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20260414002931-afd174a4e478 h1:RmoJA1ujG+/lRGNfUnOMfhCy5EipVMyvUE+KNbPbTlw=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20260414002931-afd174a4e478/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
+google.golang.org/grpc v1.82.1 h1:NnAxzGRA0677vCa4BUkOAnO5+FfQqVl9iUXeD0IqcGE=
+google.golang.org/grpc v1.82.1/go.mod h1:yzTZ1TB1Z3SG+LIYaI+WiE8D5+PZ3ArnrSp8zF3+/ZA=
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
diff --git a/transports/axiom/livetest_test.go b/transports/axiom/livetest_test.go
index 0ea9aa1..09f34ab 100644
--- a/transports/axiom/livetest_test.go
+++ b/transports/axiom/livetest_test.go
@@ -18,8 +18,8 @@ import (
"testing"
"github.com/axiomhq/axiom-go/axiom"
- "go.loglayer.dev/v2/transport"
- "go.loglayer.dev/v2/transport/transporttest"
+ "go.loglayer.dev/v3/transport"
+ "go.loglayer.dev/v3/transport/transporttest"
)
func TestLive_Axiom_SendsLog(t *testing.T) {
diff --git a/transports/betterstack/README.md b/transports/betterstack/README.md
index 771f2e1..f2cfe53 100644
--- a/transports/betterstack/README.md
+++ b/transports/betterstack/README.md
@@ -5,7 +5,7 @@ Send structured logs to [Better Stack](https://betterstack.com) from your Go app
## Installation
```bash
-go get go.loglayer.dev/transports/betterstack
+go get go.loglayer.dev/transports/betterstack/v2
```
## Quick start
@@ -16,8 +16,8 @@ package main
import (
"log"
- "go.loglayer.dev/v2"
- bs "go.loglayer.dev/transports/betterstack"
+ "go.loglayer.dev/v3"
+ bs "go.loglayer.dev/transports/betterstack/v2"
)
func main() {
diff --git a/transports/betterstack/betterstack.go b/transports/betterstack/betterstack.go
index 43a07d8..4f3fac3 100644
--- a/transports/betterstack/betterstack.go
+++ b/transports/betterstack/betterstack.go
@@ -18,9 +18,9 @@ import (
"github.com/goccy/go-json"
- httptr "go.loglayer.dev/transports/http/v2"
- "go.loglayer.dev/v2"
- "go.loglayer.dev/v2/transport"
+ httptr "go.loglayer.dev/transports/http/v3"
+ "go.loglayer.dev/v3"
+ "go.loglayer.dev/v3/transport"
)
// Config holds Better Stack transport configuration.
diff --git a/transports/betterstack/betterstack_test.go b/transports/betterstack/betterstack_test.go
index 0b35ed8..c5bb8cf 100644
--- a/transports/betterstack/betterstack_test.go
+++ b/transports/betterstack/betterstack_test.go
@@ -11,8 +11,8 @@ import (
"testing"
"time"
- httptr "go.loglayer.dev/transports/http/v2"
- "go.loglayer.dev/v2"
+ httptr "go.loglayer.dev/transports/http/v3"
+ "go.loglayer.dev/v3"
)
type capture struct {
@@ -169,8 +169,9 @@ func TestEncoder_Shape(t *testing.T) {
if obj["userId"] != "123" {
t.Errorf("userId: got %v", obj["userId"])
}
- if obj["traceId"] != "abc" {
- t.Errorf("traceId: got %v", obj["traceId"])
+ // The metadata map nests under "metadata" per the v3 core default.
+ if obj["metadata"].(map[string]any)["traceId"] != "abc" {
+ t.Errorf("metadata.traceId: got %v", obj["metadata"])
}
}
diff --git a/transports/betterstack/example_test.go b/transports/betterstack/example_test.go
index 5d94a24..fdc5f4d 100644
--- a/transports/betterstack/example_test.go
+++ b/transports/betterstack/example_test.go
@@ -1,7 +1,7 @@
package betterstack
import (
- "go.loglayer.dev/v2"
+ "go.loglayer.dev/v3"
)
// ExampleNew shows how to create a Better Stack transport and ship logs.
diff --git a/transports/betterstack/go.mod b/transports/betterstack/go.mod
index 48d0409..5e7e7c7 100644
--- a/transports/betterstack/go.mod
+++ b/transports/betterstack/go.mod
@@ -1,13 +1,13 @@
-module go.loglayer.dev/transports/betterstack
+module go.loglayer.dev/transports/betterstack/v2
go 1.25.0
require (
github.com/goccy/go-json v0.10.6
- go.loglayer.dev/transports/http/v2 v2.1.0
- go.loglayer.dev/v2 v2.1.0
+ go.loglayer.dev/transports/http/v3 v3.0.0-00010101000000-000000000000
+ go.loglayer.dev/v3 v3.0.0
)
replace go.loglayer.dev => ../..
-replace go.loglayer.dev/transports/http => ../http
+replace go.loglayer.dev/transports/http/v3 => ../http
diff --git a/transports/betterstack/go.sum b/transports/betterstack/go.sum
index 512eb95..42ec334 100644
--- a/transports/betterstack/go.sum
+++ b/transports/betterstack/go.sum
@@ -1,8 +1,6 @@
github.com/goccy/go-json v0.10.6 h1:p8HrPJzOakx/mn/bQtjgNjdTcN+/S6FcG2CTtQOrHVU=
github.com/goccy/go-json v0.10.6/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
-go.loglayer.dev/transports/http/v2 v2.1.0 h1:+9kaawgxkbFYKvIliTWwSjj9WKUnG9Uzjm3dRmuCkRY=
-go.loglayer.dev/transports/http/v2 v2.1.0/go.mod h1:OeIaQoUHcT3Qeb8HI8CaY4OFhQwwpC/Pb0yXGTrHxIM=
-go.loglayer.dev/v2 v2.1.0 h1:8/fq8Z1NNLtjfKgaPn6S0Hy7zWPnkjn9Gj3YgEDFk4w=
-go.loglayer.dev/v2 v2.1.0/go.mod h1:+BWhs5AyICvCLBz07qHnCE12W34tArUWfXOba0Ct/QI=
+go.loglayer.dev/v3 v3.0.0 h1:TEkgoAO21SAE0vKzrNa53G3VG88TJCKs/fMo177uY8s=
+go.loglayer.dev/v3 v3.0.0/go.mod h1:ghbgQp7Y/Vo4/jPCLiokMZTy2DrA3X8dfqtgcWhy3pg=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
diff --git a/transports/betterstack/livetest_test.go b/transports/betterstack/livetest_test.go
index f17706e..6c8f576 100644
--- a/transports/betterstack/livetest_test.go
+++ b/transports/betterstack/livetest_test.go
@@ -27,10 +27,10 @@ import (
"testing"
"time"
- betterstack "go.loglayer.dev/transports/betterstack"
- httptr "go.loglayer.dev/transports/http/v2"
- "go.loglayer.dev/v2/transport/transporttest"
- "go.loglayer.dev/v2/utils/idgen"
+ betterstack "go.loglayer.dev/transports/betterstack/v2"
+ httptr "go.loglayer.dev/transports/http/v3"
+ "go.loglayer.dev/v3/transport/transporttest"
+ "go.loglayer.dev/v3/utils/idgen"
)
func TestLive_BetterStack_SendsLog(t *testing.T) {
diff --git a/transports/blank/blank.go b/transports/blank/blank.go
index e553456..6220b50 100644
--- a/transports/blank/blank.go
+++ b/transports/blank/blank.go
@@ -12,8 +12,8 @@
package blank
import (
- "go.loglayer.dev/v2"
- "go.loglayer.dev/v2/transport"
+ "go.loglayer.dev/v3"
+ "go.loglayer.dev/v3/transport"
)
// Config holds configuration for the blank transport.
diff --git a/transports/blank/blank_test.go b/transports/blank/blank_test.go
index b504d0b..dc5e15e 100644
--- a/transports/blank/blank_test.go
+++ b/transports/blank/blank_test.go
@@ -4,9 +4,9 @@ import (
"sync/atomic"
"testing"
- "go.loglayer.dev/transports/blank/v2"
- "go.loglayer.dev/v2"
- "go.loglayer.dev/v2/transport"
+ "go.loglayer.dev/transports/blank/v3"
+ "go.loglayer.dev/v3"
+ "go.loglayer.dev/v3/transport"
)
func TestBlank_CallsShipToLogger(t *testing.T) {
diff --git a/transports/blank/example_test.go b/transports/blank/example_test.go
index 3538be5..91ee565 100644
--- a/transports/blank/example_test.go
+++ b/transports/blank/example_test.go
@@ -3,8 +3,8 @@ package blank_test
import (
"fmt"
- "go.loglayer.dev/transports/blank/v2"
- "go.loglayer.dev/v2"
+ "go.loglayer.dev/transports/blank/v3"
+ "go.loglayer.dev/v3"
)
// New wraps a callback. ShipToLogger receives every TransportParams
diff --git a/transports/blank/go.mod b/transports/blank/go.mod
index 62ef48a..5e7d7ea 100644
--- a/transports/blank/go.mod
+++ b/transports/blank/go.mod
@@ -1,7 +1,7 @@
-module go.loglayer.dev/transports/blank/v2
+module go.loglayer.dev/transports/blank/v3
go 1.25.0
-require go.loglayer.dev/v2 v2.0.1
+require go.loglayer.dev/v3 v3.0.0
require github.com/goccy/go-json v0.10.6 // indirect
diff --git a/transports/blank/go.sum b/transports/blank/go.sum
index c00692c..42ec334 100644
--- a/transports/blank/go.sum
+++ b/transports/blank/go.sum
@@ -1,6 +1,6 @@
github.com/goccy/go-json v0.10.6 h1:p8HrPJzOakx/mn/bQtjgNjdTcN+/S6FcG2CTtQOrHVU=
github.com/goccy/go-json v0.10.6/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
-go.loglayer.dev/v2 v2.0.1 h1:B7oYpkfMky0UG/N8IdKeIiiTi6h7eyj5NvRyEth9DHI=
-go.loglayer.dev/v2 v2.0.1/go.mod h1:+BWhs5AyICvCLBz07qHnCE12W34tArUWfXOba0Ct/QI=
+go.loglayer.dev/v3 v3.0.0 h1:TEkgoAO21SAE0vKzrNa53G3VG88TJCKs/fMo177uY8s=
+go.loglayer.dev/v3 v3.0.0/go.mod h1:ghbgQp7Y/Vo4/jPCLiokMZTy2DrA3X8dfqtgcWhy3pg=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
diff --git a/transports/central/central.go b/transports/central/central.go
index 04c8867..1a816bf 100644
--- a/transports/central/central.go
+++ b/transports/central/central.go
@@ -15,8 +15,8 @@ import (
"github.com/goccy/go-json"
- httptr "go.loglayer.dev/transports/http/v2"
- "go.loglayer.dev/v2/transport"
+ httptr "go.loglayer.dev/transports/http/v3"
+ "go.loglayer.dev/v3/transport"
)
// DefaultPort is the port the Central server listens on by default.
diff --git a/transports/central/central_test.go b/transports/central/central_test.go
index 98f4449..f1ca87d 100644
--- a/transports/central/central_test.go
+++ b/transports/central/central_test.go
@@ -11,9 +11,9 @@ import (
"time"
"go.loglayer.dev/transports/central/v2"
- httptr "go.loglayer.dev/transports/http/v2"
- "go.loglayer.dev/v2"
- "go.loglayer.dev/v2/transport"
+ httptr "go.loglayer.dev/transports/http/v3"
+ "go.loglayer.dev/v3"
+ "go.loglayer.dev/v3/transport"
)
type capture struct {
diff --git a/transports/central/example_test.go b/transports/central/example_test.go
index 89081b0..106917f 100644
--- a/transports/central/example_test.go
+++ b/transports/central/example_test.go
@@ -2,7 +2,7 @@ package central_test
import (
"go.loglayer.dev/transports/central/v2"
- "go.loglayer.dev/v2"
+ "go.loglayer.dev/v3"
)
// New ships log entries to the LogLayer Central server's HTTP intake.
diff --git a/transports/central/go.mod b/transports/central/go.mod
index b461b50..1176a1f 100644
--- a/transports/central/go.mod
+++ b/transports/central/go.mod
@@ -2,13 +2,13 @@ module go.loglayer.dev/transports/central/v2
go 1.25.0
-replace go.loglayer.dev/v2 => ../..
+replace go.loglayer.dev/v3 => ../..
-replace go.loglayer.dev/transports/http/v2 => ../http
+replace go.loglayer.dev/transports/http/v3 => ../http
require (
github.com/goccy/go-json v0.10.6
- go.loglayer.dev/transports/http/v2 v2.0.0-00010101000000-000000000000
- go.loglayer.dev/v2 v2.0.1
+ go.loglayer.dev/transports/http/v3 v3.0.0-00010101000000-000000000000
+ go.loglayer.dev/v3 v3.0.0
go.uber.org/goleak v1.3.0
)
diff --git a/transports/charmlog/bench_test.go b/transports/charmlog/bench_test.go
index 1903661..36dce5e 100644
--- a/transports/charmlog/bench_test.go
+++ b/transports/charmlog/bench_test.go
@@ -5,10 +5,10 @@ import (
clog "github.com/charmbracelet/log"
- llcharm "go.loglayer.dev/transports/charmlog/v2"
- "go.loglayer.dev/v2"
- "go.loglayer.dev/v2/transport"
- "go.loglayer.dev/v2/transport/benchtest"
+ llcharm "go.loglayer.dev/transports/charmlog/v3"
+ "go.loglayer.dev/v3"
+ "go.loglayer.dev/v3/transport"
+ "go.loglayer.dev/v3/transport/benchtest"
)
func newDirect() *clog.Logger {
diff --git a/transports/charmlog/charmlog.go b/transports/charmlog/charmlog.go
index a8cb5b6..fb7884d 100644
--- a/transports/charmlog/charmlog.go
+++ b/transports/charmlog/charmlog.go
@@ -8,8 +8,8 @@ import (
clog "github.com/charmbracelet/log"
- "go.loglayer.dev/v2"
- "go.loglayer.dev/v2/transport"
+ "go.loglayer.dev/v3"
+ "go.loglayer.dev/v3/transport"
)
// Config holds configuration options for the charmbracelet/log transport.
diff --git a/transports/charmlog/charmlog_test.go b/transports/charmlog/charmlog_test.go
index 7013e6b..7249e9d 100644
--- a/transports/charmlog/charmlog_test.go
+++ b/transports/charmlog/charmlog_test.go
@@ -6,10 +6,10 @@ import (
clog "github.com/charmbracelet/log"
- llcharm "go.loglayer.dev/transports/charmlog/v2"
- "go.loglayer.dev/v2"
- "go.loglayer.dev/v2/transport"
- "go.loglayer.dev/v2/transport/transporttest"
+ llcharm "go.loglayer.dev/transports/charmlog/v3"
+ "go.loglayer.dev/v3"
+ "go.loglayer.dev/v3/transport"
+ "go.loglayer.dev/v3/transport/transporttest"
)
func factory(opts transporttest.FactoryOpts) (*loglayer.LogLayer, *bytes.Buffer) {
diff --git a/transports/charmlog/example_test.go b/transports/charmlog/example_test.go
index 9a2cf6c..3150a8b 100644
--- a/transports/charmlog/example_test.go
+++ b/transports/charmlog/example_test.go
@@ -3,8 +3,8 @@ package charmlog_test
import (
"io"
- "go.loglayer.dev/transports/charmlog/v2"
- "go.loglayer.dev/v2"
+ "go.loglayer.dev/transports/charmlog/v3"
+ "go.loglayer.dev/v3"
)
// New wraps a *charmbracelet/log.Logger. When Logger is nil a default
diff --git a/transports/charmlog/go.mod b/transports/charmlog/go.mod
index 6334a27..d0886fb 100644
--- a/transports/charmlog/go.mod
+++ b/transports/charmlog/go.mod
@@ -1,17 +1,17 @@
-module go.loglayer.dev/transports/charmlog/v2
+module go.loglayer.dev/transports/charmlog/v3
go 1.25.0
require (
github.com/charmbracelet/log v1.0.0
- go.loglayer.dev/v2 v2.0.1
+ go.loglayer.dev/v3 v3.0.0
)
require (
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc // indirect
github.com/charmbracelet/lipgloss v1.1.0 // indirect
- github.com/charmbracelet/x/ansi v0.8.0 // indirect
+ github.com/charmbracelet/x/ansi v0.10.1 // indirect
github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd // indirect
github.com/charmbracelet/x/term v0.2.1 // indirect
github.com/go-logfmt/logfmt v0.6.1 // indirect
@@ -23,5 +23,5 @@ require (
github.com/rivo/uniseg v0.4.7 // indirect
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
golang.org/x/exp v0.0.0-20260209203927-2842357ff358 // indirect
- golang.org/x/sys v0.42.0 // indirect
+ golang.org/x/sys v0.43.0 // indirect
)
diff --git a/transports/charmlog/go.sum b/transports/charmlog/go.sum
index e8eefda..509fd25 100644
--- a/transports/charmlog/go.sum
+++ b/transports/charmlog/go.sum
@@ -6,8 +6,8 @@ github.com/charmbracelet/lipgloss v1.1.0 h1:vYXsiLHVkK7fp74RkV7b2kq9+zDLoEU4MZoF
github.com/charmbracelet/lipgloss v1.1.0/go.mod h1:/6Q8FR2o+kj8rz4Dq0zQc3vYf7X+B0binUUBwA0aL30=
github.com/charmbracelet/log v1.0.0 h1:HVVVMmfOorfj3BA9i8X8UL69Hoz9lI0PYwXfJvOdRc4=
github.com/charmbracelet/log v1.0.0/go.mod h1:uYgY3SmLpwJWxmlrPwXvzVYujxis1vAKRV/0VQB7yWA=
-github.com/charmbracelet/x/ansi v0.8.0 h1:9GTq3xq9caJW8ZrBTe0LIe2fvfLR/bYXKTx2llXn7xE=
-github.com/charmbracelet/x/ansi v0.8.0/go.mod h1:wdYl/ONOLHLIVmQaxbIYEC/cRKOQyjTkowiI4blgS9Q=
+github.com/charmbracelet/x/ansi v0.10.1 h1:rL3Koar5XvX0pHGfovN03f5cxLbCF2YvLeyz7D2jVDQ=
+github.com/charmbracelet/x/ansi v0.10.1/go.mod h1:3RQDQ6lDnROptfpWuUVIUG64bD2g2BgntdxH0Ya5TeE=
github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd h1:vy0GVL4jeHEwG5YOXDmi86oYw2yuYUGqz6a8sLwg0X8=
github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd/go.mod h1:xe0nKWGd3eJgtqZRaN9RjMtK7xUYchjzPr7q6kcvCCs=
github.com/charmbracelet/x/term v0.2.1 h1:AQeHeLZ1OqSXhrAWpYUtZyX1T3zVxfpZuEQMIQaGIAQ=
@@ -37,14 +37,14 @@ github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
-go.loglayer.dev/v2 v2.0.1 h1:B7oYpkfMky0UG/N8IdKeIiiTi6h7eyj5NvRyEth9DHI=
-go.loglayer.dev/v2 v2.0.1/go.mod h1:+BWhs5AyICvCLBz07qHnCE12W34tArUWfXOba0Ct/QI=
+go.loglayer.dev/v3 v3.0.0 h1:TEkgoAO21SAE0vKzrNa53G3VG88TJCKs/fMo177uY8s=
+go.loglayer.dev/v3 v3.0.0/go.mod h1:ghbgQp7Y/Vo4/jPCLiokMZTy2DrA3X8dfqtgcWhy3pg=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
golang.org/x/exp v0.0.0-20260209203927-2842357ff358 h1:kpfSV7uLwKJbFSEgNhWzGSL47NDSF/5pYYQw1V0ub6c=
golang.org/x/exp v0.0.0-20260209203927-2842357ff358/go.mod h1:R3t0oliuryB5eenPWl3rrQxwnNM3WTwnsRZZiXLAAW8=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo=
-golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
+golang.org/x/sys v0.43.0 h1:Rlag2XtaFTxp19wS8MXlJwTvoh8ArU6ezoyFsMyCTNI=
+golang.org/x/sys v0.43.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
diff --git a/transports/cli/cli.go b/transports/cli/cli.go
index 7ace7da..af29b91 100644
--- a/transports/cli/cli.go
+++ b/transports/cli/cli.go
@@ -2,7 +2,7 @@
// output rather than diagnostic logging.
//
// What makes it different from the other terminal-shaped transports
-// ([go.loglayer.dev/transports/console/v2], [go.loglayer.dev/transports/pretty/v2]):
+// ([go.loglayer.dev/transports/console/v3], [go.loglayer.dev/transports/pretty/v3]):
//
// - No timestamp, no log-id, no level label embedded in info/debug
// output. The message string is printed as-is.
@@ -23,18 +23,18 @@
// What this transport is NOT:
//
// - A diagnostic logger. If you want timestamps and structured
-// fields, use [go.loglayer.dev/transports/console/v2] or
-// [go.loglayer.dev/transports/pretty/v2].
+// fields, use [go.loglayer.dev/transports/console/v3] or
+// [go.loglayer.dev/transports/pretty/v3].
// - A JSON formatter. Pair this transport with a swap to
-// [go.loglayer.dev/transports/structured/v2] when the CLI's
+// [go.loglayer.dev/transports/structured/v3] when the CLI's
// `--json` flag is set.
//
// Recommended plugin pairings:
//
-// - [go.loglayer.dev/plugins/fmtlog/v2] for fmt.Sprintf-style format
+// - [go.loglayer.dev/plugins/fmtlog/v3] for fmt.Sprintf-style format
// strings (`log.Info("Applied %d release(s) at %s:", n, sha)`).
// CLI output almost always wants format-string semantics.
-// - [go.loglayer.dev/plugins/redact/v2] when log values may include
+// - [go.loglayer.dev/plugins/redact/v3] when log values may include
// tokens or other secrets that shouldn't reach stdout / stderr.
//
// See https://go.loglayer.dev for usage guides and the full API
@@ -54,9 +54,9 @@ import (
"github.com/fatih/color"
"github.com/mattn/go-isatty"
- "go.loglayer.dev/v2"
- "go.loglayer.dev/v2/transport"
- "go.loglayer.dev/v2/utils/sanitize"
+ "go.loglayer.dev/v3"
+ "go.loglayer.dev/v3/transport"
+ "go.loglayer.dev/v3/utils/sanitize"
)
// ColorMode controls ANSI color output.
diff --git a/transports/cli/cli_test.go b/transports/cli/cli_test.go
index 660fd0f..153b6a6 100644
--- a/transports/cli/cli_test.go
+++ b/transports/cli/cli_test.go
@@ -12,8 +12,8 @@ import (
"github.com/fatih/color"
"golang.org/x/sys/unix"
- clitr "go.loglayer.dev/transports/cli/v2"
- "go.loglayer.dev/v2"
+ clitr "go.loglayer.dev/transports/cli/v3"
+ "go.loglayer.dev/v3"
)
// openPTY opens a new pseudo-terminal pair and returns the slave end (a
@@ -178,7 +178,7 @@ func TestShowFieldsAppendsLogfmt(t *testing.T) {
Info("done")
got := strings.TrimRight(stdout.String(), "\n")
- for _, want := range []string{"done", "requestID=abc-123", "user=alice"} {
+ for _, want := range []string{"done", "requestID=abc-123", "metadata=map[user:alice]"} {
if !strings.Contains(got, want) {
t.Errorf("output %q missing %q", got, want)
}
@@ -191,7 +191,7 @@ func TestShowFieldsQuotesValuesWithSpaces(t *testing.T) {
log.WithMetadata(loglayer.Metadata{"path": "/var/log/my app"}).Info("ok")
got := strings.TrimRight(stdout.String(), "\n")
- if !strings.Contains(got, `path="/var/log/my app"`) {
+ if !strings.Contains(got, `metadata="map[path:/var/log/my app]"`) {
t.Errorf("quoted value missing: %q", got)
}
}
diff --git a/transports/cli/example_test.go b/transports/cli/example_test.go
index 6b3b8dd..a2b3db9 100644
--- a/transports/cli/example_test.go
+++ b/transports/cli/example_test.go
@@ -5,8 +5,8 @@ import (
"github.com/fatih/color"
- clitr "go.loglayer.dev/transports/cli/v2"
- "go.loglayer.dev/v2"
+ clitr "go.loglayer.dev/transports/cli/v3"
+ "go.loglayer.dev/v3"
)
// The package-level Example shows the canonical wiring: a CLI app's
@@ -63,7 +63,7 @@ func ExampleConfig_ShowFields() {
log.WithFields(loglayer.Fields{"requestID": "abc-123"}).
WithMetadata(loglayer.Metadata{"latencyMs": 42}).
Info("handled")
- // Output: handled latencyMs=42 requestID=abc-123
+ // Output: handled metadata=map[latencyMs:42] requestID=abc-123
}
// Example_table demonstrates the table rendering mode: when
diff --git a/transports/cli/go.mod b/transports/cli/go.mod
index 989f42a..e9edf40 100644
--- a/transports/cli/go.mod
+++ b/transports/cli/go.mod
@@ -1,12 +1,12 @@
-module go.loglayer.dev/transports/cli/v2
+module go.loglayer.dev/transports/cli/v3
go 1.25.0
require (
github.com/fatih/color v1.19.0
github.com/mattn/go-isatty v0.0.20
- go.loglayer.dev/v2 v2.2.0
- golang.org/x/sys v0.42.0
+ go.loglayer.dev/v3 v3.0.0
+ golang.org/x/sys v0.43.0
)
require (
diff --git a/transports/cli/go.sum b/transports/cli/go.sum
index 44e3a3f..1252d85 100644
--- a/transports/cli/go.sum
+++ b/transports/cli/go.sum
@@ -6,10 +6,10 @@ github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHP
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
-go.loglayer.dev/v2 v2.2.0 h1:m8sgblBOtMrhbvcxETYKP+9S/K0CbCwva/Z3eflJ0ls=
-go.loglayer.dev/v2 v2.2.0/go.mod h1:+BWhs5AyICvCLBz07qHnCE12W34tArUWfXOba0Ct/QI=
+go.loglayer.dev/v3 v3.0.0 h1:TEkgoAO21SAE0vKzrNa53G3VG88TJCKs/fMo177uY8s=
+go.loglayer.dev/v3 v3.0.0/go.mod h1:ghbgQp7Y/Vo4/jPCLiokMZTy2DrA3X8dfqtgcWhy3pg=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo=
-golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
+golang.org/x/sys v0.43.0 h1:Rlag2XtaFTxp19wS8MXlJwTvoh8ArU6ezoyFsMyCTNI=
+golang.org/x/sys v0.43.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
diff --git a/transports/console/bench_test.go b/transports/console/bench_test.go
index b39f1ad..a958b5f 100644
--- a/transports/console/bench_test.go
+++ b/transports/console/bench_test.go
@@ -7,10 +7,10 @@ package console_test
import (
"testing"
- "go.loglayer.dev/transports/console/v2"
- "go.loglayer.dev/v2"
- "go.loglayer.dev/v2/transport"
- "go.loglayer.dev/v2/transport/benchtest"
+ "go.loglayer.dev/transports/console/v3"
+ "go.loglayer.dev/v3"
+ "go.loglayer.dev/v3/transport"
+ "go.loglayer.dev/v3/transport/benchtest"
)
func BenchmarkRender_Console_SimpleMessage(b *testing.B) {
diff --git a/transports/console/console.go b/transports/console/console.go
index 3b77254..b16f5a2 100644
--- a/transports/console/console.go
+++ b/transports/console/console.go
@@ -16,9 +16,9 @@ import (
"github.com/goccy/go-json"
- "go.loglayer.dev/v2"
- "go.loglayer.dev/v2/transport"
- "go.loglayer.dev/v2/utils/sanitize"
+ "go.loglayer.dev/v3"
+ "go.loglayer.dev/v3/transport"
+ "go.loglayer.dev/v3/utils/sanitize"
)
// Config holds configuration options for Transport.
diff --git a/transports/console/console_test.go b/transports/console/console_test.go
index 265ff9a..0f518ab 100644
--- a/transports/console/console_test.go
+++ b/transports/console/console_test.go
@@ -6,9 +6,9 @@ import (
"strings"
"testing"
- "go.loglayer.dev/transports/console/v2"
- "go.loglayer.dev/v2"
- "go.loglayer.dev/v2/transport"
+ "go.loglayer.dev/transports/console/v3"
+ "go.loglayer.dev/v3"
+ "go.loglayer.dev/v3/transport"
)
func newLogger(cfg console.Config) (*loglayer.LogLayer, *bytes.Buffer) {
@@ -55,20 +55,10 @@ func TestConsoleLogfmtOutput(t *testing.T) {
if !strings.HasPrefix(out, "event ") {
t.Errorf("expected message before fields, got: %q", out)
}
- if !strings.Contains(out, "id=42") {
- t.Errorf("expected unquoted scalar 'id=42', got: %q", out)
- }
- if !strings.Contains(out, "name=alice") {
- t.Errorf("expected unquoted 'name=alice', got: %q", out)
- }
- if !strings.Contains(out, `"with s"="value with space"`) {
- t.Errorf("expected quoted key + value with space, got: %q", out)
- }
- // Sorted: id < name < with s.
- idIdx := strings.Index(out, "id=")
- nameIdx := strings.Index(out, "name=")
- if !(idIdx < nameIdx) {
- t.Errorf("expected sorted keys (id before name), got: %q", out)
+ // Metadata nests under the "metadata" key as a JSON-encoded value
+ // (sorted keys) per the v3 core's default assembly shape.
+ if !strings.Contains(out, `metadata="{\"id\":42,\"name\":\"alice\",\"with s\":\"value with space\"}"`) {
+ t.Errorf("expected JSON-encoded metadata value, got: %q", out)
}
}
@@ -78,8 +68,9 @@ func TestConsoleLogfmtNestedJSON(t *testing.T) {
"nested": map[string]any{"a": 1, "b": "two"},
}).Info("event")
out := strings.TrimSpace(buf.String())
- // Nested values are JSON-encoded and string-quoted.
- if !strings.Contains(out, `nested="{`) {
+ // The metadata map nests under "metadata" as one JSON-encoded value;
+ // non-scalar values inside are JSON-encoded.
+ if !strings.Contains(out, `metadata="{\"nested\":{\"a\":1,\"b\":\"two\"}}"`) {
t.Errorf("expected JSON-encoded nested value, got: %q", out)
}
}
diff --git a/transports/console/example_test.go b/transports/console/example_test.go
index a642282..7292d27 100644
--- a/transports/console/example_test.go
+++ b/transports/console/example_test.go
@@ -3,8 +3,8 @@ package console_test
import (
"os"
- "go.loglayer.dev/transports/console/v2"
- "go.loglayer.dev/v2"
+ "go.loglayer.dev/transports/console/v3"
+ "go.loglayer.dev/v3"
)
// New builds a logfmt-style console transport. DateFn returns a fixed
@@ -21,5 +21,5 @@ func ExampleNew() {
DisableFatalExit: true,
})
log.WithMetadata(loglayer.Metadata{"durationMs": 42}).Info("served")
- // Output: served durationMs=42 level=info time=2026-04-26T12:00:00Z
+ // Output: served level=info metadata="{\"durationMs\":42}" time=2026-04-26T12:00:00Z
}
diff --git a/transports/console/go.mod b/transports/console/go.mod
index 425d829..8e57bd4 100644
--- a/transports/console/go.mod
+++ b/transports/console/go.mod
@@ -1,8 +1,8 @@
-module go.loglayer.dev/transports/console/v2
+module go.loglayer.dev/transports/console/v3
go 1.25.0
require (
github.com/goccy/go-json v0.10.6
- go.loglayer.dev/v2 v2.1.0
+ go.loglayer.dev/v3 v3.0.0
)
diff --git a/transports/console/go.sum b/transports/console/go.sum
index e9e294d..42ec334 100644
--- a/transports/console/go.sum
+++ b/transports/console/go.sum
@@ -1,6 +1,6 @@
github.com/goccy/go-json v0.10.6 h1:p8HrPJzOakx/mn/bQtjgNjdTcN+/S6FcG2CTtQOrHVU=
github.com/goccy/go-json v0.10.6/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
-go.loglayer.dev/v2 v2.1.0 h1:8/fq8Z1NNLtjfKgaPn6S0Hy7zWPnkjn9Gj3YgEDFk4w=
-go.loglayer.dev/v2 v2.1.0/go.mod h1:+BWhs5AyICvCLBz07qHnCE12W34tArUWfXOba0Ct/QI=
+go.loglayer.dev/v3 v3.0.0 h1:TEkgoAO21SAE0vKzrNa53G3VG88TJCKs/fMo177uY8s=
+go.loglayer.dev/v3 v3.0.0/go.mod h1:ghbgQp7Y/Vo4/jPCLiokMZTy2DrA3X8dfqtgcWhy3pg=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
diff --git a/transports/console/routing_test.go b/transports/console/routing_test.go
index 8b2124a..4acc571 100644
--- a/transports/console/routing_test.go
+++ b/transports/console/routing_test.go
@@ -5,7 +5,7 @@ import (
"os"
"testing"
- "go.loglayer.dev/v2"
+ "go.loglayer.dev/v3"
)
func TestWriterRouting(t *testing.T) {
diff --git a/transports/datadog/README.md b/transports/datadog/README.md
index 1781239..3cd8691 100644
--- a/transports/datadog/README.md
+++ b/transports/datadog/README.md
@@ -1,13 +1,13 @@
# go.loglayer.dev/transports/datadog
-[](https://pkg.go.dev/go.loglayer.dev/transports/datadog/v2)
+[](https://pkg.go.dev/go.loglayer.dev/transports/datadog/v3)
Datadog Logs HTTP intake transport for LogLayer. Built on `transports/http` with a Datadog-specific encoder, site-aware URL, and `DD-API-KEY` header. Rejects non-https URLs by default; opt-in via `Config.AllowInsecureURL` for on-prem TLS-terminating proxies. The API key is redacted in `String()` output and tagged `json:"-"` to keep it out of accidental config dumps.
## Install
```sh
-go get go.loglayer.dev/transports/datadog
+go get go.loglayer.dev/transports/datadog/v3
```
## Documentation
diff --git a/transports/datadog/datadog.go b/transports/datadog/datadog.go
index 6c42271..cfe8b09 100644
--- a/transports/datadog/datadog.go
+++ b/transports/datadog/datadog.go
@@ -19,9 +19,9 @@ import (
"github.com/goccy/go-json"
- httptr "go.loglayer.dev/transports/http/v2"
- "go.loglayer.dev/v2"
- "go.loglayer.dev/v2/transport"
+ httptr "go.loglayer.dev/transports/http/v3"
+ "go.loglayer.dev/v3"
+ "go.loglayer.dev/v3/transport"
)
// Site identifies the Datadog region. Affects only the intake URL.
diff --git a/transports/datadog/datadog_test.go b/transports/datadog/datadog_test.go
index 2dc940f..2205949 100644
--- a/transports/datadog/datadog_test.go
+++ b/transports/datadog/datadog_test.go
@@ -13,10 +13,10 @@ import (
"testing"
"time"
- "go.loglayer.dev/transports/datadog/v2"
- httptr "go.loglayer.dev/transports/http/v2"
- "go.loglayer.dev/v2"
- "go.loglayer.dev/v2/transport"
+ "go.loglayer.dev/transports/datadog/v3"
+ httptr "go.loglayer.dev/transports/http/v3"
+ "go.loglayer.dev/v3"
+ "go.loglayer.dev/v3/transport"
)
type capture struct {
@@ -99,8 +99,10 @@ func TestDatadog_RealEncoderShape(t *testing.T) {
t.Errorf("entry[%q]: got %v, want %v", k, obj[k], v)
}
}
- if obj["durationMs"] != float64(42) {
- t.Errorf("durationMs: got %v", obj["durationMs"])
+ // The metadata map nests under "metadata" per the v3 core default.
+ md, ok := obj["metadata"].(map[string]any)
+ if !ok || md["durationMs"] != float64(42) {
+ t.Errorf("metadata.durationMs: got %v", obj["metadata"])
}
if _, hasDate := obj["date"]; !hasDate {
t.Errorf("expected date field, got %v", obj)
diff --git a/transports/datadog/example_test.go b/transports/datadog/example_test.go
index 2bb7ca7..4b4dbc0 100644
--- a/transports/datadog/example_test.go
+++ b/transports/datadog/example_test.go
@@ -1,8 +1,8 @@
package datadog_test
import (
- "go.loglayer.dev/transports/datadog/v2"
- "go.loglayer.dev/v2"
+ "go.loglayer.dev/transports/datadog/v3"
+ "go.loglayer.dev/v3"
)
// New ships log entries to the Datadog Logs HTTP intake. APIKey is
diff --git a/transports/datadog/go.mod b/transports/datadog/go.mod
index e06a308..5340b5b 100644
--- a/transports/datadog/go.mod
+++ b/transports/datadog/go.mod
@@ -1,10 +1,12 @@
-module go.loglayer.dev/transports/datadog/v2
+module go.loglayer.dev/transports/datadog/v3
go 1.25.0
require (
github.com/goccy/go-json v0.10.6
- go.loglayer.dev/transports/http/v2 v2.0.1
- go.loglayer.dev/v2 v2.0.1
+ go.loglayer.dev/transports/http/v3 v3.0.0-00010101000000-000000000000
+ go.loglayer.dev/v3 v3.0.0
go.uber.org/goleak v1.3.0
)
+
+replace go.loglayer.dev/transports/http/v3 => ../http
diff --git a/transports/datadog/go.sum b/transports/datadog/go.sum
index 84d822b..39cf249 100644
--- a/transports/datadog/go.sum
+++ b/transports/datadog/go.sum
@@ -6,10 +6,8 @@ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRI
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
-go.loglayer.dev/transports/http/v2 v2.0.1 h1:QMKdVMDQ16IQiDSvxm7bKNQeW6a6XFQxGvjTcpHa6nk=
-go.loglayer.dev/transports/http/v2 v2.0.1/go.mod h1:OeIaQoUHcT3Qeb8HI8CaY4OFhQwwpC/Pb0yXGTrHxIM=
-go.loglayer.dev/v2 v2.0.1 h1:B7oYpkfMky0UG/N8IdKeIiiTi6h7eyj5NvRyEth9DHI=
-go.loglayer.dev/v2 v2.0.1/go.mod h1:+BWhs5AyICvCLBz07qHnCE12W34tArUWfXOba0Ct/QI=
+go.loglayer.dev/v3 v3.0.0 h1:TEkgoAO21SAE0vKzrNa53G3VG88TJCKs/fMo177uY8s=
+go.loglayer.dev/v3 v3.0.0/go.mod h1:ghbgQp7Y/Vo4/jPCLiokMZTy2DrA3X8dfqtgcWhy3pg=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
diff --git a/transports/datadog/livetest_test.go b/transports/datadog/livetest_test.go
index bf221f9..8242aa8 100644
--- a/transports/datadog/livetest_test.go
+++ b/transports/datadog/livetest_test.go
@@ -31,10 +31,10 @@ import (
"testing"
"time"
- "go.loglayer.dev/transports/datadog/v2"
- httptr "go.loglayer.dev/transports/http/v2"
- "go.loglayer.dev/v2/transport/transporttest"
- "go.loglayer.dev/v2/utils/idgen"
+ "go.loglayer.dev/transports/datadog/v3"
+ httptr "go.loglayer.dev/transports/http/v3"
+ "go.loglayer.dev/v3/transport/transporttest"
+ "go.loglayer.dev/v3/utils/idgen"
)
func TestLive_Datadog_SendsLog(t *testing.T) {
diff --git a/transports/gcplogging/example_test.go b/transports/gcplogging/example_test.go
index 7034d5f..fc7ac1c 100644
--- a/transports/gcplogging/example_test.go
+++ b/transports/gcplogging/example_test.go
@@ -3,8 +3,8 @@ package gcplogging_test
import (
"cloud.google.com/go/logging"
- "go.loglayer.dev/transports/gcplogging/v2"
- "go.loglayer.dev/v2"
+ "go.loglayer.dev/transports/gcplogging/v3"
+ "go.loglayer.dev/v3"
)
// New forwards entries to a caller-supplied *logging.Logger from
diff --git a/transports/gcplogging/gcplogging.go b/transports/gcplogging/gcplogging.go
index be74e67..ba9dda2 100644
--- a/transports/gcplogging/gcplogging.go
+++ b/transports/gcplogging/gcplogging.go
@@ -43,8 +43,8 @@ import (
"cloud.google.com/go/logging"
- "go.loglayer.dev/v2"
- "go.loglayer.dev/v2/transport"
+ "go.loglayer.dev/v3"
+ "go.loglayer.dev/v3/transport"
)
// Config holds configuration options for the GCP Cloud Logging transport.
diff --git a/transports/gcplogging/gcplogging_test.go b/transports/gcplogging/gcplogging_test.go
index 665a3f0..d8bcc19 100644
--- a/transports/gcplogging/gcplogging_test.go
+++ b/transports/gcplogging/gcplogging_test.go
@@ -7,7 +7,7 @@ import (
"cloud.google.com/go/logging"
- "go.loglayer.dev/v2"
+ "go.loglayer.dev/v3"
)
func TestSeverityFor(t *testing.T) {
diff --git a/transports/gcplogging/go.mod b/transports/gcplogging/go.mod
index fad0517..67d0ff0 100644
--- a/transports/gcplogging/go.mod
+++ b/transports/gcplogging/go.mod
@@ -1,29 +1,30 @@
-module go.loglayer.dev/transports/gcplogging/v2
+module go.loglayer.dev/transports/gcplogging/v3
go 1.25.0
require (
cloud.google.com/go/logging v1.13.0
- go.loglayer.dev/v2 v2.2.0
+ go.loglayer.dev/v3 v3.0.0
)
require (
- cloud.google.com/go v0.117.0 // indirect
- cloud.google.com/go/auth v0.13.0 // indirect
- cloud.google.com/go/auth/oauth2adapt v0.2.6 // indirect
+ cloud.google.com/go v0.123.0 // indirect
+ cloud.google.com/go/auth v0.17.0 // indirect
+ cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect
cloud.google.com/go/compute/metadata v0.9.0 // indirect
- cloud.google.com/go/longrunning v0.6.2 // indirect
+ cloud.google.com/go/longrunning v0.7.0 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/go-logr/logr v1.4.3 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/goccy/go-json v0.10.6 // indirect
- github.com/google/s2a-go v0.1.8 // indirect
- github.com/googleapis/enterprise-certificate-proxy v0.3.4 // indirect
- github.com/googleapis/gax-go/v2 v2.14.0 // indirect
+ github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect
+ github.com/google/s2a-go v0.1.9 // indirect
+ github.com/googleapis/enterprise-certificate-proxy v0.3.7 // indirect
+ github.com/googleapis/gax-go/v2 v2.15.0 // indirect
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
- go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.54.0 // indirect
- go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0 // indirect
+ go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0 // indirect
+ go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.67.0 // indirect
go.opentelemetry.io/otel v1.43.0 // indirect
go.opentelemetry.io/otel/metric v1.43.0 // indirect
go.opentelemetry.io/otel/trace v1.43.0 // indirect
@@ -33,9 +34,9 @@ require (
golang.org/x/sync v0.20.0 // indirect
golang.org/x/sys v0.43.0 // indirect
golang.org/x/text v0.36.0 // indirect
- golang.org/x/time v0.8.0 // indirect
- google.golang.org/api v0.214.0 // indirect
- google.golang.org/genproto v0.0.0-20241118233622-e639e219e697 // indirect
+ golang.org/x/time v0.15.0 // indirect
+ google.golang.org/api v0.258.0 // indirect
+ google.golang.org/genproto v0.0.0-20251022142026-3a174f9686a8 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20260414002931-afd174a4e478 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20260414002931-afd174a4e478 // indirect
google.golang.org/grpc v1.82.1 // indirect
diff --git a/transports/gcplogging/go.sum b/transports/gcplogging/go.sum
index fc1e8c1..7fef656 100644
--- a/transports/gcplogging/go.sum
+++ b/transports/gcplogging/go.sum
@@ -1,17 +1,17 @@
-cloud.google.com/go v0.117.0 h1:Z5TNFfQxj7WG2FgOGX1ekC5RiXrYgms6QscOm32M/4s=
-cloud.google.com/go v0.117.0/go.mod h1:ZbwhVTb1DBGt2Iwb3tNO6SEK4q+cplHZmLWH+DelYYc=
-cloud.google.com/go/auth v0.13.0 h1:8Fu8TZy167JkW8Tj3q7dIkr2v4cndv41ouecJx0PAHs=
-cloud.google.com/go/auth v0.13.0/go.mod h1:COOjD9gwfKNKz+IIduatIhYJQIc0mG3H102r/EMxX6Q=
-cloud.google.com/go/auth/oauth2adapt v0.2.6 h1:V6a6XDu2lTwPZWOawrAa9HUK+DB2zfJyTuciBG5hFkU=
-cloud.google.com/go/auth/oauth2adapt v0.2.6/go.mod h1:AlmsELtlEBnaNTL7jCj8VQFLy6mbZv0s4Q7NGBeQ5E8=
+cloud.google.com/go v0.123.0 h1:2NAUJwPR47q+E35uaJeYoNhuNEM9kM8SjgRgdeOJUSE=
+cloud.google.com/go v0.123.0/go.mod h1:xBoMV08QcqUGuPW65Qfm1o9Y4zKZBpGS+7bImXLTAZU=
+cloud.google.com/go/auth v0.17.0 h1:74yCm7hCj2rUyyAocqnFzsAYXgJhrG26XCFimrc/Kz4=
+cloud.google.com/go/auth v0.17.0/go.mod h1:6wv/t5/6rOPAX4fJiRjKkJCvswLwdet7G8+UGXt7nCQ=
+cloud.google.com/go/auth/oauth2adapt v0.2.8 h1:keo8NaayQZ6wimpNSmW5OPc283g65QNIiLpZnkHRbnc=
+cloud.google.com/go/auth/oauth2adapt v0.2.8/go.mod h1:XQ9y31RkqZCcwJWNSx2Xvric3RrU88hAYYbjDWYDL+c=
cloud.google.com/go/compute/metadata v0.9.0 h1:pDUj4QMoPejqq20dK0Pg2N4yG9zIkYGdBtwLoEkH9Zs=
cloud.google.com/go/compute/metadata v0.9.0/go.mod h1:E0bWwX5wTnLPedCKqk3pJmVgCBSM6qQI1yTBdEb3C10=
-cloud.google.com/go/iam v1.2.2 h1:ozUSofHUGf/F4tCNy/mu9tHLTaxZFLOUiKzjcgWHGIA=
-cloud.google.com/go/iam v1.2.2/go.mod h1:0Ys8ccaZHdI1dEUilwzqng/6ps2YB6vRsjIe00/+6JY=
+cloud.google.com/go/iam v1.5.3 h1:+vMINPiDF2ognBJ97ABAYYwRgsaqxPbQDlMnbHMjolc=
+cloud.google.com/go/iam v1.5.3/go.mod h1:MR3v9oLkZCTlaqljW6Eb2d3HGDGK5/bDv93jhfISFvU=
cloud.google.com/go/logging v1.13.0 h1:7j0HgAp0B94o1YRDqiqm26w4q1rDMH7XNRU34lJXHYc=
cloud.google.com/go/logging v1.13.0/go.mod h1:36CoKh6KA/M0PbhPKMq6/qety2DCAErbhXT62TuXALA=
-cloud.google.com/go/longrunning v0.6.2 h1:xjDfh1pQcWPEvnfjZmwjKQEcHnpz6lHjfy7Fo0MK+hc=
-cloud.google.com/go/longrunning v0.6.2/go.mod h1:k/vIs83RN4bE3YCswdXC5PFfWVILjm3hpEUlSko4PiI=
+cloud.google.com/go/longrunning v0.7.0 h1:FV0+SYF1RIj59gyoWDRi45GiYUMM3K1qO51qoboQT1E=
+cloud.google.com/go/longrunning v0.7.0/go.mod h1:ySn2yXmjbK9Ba0zsQqunhDkYi0+9rlXIwnoAf+h+TPY=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cncf/xds/go v0.0.0-20260202195803-dba9d589def2 h1:aBangftG7EVZoUb69Os8IaYg++6uMOdKK83QtkkvJik=
@@ -32,36 +32,36 @@ github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/goccy/go-json v0.10.6 h1:p8HrPJzOakx/mn/bQtjgNjdTcN+/S6FcG2CTtQOrHVU=
github.com/goccy/go-json v0.10.6/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
-github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
-github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
+github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ=
+github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
-github.com/google/s2a-go v0.1.8 h1:zZDs9gcbt9ZPLV0ndSyQk6Kacx2g/X+SKYovpnz3SMM=
-github.com/google/s2a-go v0.1.8/go.mod h1:6iNWHTpQ+nfNRN5E00MSdfDwVesa8hhS32PhPO8deJA=
+github.com/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0=
+github.com/google/s2a-go v0.1.9/go.mod h1:YA0Ei2ZQL3acow2O62kdp9UlnvMmU7kA6Eutn0dXayM=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
-github.com/googleapis/enterprise-certificate-proxy v0.3.4 h1:XYIDZApgAnrN1c855gTgghdIA6Stxb52D5RnLI1SLyw=
-github.com/googleapis/enterprise-certificate-proxy v0.3.4/go.mod h1:YKe7cfqYXjKGpGvmSg28/fFvhNzinZQm8DGnaburhGA=
-github.com/googleapis/gax-go/v2 v2.14.0 h1:f+jMrjBPl+DL9nI4IQzLUxMq7XrAqFYB7hBPqMNIe8o=
-github.com/googleapis/gax-go/v2 v2.14.0/go.mod h1:lhBCnjdLrWRaPvLWhmc8IS24m9mr07qSYnHncrgo+zk=
+github.com/googleapis/enterprise-certificate-proxy v0.3.7 h1:zrn2Ee/nWmHulBx5sAVrGgAa0f2/R35S4DJwfFaUPFQ=
+github.com/googleapis/enterprise-certificate-proxy v0.3.7/go.mod h1:MkHOF77EYAE7qfSuSS9PU6g4Nt4e11cnsDUowfwewLA=
+github.com/googleapis/gax-go/v2 v2.15.0 h1:SyjDc1mGgZU5LncH8gimWo9lW1DtIfPibOG81vgd/bo=
+github.com/googleapis/gax-go/v2 v2.15.0/go.mod h1:zVVkkxAQHa1RQpg9z2AUCMnKhi0Qld9rcmyfL1OZhoc=
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 h1:GFCKgmp0tecUJ0sJuv4pzYCqS9+RGSn52M3FUwPs+uo=
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
-go.loglayer.dev/v2 v2.2.0 h1:m8sgblBOtMrhbvcxETYKP+9S/K0CbCwva/Z3eflJ0ls=
-go.loglayer.dev/v2 v2.2.0/go.mod h1:+BWhs5AyICvCLBz07qHnCE12W34tArUWfXOba0Ct/QI=
+go.loglayer.dev/v3 v3.0.0 h1:TEkgoAO21SAE0vKzrNa53G3VG88TJCKs/fMo177uY8s=
+go.loglayer.dev/v3 v3.0.0/go.mod h1:ghbgQp7Y/Vo4/jPCLiokMZTy2DrA3X8dfqtgcWhy3pg=
go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
-go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.54.0 h1:r6I7RJCN86bpD/FQwedZ0vSixDpwuWREjW9oRMsmqDc=
-go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.54.0/go.mod h1:B9yO6b04uB80CzjedvewuqDhxJxi11s7/GtiGa8bAjI=
-go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0 h1:TT4fX+nBOA/+LUkobKGW1ydGcn+G3vRw9+g5HwCphpk=
-go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0/go.mod h1:L7UH0GbB0p47T4Rri3uHjbpCFYrVrwc1I25QhNPiGK8=
+go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0 h1:q4XOmH/0opmeuJtPsbFNivyl7bCt7yRBbeEm2sC/XtQ=
+go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0/go.mod h1:snMWehoOh2wsEwnvvwtDyFCxVeDAODenXHtn5vzrKjo=
+go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.67.0 h1:OyrsyzuttWTSur2qN/Lm0m2a8yqyIjUVBZcxFPuXq2o=
+go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.67.0/go.mod h1:C2NGBr+kAB4bk3xtMXfZ94gqFDtg/GkI7e9zqGh5Beg=
go.opentelemetry.io/otel v1.43.0 h1:mYIM03dnh5zfN7HautFE4ieIig9amkNANT+xcVxAj9I=
go.opentelemetry.io/otel v1.43.0/go.mod h1:JuG+u74mvjvcm8vj8pI5XiHy1zDeoCS2LB1spIq7Ay0=
go.opentelemetry.io/otel/metric v1.43.0 h1:d7638QeInOnuwOONPp4JAOGfbCEpYb+K6DVWvdxGzgM=
@@ -86,14 +86,14 @@ golang.org/x/sys v0.43.0 h1:Rlag2XtaFTxp19wS8MXlJwTvoh8ArU6ezoyFsMyCTNI=
golang.org/x/sys v0.43.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
golang.org/x/text v0.36.0 h1:JfKh3XmcRPqZPKevfXVpI1wXPTqbkE5f7JA92a55Yxg=
golang.org/x/text v0.36.0/go.mod h1:NIdBknypM8iqVmPiuco0Dh6P5Jcdk8lJL0CUebqK164=
-golang.org/x/time v0.8.0 h1:9i3RxcPv3PZnitoVGMPDKZSq1xW1gK1Xy3ArNOGZfEg=
-golang.org/x/time v0.8.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
+golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U=
+golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno=
gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4=
gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E=
-google.golang.org/api v0.214.0 h1:h2Gkq07OYi6kusGOaT/9rnNljuXmqPnaig7WGPmKbwA=
-google.golang.org/api v0.214.0/go.mod h1:bYPpLG8AyeMWwDU6NXoB00xC0DFkikVvd5MfwoxjLqE=
-google.golang.org/genproto v0.0.0-20241118233622-e639e219e697 h1:ToEetK57OidYuqD4Q5w+vfEnPvPpuTwedCNVohYJfNk=
-google.golang.org/genproto v0.0.0-20241118233622-e639e219e697/go.mod h1:JJrvXBWRZaFMxBufik1a4RpFw4HhgVtBBWQeQgUj2cc=
+google.golang.org/api v0.258.0 h1:IKo1j5FBlN74fe5isA2PVozN3Y5pwNKriEgAXPOkDAc=
+google.golang.org/api v0.258.0/go.mod h1:qhOMTQEZ6lUps63ZNq9jhODswwjkjYYguA7fA3TBFww=
+google.golang.org/genproto v0.0.0-20251022142026-3a174f9686a8 h1:a12a2/BiVRxRWIqBbfqoSK6tgq8cyUgMnEI81QlPge0=
+google.golang.org/genproto v0.0.0-20251022142026-3a174f9686a8/go.mod h1:1Ic78BnpzY8OaTCmzxJDP4qC9INZPbGZl+54RKjtyeI=
google.golang.org/genproto/googleapis/api v0.0.0-20260414002931-afd174a4e478 h1:yQugLulqltosq0B/f8l4w9VryjV+N/5gcW0jQ3N8Qec=
google.golang.org/genproto/googleapis/api v0.0.0-20260414002931-afd174a4e478/go.mod h1:C6ADNqOxbgdUUeRTU+LCHDPB9ttAMCTff6auwCVa4uc=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260414002931-afd174a4e478 h1:RmoJA1ujG+/lRGNfUnOMfhCy5EipVMyvUE+KNbPbTlw=
diff --git a/transports/gcplogging/livetest_test.go b/transports/gcplogging/livetest_test.go
index 5e713b1..a7e1a18 100644
--- a/transports/gcplogging/livetest_test.go
+++ b/transports/gcplogging/livetest_test.go
@@ -28,9 +28,9 @@ import (
"cloud.google.com/go/logging"
- "go.loglayer.dev/transports/gcplogging/v2"
- "go.loglayer.dev/v2/transport/transporttest"
- "go.loglayer.dev/v2/utils/idgen"
+ "go.loglayer.dev/transports/gcplogging/v3"
+ "go.loglayer.dev/v3/transport/transporttest"
+ "go.loglayer.dev/v3/utils/idgen"
)
func TestLive_GCPLogging_SendsLog(t *testing.T) {
diff --git a/transports/http/example_test.go b/transports/http/example_test.go
index a46be9f..9df9324 100644
--- a/transports/http/example_test.go
+++ b/transports/http/example_test.go
@@ -1,8 +1,8 @@
package httptransport_test
import (
- httptransport "go.loglayer.dev/transports/http/v2"
- "go.loglayer.dev/v2"
+ httptransport "go.loglayer.dev/transports/http/v3"
+ "go.loglayer.dev/v3"
)
// New POSTs JSON batches to URL. The worker goroutine starts at
diff --git a/transports/http/go.mod b/transports/http/go.mod
index 759991a..290f2a3 100644
--- a/transports/http/go.mod
+++ b/transports/http/go.mod
@@ -1,9 +1,9 @@
-module go.loglayer.dev/transports/http/v2
+module go.loglayer.dev/transports/http/v3
go 1.25.0
require (
github.com/goccy/go-json v0.10.6
- go.loglayer.dev/v2 v2.0.1
+ go.loglayer.dev/v3 v3.0.0
go.uber.org/goleak v1.3.0
)
diff --git a/transports/http/go.sum b/transports/http/go.sum
index 9feb45c..39cf249 100644
--- a/transports/http/go.sum
+++ b/transports/http/go.sum
@@ -6,8 +6,8 @@ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRI
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
-go.loglayer.dev/v2 v2.0.1 h1:B7oYpkfMky0UG/N8IdKeIiiTi6h7eyj5NvRyEth9DHI=
-go.loglayer.dev/v2 v2.0.1/go.mod h1:+BWhs5AyICvCLBz07qHnCE12W34tArUWfXOba0Ct/QI=
+go.loglayer.dev/v3 v3.0.0 h1:TEkgoAO21SAE0vKzrNa53G3VG88TJCKs/fMo177uY8s=
+go.loglayer.dev/v3 v3.0.0/go.mod h1:ghbgQp7Y/Vo4/jPCLiokMZTy2DrA3X8dfqtgcWhy3pg=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
diff --git a/transports/http/http.go b/transports/http/http.go
index de6209d..1b645d9 100644
--- a/transports/http/http.go
+++ b/transports/http/http.go
@@ -24,8 +24,8 @@ import (
"github.com/goccy/go-json"
- "go.loglayer.dev/v2"
- "go.loglayer.dev/v2/transport"
+ "go.loglayer.dev/v3"
+ "go.loglayer.dev/v3/transport"
)
const (
diff --git a/transports/http/http_test.go b/transports/http/http_test.go
index 31506c7..c7e7e12 100644
--- a/transports/http/http_test.go
+++ b/transports/http/http_test.go
@@ -14,9 +14,9 @@ import (
"testing"
"time"
- httptr "go.loglayer.dev/transports/http/v2"
- "go.loglayer.dev/v2"
- "go.loglayer.dev/v2/transport"
+ httptr "go.loglayer.dev/transports/http/v3"
+ "go.loglayer.dev/v3"
+ "go.loglayer.dev/v3/transport"
)
// captureServer accumulates request bodies for assertion. It supports an
@@ -339,8 +339,9 @@ func TestHTTP_FieldsAndMetadataInBody(t *testing.T) {
if entry["requestId"] != "abc" {
t.Errorf("requestId: got %v", entry["requestId"])
}
- if entry["durationMs"] != float64(42) {
- t.Errorf("durationMs: got %v", entry["durationMs"])
+ // The metadata map nests under "metadata" per the v3 core default.
+ if entry["metadata"].(map[string]any)["durationMs"] != float64(42) {
+ t.Errorf("metadata.durationMs: got %v", entry["metadata"])
}
}
diff --git a/transports/logrus/bench_test.go b/transports/logrus/bench_test.go
index 835dc38..4411f46 100644
--- a/transports/logrus/bench_test.go
+++ b/transports/logrus/bench_test.go
@@ -5,10 +5,10 @@ import (
logrusbase "github.com/sirupsen/logrus"
- lllogrus "go.loglayer.dev/transports/logrus/v2"
- "go.loglayer.dev/v2"
- "go.loglayer.dev/v2/transport"
- "go.loglayer.dev/v2/transport/benchtest"
+ lllogrus "go.loglayer.dev/transports/logrus/v3"
+ "go.loglayer.dev/v3"
+ "go.loglayer.dev/v3/transport"
+ "go.loglayer.dev/v3/transport/benchtest"
)
func newDirect() *logrusbase.Logger {
diff --git a/transports/logrus/example_test.go b/transports/logrus/example_test.go
index 7be2ac4..cf96219 100644
--- a/transports/logrus/example_test.go
+++ b/transports/logrus/example_test.go
@@ -3,8 +3,8 @@ package logrus_test
import (
"io"
- "go.loglayer.dev/transports/logrus/v2"
- "go.loglayer.dev/v2"
+ "go.loglayer.dev/transports/logrus/v3"
+ "go.loglayer.dev/v3"
)
// New wraps a *logrus.Logger. When Logger is nil a default logger is
diff --git a/transports/logrus/go.mod b/transports/logrus/go.mod
index 302b197..6046f49 100644
--- a/transports/logrus/go.mod
+++ b/transports/logrus/go.mod
@@ -1,13 +1,13 @@
-module go.loglayer.dev/transports/logrus/v2
+module go.loglayer.dev/transports/logrus/v3
go 1.25.0
require (
github.com/sirupsen/logrus v1.9.4
- go.loglayer.dev/v2 v2.0.1
+ go.loglayer.dev/v3 v3.0.0
)
require (
github.com/goccy/go-json v0.10.6 // indirect
- golang.org/x/sys v0.42.0 // indirect
+ golang.org/x/sys v0.43.0 // indirect
)
diff --git a/transports/logrus/go.sum b/transports/logrus/go.sum
index 953106f..4c6c2fb 100644
--- a/transports/logrus/go.sum
+++ b/transports/logrus/go.sum
@@ -8,11 +8,11 @@ github.com/sirupsen/logrus v1.9.4 h1:TsZE7l11zFCLZnZ+teH4Umoq5BhEIfIzfRDZ1Uzql2w
github.com/sirupsen/logrus v1.9.4/go.mod h1:ftWc9WdOfJ0a92nsE2jF5u5ZwH8Bv2zdeOC42RjbV2g=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
-go.loglayer.dev/v2 v2.0.1 h1:B7oYpkfMky0UG/N8IdKeIiiTi6h7eyj5NvRyEth9DHI=
-go.loglayer.dev/v2 v2.0.1/go.mod h1:+BWhs5AyICvCLBz07qHnCE12W34tArUWfXOba0Ct/QI=
+go.loglayer.dev/v3 v3.0.0 h1:TEkgoAO21SAE0vKzrNa53G3VG88TJCKs/fMo177uY8s=
+go.loglayer.dev/v3 v3.0.0/go.mod h1:ghbgQp7Y/Vo4/jPCLiokMZTy2DrA3X8dfqtgcWhy3pg=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
-golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo=
-golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
+golang.org/x/sys v0.43.0 h1:Rlag2XtaFTxp19wS8MXlJwTvoh8ArU6ezoyFsMyCTNI=
+golang.org/x/sys v0.43.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
diff --git a/transports/logrus/logrus.go b/transports/logrus/logrus.go
index c8a452d..9871588 100644
--- a/transports/logrus/logrus.go
+++ b/transports/logrus/logrus.go
@@ -9,8 +9,8 @@ import (
"github.com/sirupsen/logrus"
- "go.loglayer.dev/v2"
- "go.loglayer.dev/v2/transport"
+ "go.loglayer.dev/v3"
+ "go.loglayer.dev/v3/transport"
)
// Config holds configuration options for the logrus transport.
diff --git a/transports/logrus/logrus_test.go b/transports/logrus/logrus_test.go
index ab83c2d..775f0f1 100644
--- a/transports/logrus/logrus_test.go
+++ b/transports/logrus/logrus_test.go
@@ -6,10 +6,10 @@ import (
logrusbase "github.com/sirupsen/logrus"
- lllogrus "go.loglayer.dev/transports/logrus/v2"
- "go.loglayer.dev/v2"
- "go.loglayer.dev/v2/transport"
- "go.loglayer.dev/v2/transport/transporttest"
+ lllogrus "go.loglayer.dev/transports/logrus/v3"
+ "go.loglayer.dev/v3"
+ "go.loglayer.dev/v3/transport"
+ "go.loglayer.dev/v3/transport/transporttest"
)
func factory(opts transporttest.FactoryOpts) (*loglayer.LogLayer, *bytes.Buffer) {
diff --git a/transports/lumberjack/example_test.go b/transports/lumberjack/example_test.go
index 2309b95..910b4c7 100644
--- a/transports/lumberjack/example_test.go
+++ b/transports/lumberjack/example_test.go
@@ -1,8 +1,8 @@
package lumberjack_test
import (
- "go.loglayer.dev/transports/lumberjack/v2"
- "go.loglayer.dev/v2"
+ "go.loglayer.dev/transports/lumberjack/v3"
+ "go.loglayer.dev/v3"
)
// New writes one JSON object per entry to a rotating file. Filename is
diff --git a/transports/lumberjack/go.mod b/transports/lumberjack/go.mod
index 603d80e..9875b77 100644
--- a/transports/lumberjack/go.mod
+++ b/transports/lumberjack/go.mod
@@ -1,11 +1,13 @@
-module go.loglayer.dev/transports/lumberjack/v2
+module go.loglayer.dev/transports/lumberjack/v3
go 1.25.0
require (
- go.loglayer.dev/transports/structured/v2 v2.0.1
- go.loglayer.dev/v2 v2.0.1
+ go.loglayer.dev/transports/structured/v3 v3.0.0-00010101000000-000000000000
+ go.loglayer.dev/v3 v3.0.0
gopkg.in/natefinch/lumberjack.v2 v2.2.1
)
require github.com/goccy/go-json v0.10.6 // indirect
+
+replace go.loglayer.dev/transports/structured/v3 => ../structured
diff --git a/transports/lumberjack/go.sum b/transports/lumberjack/go.sum
index eb16e42..ec105ed 100644
--- a/transports/lumberjack/go.sum
+++ b/transports/lumberjack/go.sum
@@ -1,9 +1,7 @@
github.com/goccy/go-json v0.10.6 h1:p8HrPJzOakx/mn/bQtjgNjdTcN+/S6FcG2CTtQOrHVU=
github.com/goccy/go-json v0.10.6/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
-go.loglayer.dev/transports/structured/v2 v2.0.1 h1:t+7zgTwd/3aKTPVLrtVee76prA9//+G+q4AgdMev8w4=
-go.loglayer.dev/transports/structured/v2 v2.0.1/go.mod h1:2XWKjWpy0OfJ0WQHpu/4hwbhAwXQrfNNJ54LUxSyMG0=
-go.loglayer.dev/v2 v2.0.1 h1:B7oYpkfMky0UG/N8IdKeIiiTi6h7eyj5NvRyEth9DHI=
-go.loglayer.dev/v2 v2.0.1/go.mod h1:+BWhs5AyICvCLBz07qHnCE12W34tArUWfXOba0Ct/QI=
+go.loglayer.dev/v3 v3.0.0 h1:TEkgoAO21SAE0vKzrNa53G3VG88TJCKs/fMo177uY8s=
+go.loglayer.dev/v3 v3.0.0/go.mod h1:ghbgQp7Y/Vo4/jPCLiokMZTy2DrA3X8dfqtgcWhy3pg=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc=
diff --git a/transports/lumberjack/lumberjack.go b/transports/lumberjack/lumberjack.go
index 848c7f8..52a9a9e 100644
--- a/transports/lumberjack/lumberjack.go
+++ b/transports/lumberjack/lumberjack.go
@@ -23,9 +23,9 @@ import (
lj "gopkg.in/natefinch/lumberjack.v2"
- "go.loglayer.dev/transports/structured/v2"
- "go.loglayer.dev/v2"
- "go.loglayer.dev/v2/transport"
+ "go.loglayer.dev/transports/structured/v3"
+ "go.loglayer.dev/v3"
+ "go.loglayer.dev/v3/transport"
)
// Config holds configuration options for Transport.
diff --git a/transports/lumberjack/lumberjack_test.go b/transports/lumberjack/lumberjack_test.go
index a0995b0..64ca598 100644
--- a/transports/lumberjack/lumberjack_test.go
+++ b/transports/lumberjack/lumberjack_test.go
@@ -10,10 +10,10 @@ import (
"sync/atomic"
"testing"
- "go.loglayer.dev/transports/lumberjack/v2"
- "go.loglayer.dev/v2"
- "go.loglayer.dev/v2/transport"
- "go.loglayer.dev/v2/transport/transporttest"
+ "go.loglayer.dev/transports/lumberjack/v3"
+ "go.loglayer.dev/v3"
+ "go.loglayer.dev/v3/transport"
+ "go.loglayer.dev/v3/transport/transporttest"
)
// readFileBuffer reads path into a *bytes.Buffer so we can reuse the
@@ -94,8 +94,9 @@ func TestFile_FieldsAndMetadataMergeAtRoot(t *testing.T) {
if obj["requestId"] != "abc" {
t.Errorf("requestId: got %v", obj["requestId"])
}
- if obj["durationMs"] != float64(42) {
- t.Errorf("durationMs: got %v", obj["durationMs"])
+ // The metadata map nests under "metadata" per the v3 core default.
+ if obj["metadata"].(map[string]any)["durationMs"] != float64(42) {
+ t.Errorf("metadata.durationMs: got %v", obj["metadata"])
}
}
diff --git a/transports/newrelic/README.md b/transports/newrelic/README.md
index 4020d1d..8be760a 100644
--- a/transports/newrelic/README.md
+++ b/transports/newrelic/README.md
@@ -1,13 +1,13 @@
-# go.loglayer.dev/transports/newrelic
+# go.loglayer.dev/transports/newrelic/v2
-[](https://pkg.go.dev/go.loglayer.dev/transports/newrelic)
+[](https://pkg.go.dev/go.loglayer.dev/transports/newrelic/v2)
New Relic Logs HTTP intake transport for LogLayer. Site-aware URL, Api-Key header, New Relic log format encoder with logtype and timestamp. Built on the HTTP transport.
## Install
```sh
-go get go.loglayer.dev/transports/newrelic
+go get go.loglayer.dev/transports/newrelic/v2
```
## Documentation
diff --git a/transports/newrelic/example_test.go b/transports/newrelic/example_test.go
index c82f7d0..e5226b0 100644
--- a/transports/newrelic/example_test.go
+++ b/transports/newrelic/example_test.go
@@ -1,8 +1,8 @@
package newrelic_test
import (
- "go.loglayer.dev/transports/newrelic"
- "go.loglayer.dev/v2"
+ "go.loglayer.dev/transports/newrelic/v2"
+ "go.loglayer.dev/v3"
)
// New ships log entries to the New Relic Logs HTTP intake. LicenseKey is
diff --git a/transports/newrelic/go.mod b/transports/newrelic/go.mod
index d1ff53a..9842b85 100644
--- a/transports/newrelic/go.mod
+++ b/transports/newrelic/go.mod
@@ -1,12 +1,14 @@
-module go.loglayer.dev/transports/newrelic
+module go.loglayer.dev/transports/newrelic/v2
go 1.25.0
require (
github.com/goccy/go-json v0.10.6
- go.loglayer.dev/transports/http/v2 v2.1.0
- go.loglayer.dev/v2 v2.1.0
+ go.loglayer.dev/transports/http/v3 v3.0.0-00010101000000-000000000000
+ go.loglayer.dev/v3 v3.0.0
go.uber.org/goleak v1.3.0
)
replace go.loglayer.dev => ../..
+
+replace go.loglayer.dev/transports/http/v3 => ../http
diff --git a/transports/newrelic/go.sum b/transports/newrelic/go.sum
index 43b75e5..39cf249 100644
--- a/transports/newrelic/go.sum
+++ b/transports/newrelic/go.sum
@@ -6,10 +6,8 @@ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRI
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
-go.loglayer.dev/transports/http/v2 v2.1.0 h1:+9kaawgxkbFYKvIliTWwSjj9WKUnG9Uzjm3dRmuCkRY=
-go.loglayer.dev/transports/http/v2 v2.1.0/go.mod h1:OeIaQoUHcT3Qeb8HI8CaY4OFhQwwpC/Pb0yXGTrHxIM=
-go.loglayer.dev/v2 v2.1.0 h1:8/fq8Z1NNLtjfKgaPn6S0Hy7zWPnkjn9Gj3YgEDFk4w=
-go.loglayer.dev/v2 v2.1.0/go.mod h1:+BWhs5AyICvCLBz07qHnCE12W34tArUWfXOba0Ct/QI=
+go.loglayer.dev/v3 v3.0.0 h1:TEkgoAO21SAE0vKzrNa53G3VG88TJCKs/fMo177uY8s=
+go.loglayer.dev/v3 v3.0.0/go.mod h1:ghbgQp7Y/Vo4/jPCLiokMZTy2DrA3X8dfqtgcWhy3pg=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
diff --git a/transports/newrelic/livetest_test.go b/transports/newrelic/livetest_test.go
index b530014..6a44aae 100644
--- a/transports/newrelic/livetest_test.go
+++ b/transports/newrelic/livetest_test.go
@@ -26,10 +26,10 @@ import (
"testing"
"time"
- httptr "go.loglayer.dev/transports/http/v2"
- "go.loglayer.dev/transports/newrelic"
- "go.loglayer.dev/v2/transport/transporttest"
- "go.loglayer.dev/v2/utils/idgen"
+ httptr "go.loglayer.dev/transports/http/v3"
+ "go.loglayer.dev/transports/newrelic/v2"
+ "go.loglayer.dev/v3/transport/transporttest"
+ "go.loglayer.dev/v3/utils/idgen"
)
func TestLive_NewRelic_SendsLog(t *testing.T) {
diff --git a/transports/newrelic/newrelic.go b/transports/newrelic/newrelic.go
index b741f35..226ddd2 100644
--- a/transports/newrelic/newrelic.go
+++ b/transports/newrelic/newrelic.go
@@ -19,9 +19,9 @@ import (
"github.com/goccy/go-json"
- httptr "go.loglayer.dev/transports/http/v2"
- "go.loglayer.dev/v2"
- "go.loglayer.dev/v2/transport"
+ httptr "go.loglayer.dev/transports/http/v3"
+ "go.loglayer.dev/v3"
+ "go.loglayer.dev/v3/transport"
)
// Site identifies the New Relic region. Affects only the intake URL.
@@ -186,30 +186,31 @@ const (
// map, enforcing New Relic's API constraints: max 255 attributes, max 255-
// char attribute names, and values truncated at 4094 chars. Reserved fields
// (timestamp, level, log) are excluded to prevent collisions.
+//
+// Metadata placement follows the core's Schema.MetadataFieldName: under the
+// v3 default ("metadata") the whole metadata value nests under that key;
+// with FlattenMetadata: true it merges at the root like the sibling network
+// encoders. (Mirrors transport.MergeIntoMap, then applies the reserved-key
+// filter and per-attribute constraints.)
func mergeAttributes(e httptr.Entry) map[string]any {
attrs := make(map[string]any)
- for k, v := range e.Data {
+ transport.MergeIntoMap(attrs, e.Data, e.Metadata, e.Schema.MetadataFieldName)
+ // Iterate a key snapshot so the truncation break stays deterministic:
+ // attrs is mutated (reserved keys deleted, values rewritten) mid-loop.
+ keys := make([]string, 0, len(attrs))
+ for k := range attrs {
+ keys = append(keys, k)
+ }
+ for _, k := range keys {
if reserved(k) {
+ delete(attrs, k)
continue
}
if len(attrs)+1 > maxAttributes {
break
}
- setAttr(attrs, k, v)
- }
-
- if m, ok := transport.MetadataAsRootMap(e.Metadata); ok {
- for k, v := range m {
- if reserved(k) {
- continue
- }
- if len(attrs)+1 > maxAttributes {
- break
- }
- setAttr(attrs, k, v)
- }
+ setAttr(attrs, k, attrs[k])
}
-
return attrs
}
diff --git a/transports/newrelic/newrelic_test.go b/transports/newrelic/newrelic_test.go
index 3472f36..0cfa09f 100644
--- a/transports/newrelic/newrelic_test.go
+++ b/transports/newrelic/newrelic_test.go
@@ -13,10 +13,10 @@ import (
"testing"
"time"
- httptr "go.loglayer.dev/transports/http/v2"
- "go.loglayer.dev/transports/newrelic"
- "go.loglayer.dev/v2"
- "go.loglayer.dev/v2/transport"
+ httptr "go.loglayer.dev/transports/http/v3"
+ "go.loglayer.dev/transports/newrelic/v2"
+ "go.loglayer.dev/v3"
+ "go.loglayer.dev/v3/transport"
)
type capture struct {
@@ -400,12 +400,17 @@ func TestNewRelic_FieldAndMetadataInBody(t *testing.T) {
if attrs["requestId"] != "req-42" {
t.Errorf("requestId: got %v", attrs["requestId"])
}
+ // Under the v3 default, metadata nests under the "metadata" key.
+ md, ok := attrs["metadata"].(map[string]any)
+ if !ok {
+ t.Fatalf("expected metadata map, got: %v", attrs["metadata"])
+ }
// JSON unmarshals numbers as float64.
- if attrs["durationMs"] != float64(123) {
- t.Errorf("durationMs: got %v", attrs["durationMs"])
+ if md["durationMs"] != float64(123) {
+ t.Errorf("durationMs: got %v", md["durationMs"])
}
- if attrs["endpoint"] != "/api/v1" {
- t.Errorf("endpoint: got %v", attrs["endpoint"])
+ if md["endpoint"] != "/api/v1" {
+ t.Errorf("endpoint: got %v", md["endpoint"])
}
}
diff --git a/transports/otellog/example_test.go b/transports/otellog/example_test.go
index b84eb7f..bdf3d1d 100644
--- a/transports/otellog/example_test.go
+++ b/transports/otellog/example_test.go
@@ -1,8 +1,8 @@
package otellog_test
import (
- "go.loglayer.dev/transports/otellog/v2"
- "go.loglayer.dev/v2"
+ "go.loglayer.dev/transports/otellog/v3"
+ "go.loglayer.dev/v3"
)
// New emits log entries to an OpenTelemetry log.Logger. Name is the
diff --git a/transports/otellog/go.mod b/transports/otellog/go.mod
index 2f49d1a..3ea05f7 100644
--- a/transports/otellog/go.mod
+++ b/transports/otellog/go.mod
@@ -1,9 +1,9 @@
-module go.loglayer.dev/transports/otellog/v2
+module go.loglayer.dev/transports/otellog/v3
go 1.25.0
require (
- go.loglayer.dev/v2 v2.0.1
+ go.loglayer.dev/v3 v3.0.0
go.opentelemetry.io/otel/log v0.19.0
go.opentelemetry.io/otel/log/logtest v0.19.0
go.opentelemetry.io/otel/sdk v1.43.0
@@ -21,5 +21,5 @@ require (
go.opentelemetry.io/otel v1.43.0 // indirect
go.opentelemetry.io/otel/metric v1.43.0 // indirect
go.opentelemetry.io/otel/trace v1.43.0 // indirect
- golang.org/x/sys v0.42.0 // indirect
+ golang.org/x/sys v0.43.0 // indirect
)
diff --git a/transports/otellog/go.sum b/transports/otellog/go.sum
index 7a31a60..132a923 100644
--- a/transports/otellog/go.sum
+++ b/transports/otellog/go.sum
@@ -17,8 +17,8 @@ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRI
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
-go.loglayer.dev/v2 v2.0.1 h1:B7oYpkfMky0UG/N8IdKeIiiTi6h7eyj5NvRyEth9DHI=
-go.loglayer.dev/v2 v2.0.1/go.mod h1:+BWhs5AyICvCLBz07qHnCE12W34tArUWfXOba0Ct/QI=
+go.loglayer.dev/v3 v3.0.0 h1:TEkgoAO21SAE0vKzrNa53G3VG88TJCKs/fMo177uY8s=
+go.loglayer.dev/v3 v3.0.0/go.mod h1:ghbgQp7Y/Vo4/jPCLiokMZTy2DrA3X8dfqtgcWhy3pg=
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
go.opentelemetry.io/otel v1.43.0 h1:mYIM03dnh5zfN7HautFE4ieIig9amkNANT+xcVxAj9I=
@@ -39,7 +39,7 @@ go.opentelemetry.io/otel/trace v1.43.0 h1:BkNrHpup+4k4w+ZZ86CZoHHEkohws8AY+WTX09
go.opentelemetry.io/otel/trace v1.43.0/go.mod h1:/QJhyVBUUswCphDVxq+8mld+AvhXZLhe+8WVFxiFff0=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
-golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo=
-golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
+golang.org/x/sys v0.43.0 h1:Rlag2XtaFTxp19wS8MXlJwTvoh8ArU6ezoyFsMyCTNI=
+golang.org/x/sys v0.43.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
diff --git a/transports/otellog/livetest_test.go b/transports/otellog/livetest_test.go
index 23d3048..d5e400a 100644
--- a/transports/otellog/livetest_test.go
+++ b/transports/otellog/livetest_test.go
@@ -18,8 +18,8 @@ import (
"sync"
"testing"
- "go.loglayer.dev/transports/otellog/v2"
- "go.loglayer.dev/v2"
+ "go.loglayer.dev/transports/otellog/v3"
+ "go.loglayer.dev/v3"
otelapi "go.opentelemetry.io/otel/log"
sdklog "go.opentelemetry.io/otel/sdk/log"
sdktrace "go.opentelemetry.io/otel/sdk/trace"
diff --git a/transports/otellog/otellog.go b/transports/otellog/otellog.go
index 3a2018c..d5fa8aa 100644
--- a/transports/otellog/otellog.go
+++ b/transports/otellog/otellog.go
@@ -10,13 +10,13 @@
//
// The package name is otellog (not otel) to avoid a name collision with
// go.opentelemetry.io/otel. Import path is
-// go.loglayer.dev/transports/otellog/v2.
+// go.loglayer.dev/transports/otellog/v3.
//
// Wiring with the global LoggerProvider (most common):
//
// import (
-// "go.loglayer.dev/v2"
-// "go.loglayer.dev/transports/otellog/v2"
+// "go.loglayer.dev/v3"
+// "go.loglayer.dev/transports/otellog/v3"
// )
//
// tr := otellog.New(otellog.Config{Name: "checkout-api"})
@@ -44,8 +44,8 @@ import (
"fmt"
"time"
- "go.loglayer.dev/v2"
- "go.loglayer.dev/v2/transport"
+ "go.loglayer.dev/v3"
+ "go.loglayer.dev/v3/transport"
otellog "go.opentelemetry.io/otel/log"
"go.opentelemetry.io/otel/log/global"
)
diff --git a/transports/otellog/otellog_test.go b/transports/otellog/otellog_test.go
index d54f495..6683e8a 100644
--- a/transports/otellog/otellog_test.go
+++ b/transports/otellog/otellog_test.go
@@ -5,9 +5,9 @@ import (
"errors"
"testing"
- "go.loglayer.dev/transports/otellog/v2"
- "go.loglayer.dev/v2"
- "go.loglayer.dev/v2/transport"
+ "go.loglayer.dev/transports/otellog/v3"
+ "go.loglayer.dev/v3"
+ "go.loglayer.dev/v3/transport"
otelapi "go.opentelemetry.io/otel/log"
"go.opentelemetry.io/otel/log/logtest"
)
@@ -112,15 +112,20 @@ func TestLevels(t *testing.T) {
}
}
-func TestMapMetadataFlattens(t *testing.T) {
+func TestMapMetadataNestsUnderKey(t *testing.T) {
log, rec := newLogger(t, otellog.Config{})
log.WithMetadata(loglayer.Metadata{"requestId": "abc", "n": 42}).Info("req")
a := attrs(lastRecord(t, rec))
- if a["requestId"].AsString() != "abc" {
- t.Errorf("requestId: got %v", a["requestId"])
+ meta := a["metadata"]
+ if meta.Kind() != otelapi.KindMap {
+ t.Fatalf("metadata should be Map, got %v", meta.Kind())
+ }
+ got := kvMap(meta.AsMap())
+ if got["requestId"].AsString() != "abc" {
+ t.Errorf("requestId: got %v", got["requestId"])
}
- if a["n"].AsInt64() != 42 {
- t.Errorf("n: got %v", a["n"])
+ if got["n"].AsInt64() != 42 {
+ t.Errorf("n: got %v", got["n"])
}
}
@@ -320,8 +325,13 @@ func TestNestedSliceAndMapAttributes(t *testing.T) {
"inner": map[string]any{"k": 1, "ok": true},
}).Info("nested")
a := attrs(lastRecord(t, rec))
+ meta := a["metadata"]
+ if meta.Kind() != otelapi.KindMap {
+ t.Fatalf("metadata should be Map, got %v", meta.Kind())
+ }
+ got := kvMap(meta.AsMap())
- tags := a["tags"]
+ tags := got["tags"]
if tags.Kind() != otelapi.KindSlice {
t.Fatalf("tags: kind %v, want KindSlice", tags.Kind())
}
@@ -329,12 +339,12 @@ func TestNestedSliceAndMapAttributes(t *testing.T) {
if len(tagSlice) != 3 || tagSlice[0].AsString() != "a" || tagSlice[2].AsString() != "c" {
t.Errorf("tags: got %v", tagSlice)
}
- inner := a["inner"]
+ inner := got["inner"]
if inner.Kind() != otelapi.KindMap {
t.Fatalf("inner: kind %v, want KindMap", inner.Kind())
}
- got := kvMap(inner.AsMap())
- if got["k"].AsInt64() != 1 || got["ok"].AsBool() != true {
- t.Errorf("inner: got %v", got)
+ innerKV := kvMap(inner.AsMap())
+ if innerKV["k"].AsInt64() != 1 || innerKV["ok"].AsBool() != true {
+ t.Errorf("inner: got %v", innerKV)
}
}
diff --git a/transports/phuslu/bench_test.go b/transports/phuslu/bench_test.go
index 5f56874..8b993ab 100644
--- a/transports/phuslu/bench_test.go
+++ b/transports/phuslu/bench_test.go
@@ -5,10 +5,10 @@ import (
plog "github.com/phuslu/log"
- llphuslu "go.loglayer.dev/transports/phuslu/v2"
- "go.loglayer.dev/v2"
- "go.loglayer.dev/v2/transport"
- "go.loglayer.dev/v2/transport/benchtest"
+ llphuslu "go.loglayer.dev/transports/phuslu/v3"
+ "go.loglayer.dev/v3"
+ "go.loglayer.dev/v3/transport"
+ "go.loglayer.dev/v3/transport/benchtest"
)
func newDirect() *plog.Logger {
diff --git a/transports/phuslu/example_test.go b/transports/phuslu/example_test.go
index 4a8626d..db2e23d 100644
--- a/transports/phuslu/example_test.go
+++ b/transports/phuslu/example_test.go
@@ -3,8 +3,8 @@ package phuslu_test
import (
"io"
- "go.loglayer.dev/transports/phuslu/v2"
- "go.loglayer.dev/v2"
+ "go.loglayer.dev/transports/phuslu/v3"
+ "go.loglayer.dev/v3"
)
// New wraps a *phuslu/log.Logger. When Logger is nil a default logger
diff --git a/transports/phuslu/go.mod b/transports/phuslu/go.mod
index b2543e9..82cb354 100644
--- a/transports/phuslu/go.mod
+++ b/transports/phuslu/go.mod
@@ -1,10 +1,10 @@
-module go.loglayer.dev/transports/phuslu/v2
+module go.loglayer.dev/transports/phuslu/v3
go 1.25.0
require (
github.com/phuslu/log v1.0.124
- go.loglayer.dev/v2 v2.0.1
+ go.loglayer.dev/v3 v3.0.0
)
require github.com/goccy/go-json v0.10.6 // indirect
diff --git a/transports/phuslu/go.sum b/transports/phuslu/go.sum
index 6e27b01..18bb142 100644
--- a/transports/phuslu/go.sum
+++ b/transports/phuslu/go.sum
@@ -2,7 +2,7 @@ github.com/goccy/go-json v0.10.6 h1:p8HrPJzOakx/mn/bQtjgNjdTcN+/S6FcG2CTtQOrHVU=
github.com/goccy/go-json v0.10.6/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
github.com/phuslu/log v1.0.124 h1:jQMyco4WVPW+0gf6R0cdgtsnFu86z1MbcJv+oWuAXIA=
github.com/phuslu/log v1.0.124/go.mod h1:F8osGJADo5qLK/0F88djWwdyoZZ9xDJQL1HYRHFEkS0=
-go.loglayer.dev/v2 v2.0.1 h1:B7oYpkfMky0UG/N8IdKeIiiTi6h7eyj5NvRyEth9DHI=
-go.loglayer.dev/v2 v2.0.1/go.mod h1:+BWhs5AyICvCLBz07qHnCE12W34tArUWfXOba0Ct/QI=
+go.loglayer.dev/v3 v3.0.0 h1:TEkgoAO21SAE0vKzrNa53G3VG88TJCKs/fMo177uY8s=
+go.loglayer.dev/v3 v3.0.0/go.mod h1:ghbgQp7Y/Vo4/jPCLiokMZTy2DrA3X8dfqtgcWhy3pg=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
diff --git a/transports/phuslu/phuslu.go b/transports/phuslu/phuslu.go
index 3e0c5cc..131601b 100644
--- a/transports/phuslu/phuslu.go
+++ b/transports/phuslu/phuslu.go
@@ -8,8 +8,8 @@ import (
plog "github.com/phuslu/log"
- "go.loglayer.dev/v2"
- "go.loglayer.dev/v2/transport"
+ "go.loglayer.dev/v3"
+ "go.loglayer.dev/v3/transport"
)
// Config holds configuration options for the phuslu transport.
diff --git a/transports/phuslu/phuslu_test.go b/transports/phuslu/phuslu_test.go
index d6d7bcf..cff0e81 100644
--- a/transports/phuslu/phuslu_test.go
+++ b/transports/phuslu/phuslu_test.go
@@ -6,10 +6,10 @@ import (
plog "github.com/phuslu/log"
- llphuslu "go.loglayer.dev/transports/phuslu/v2"
- "go.loglayer.dev/v2"
- "go.loglayer.dev/v2/transport"
- "go.loglayer.dev/v2/transport/transporttest"
+ llphuslu "go.loglayer.dev/transports/phuslu/v3"
+ "go.loglayer.dev/v3"
+ "go.loglayer.dev/v3/transport"
+ "go.loglayer.dev/v3/transport/transporttest"
)
func factory(opts transporttest.FactoryOpts) (*loglayer.LogLayer, *bytes.Buffer) {
diff --git a/transports/pretty/bench_test.go b/transports/pretty/bench_test.go
index 8605e08..7fc1058 100644
--- a/transports/pretty/bench_test.go
+++ b/transports/pretty/bench_test.go
@@ -3,10 +3,10 @@ package pretty_test
import (
"testing"
- "go.loglayer.dev/transports/pretty/v2"
- "go.loglayer.dev/v2"
- "go.loglayer.dev/v2/transport"
- "go.loglayer.dev/v2/transport/benchtest"
+ "go.loglayer.dev/transports/pretty/v3"
+ "go.loglayer.dev/v3"
+ "go.loglayer.dev/v3/transport"
+ "go.loglayer.dev/v3/transport/benchtest"
)
func newPrettyLogger() *loglayer.LogLayer {
diff --git a/transports/pretty/example_test.go b/transports/pretty/example_test.go
index fa68cfd..6cd5ba1 100644
--- a/transports/pretty/example_test.go
+++ b/transports/pretty/example_test.go
@@ -4,8 +4,8 @@ import (
"os"
"time"
- "go.loglayer.dev/transports/pretty/v2"
- "go.loglayer.dev/v2"
+ "go.loglayer.dev/transports/pretty/v3"
+ "go.loglayer.dev/v3"
)
// New builds the colorized terminal transport. NoColor and a fixed
diff --git a/transports/pretty/go.mod b/transports/pretty/go.mod
index cbf3593..a1229be 100644
--- a/transports/pretty/go.mod
+++ b/transports/pretty/go.mod
@@ -1,15 +1,15 @@
-module go.loglayer.dev/transports/pretty/v2
+module go.loglayer.dev/transports/pretty/v3
go 1.25.0
require (
github.com/fatih/color v1.19.0
github.com/goccy/go-json v0.10.6
- go.loglayer.dev/v2 v2.1.0
+ go.loglayer.dev/v3 v3.0.0
)
require (
github.com/mattn/go-colorable v0.1.14 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
- golang.org/x/sys v0.42.0 // indirect
+ golang.org/x/sys v0.43.0 // indirect
)
diff --git a/transports/pretty/go.sum b/transports/pretty/go.sum
index 7fb0154..1252d85 100644
--- a/transports/pretty/go.sum
+++ b/transports/pretty/go.sum
@@ -6,10 +6,10 @@ github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHP
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
-go.loglayer.dev/v2 v2.1.0 h1:8/fq8Z1NNLtjfKgaPn6S0Hy7zWPnkjn9Gj3YgEDFk4w=
-go.loglayer.dev/v2 v2.1.0/go.mod h1:+BWhs5AyICvCLBz07qHnCE12W34tArUWfXOba0Ct/QI=
+go.loglayer.dev/v3 v3.0.0 h1:TEkgoAO21SAE0vKzrNa53G3VG88TJCKs/fMo177uY8s=
+go.loglayer.dev/v3 v3.0.0/go.mod h1:ghbgQp7Y/Vo4/jPCLiokMZTy2DrA3X8dfqtgcWhy3pg=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo=
-golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
+golang.org/x/sys v0.43.0 h1:Rlag2XtaFTxp19wS8MXlJwTvoh8ArU6ezoyFsMyCTNI=
+golang.org/x/sys v0.43.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
diff --git a/transports/pretty/pretty.go b/transports/pretty/pretty.go
index e1eca37..9538fb8 100644
--- a/transports/pretty/pretty.go
+++ b/transports/pretty/pretty.go
@@ -11,9 +11,9 @@ import (
"strings"
"time"
- "go.loglayer.dev/v2"
- "go.loglayer.dev/v2/transport"
- "go.loglayer.dev/v2/utils/sanitize"
+ "go.loglayer.dev/v3"
+ "go.loglayer.dev/v3/transport"
+ "go.loglayer.dev/v3/utils/sanitize"
)
// ViewMode controls how each log entry is rendered.
diff --git a/transports/pretty/pretty_test.go b/transports/pretty/pretty_test.go
index be05c77..fa55b72 100644
--- a/transports/pretty/pretty_test.go
+++ b/transports/pretty/pretty_test.go
@@ -9,9 +9,9 @@ import (
"github.com/fatih/color"
- "go.loglayer.dev/transports/pretty/v2"
- "go.loglayer.dev/v2"
- "go.loglayer.dev/v2/transport"
+ "go.loglayer.dev/transports/pretty/v3"
+ "go.loglayer.dev/v3"
+ "go.loglayer.dev/v3/transport"
)
const fixedTime = "12:34:56.789"
@@ -47,7 +47,7 @@ func TestInlineWithMetadataMap(t *testing.T) {
log.WithMetadata(map[string]any{"user": "alice", "n": 42}).Info("served")
got := buf.String()
// keys are sorted
- want := "12:34:56.789 ▶ INFO served n=42 user=alice\n"
+ want := "12:34:56.789 ▶ INFO served metadata={n=42 user=alice}\n"
if got != want {
t.Errorf("got %q, want %q", got, want)
}
@@ -61,7 +61,7 @@ func TestInlineWithMetadataStruct(t *testing.T) {
log, buf := newLogger(pretty.Config{})
log.WithMetadata(user{ID: 7, Name: "Alice"}).Info("hi")
got := buf.String()
- want := "12:34:56.789 ▶ INFO hi id=7 name=Alice\n"
+ want := "12:34:56.789 ▶ INFO hi metadata={id=7 name=Alice}\n"
if got != want {
t.Errorf("got %q, want %q", got, want)
}
@@ -105,7 +105,7 @@ func TestExpandedMode(t *testing.T) {
"nested": map[string]any{"a": 1, "b": 2},
}).Info("served")
got := buf.String()
- want := "12:34:56.789 ▶ INFO served\n nested:\n a: 1\n b: 2\n user: alice\n"
+ want := "12:34:56.789 ▶ INFO served\n metadata:\n nested:\n a: 1\n b: 2\n user: alice\n"
if got != want {
t.Errorf("got %q\nwant %q", got, want)
}
@@ -117,7 +117,7 @@ func TestExpandedModeArrays(t *testing.T) {
"items": []any{"a", "b", "c"},
}).Info("list")
got := buf.String()
- want := "12:34:56.789 ▶ INFO list\n items:\n - a\n - b\n - c\n"
+ want := "12:34:56.789 ▶ INFO list\n metadata:\n items:\n - a\n - b\n - c\n"
if got != want {
t.Errorf("got %q\nwant %q", got, want)
}
@@ -154,11 +154,12 @@ func TestExpandedModeDeeplyNested(t *testing.T) {
// level 3: "city" (len 4) and "zip" (len 3); maxKey = 4, "zip" pads.
want := "" +
"12:34:56.789 ▶ INFO served\n" +
- " user:\n" +
- " address:\n" +
- " city: Brooklyn\n" +
- " zip: 11201\n" +
- " id: 42\n"
+ " metadata:\n" +
+ " user:\n" +
+ " address:\n" +
+ " city: Brooklyn\n" +
+ " zip: 11201\n" +
+ " id: 42\n"
if got != want {
t.Errorf("got %q\nwant %q", got, want)
}
@@ -181,14 +182,15 @@ func TestExpandedModeAlignedColumns(t *testing.T) {
// Among same-line keys: a (1), longerKey (9), middle (6). maxKey = 9.
want := "" +
"12:34:56.789 ▶ INFO aligned\n" +
- " a: 1\n" +
- " alsoIgnored:\n" +
- " - a\n" +
- " - b\n" +
- " longerKey: v\n" +
- " middle: true\n" +
- " nested:\n" +
- " x: 1\n"
+ " metadata:\n" +
+ " a: 1\n" +
+ " alsoIgnored:\n" +
+ " - a\n" +
+ " - b\n" +
+ " longerKey: v\n" +
+ " middle: true\n" +
+ " nested:\n" +
+ " x: 1\n"
if got != want {
t.Errorf("got %q\nwant %q", got, want)
}
@@ -328,12 +330,10 @@ func TestInlineDepthLimit(t *testing.T) {
"deep": map[string]any{"nested": "x"},
}).Info("depth")
got := buf.String()
- // At depth 1, the nested map should collapse to {...}
- if !strings.Contains(got, "deep={...}") {
- t.Errorf("expected deep={...} truncation, got: %q", got)
- }
- if !strings.Contains(got, "shallow=ok") {
- t.Errorf("expected shallow=ok, got: %q", got)
+ // At depth 1 the nested metadata bag's own keys render inline, and only
+ // deeper nesting truncates (matching the v2 root-merge UX under nesting).
+ if !strings.Contains(got, "metadata={deep={...} shallow=ok}") {
+ t.Errorf("expected deep={...} truncation with shallow inline, got: %q", got)
}
}
diff --git a/transports/pretty/render.go b/transports/pretty/render.go
index c920624..400f2af 100644
--- a/transports/pretty/render.go
+++ b/transports/pretty/render.go
@@ -9,7 +9,7 @@ import (
"github.com/goccy/go-json"
- "go.loglayer.dev/v2"
+ "go.loglayer.dev/v3"
)
// combineData merges Data (fields + error) and Metadata into a single map for
@@ -27,7 +27,13 @@ func combineData(params loglayer.TransportParams) map[string]any {
maps.Copy(out, params.Data)
if params.Metadata != nil {
if key := params.Schema.MetadataFieldName; key != "" {
- out[key] = metadataValueForKey(params.Metadata)
+ // The nested value is a map (or struct roundtripped to one);
+ // renderInline gives it depth-1 like any root value, which at
+ // low MaxInlineDepth collapses the whole bag to {...}. Wrap it
+ // in a depthMarker so formatValueInline can grant it one extra
+ // level: the bag's own scalar keys render, and only deeper
+ // nesting truncates (matching the v2 root-merge behavior).
+ out[key] = depthMarker{metadataValueForKey(params.Metadata)}
} else {
maps.Copy(out, metadataAsRootFields(params.Metadata))
}
@@ -38,6 +44,13 @@ func combineData(params loglayer.TransportParams) map[string]any {
return out
}
+// depthMarker wraps a value that should render with one extra level of
+// inline depth budget (used for the nested metadata value so its top-level
+// keys survive low MaxInlineDepth settings).
+type depthMarker struct {
+ value any
+}
+
// metadataValueForKey returns a value suitable for nesting under a single
// key in the YAML output. Maps and structs JSON-roundtrip into a generic
// map[string]any (so writeMap renders nested keys and slices land as []any
@@ -94,6 +107,9 @@ func (t *Transport) formatValueInline(v any, depth int) string {
if v == nil {
return "null"
}
+ if m, ok := v.(depthMarker); ok {
+ return t.formatValueInline(m.value, depth+1)
+ }
switch val := v.(type) {
case string:
return strconvQuoteIfNeeded(val)
@@ -158,6 +174,9 @@ func (t *Transport) writeMap(w io.Writer, m map[string]any, indent int) {
}
for _, k := range keys {
v := m[k]
+ if mk, ok := v.(depthMarker); ok {
+ v = mk.value
+ }
switch val := v.(type) {
case map[string]any:
if len(val) == 0 {
@@ -191,6 +210,9 @@ func (t *Transport) writeAlignedScalar(w io.Writer, prefix, key string, maxKey i
// sameLineValue reports whether v renders on the same line as its key
// (scalars and empty containers do; non-empty maps and slices recurse).
func sameLineValue(v any) bool {
+ if mk, ok := v.(depthMarker); ok {
+ v = mk.value
+ }
switch val := v.(type) {
case map[string]any:
return len(val) == 0
diff --git a/transports/sentry/example_test.go b/transports/sentry/example_test.go
index 3fc9047..4f95996 100644
--- a/transports/sentry/example_test.go
+++ b/transports/sentry/example_test.go
@@ -5,8 +5,8 @@ import (
"github.com/getsentry/sentry-go"
- sentrytransport "go.loglayer.dev/transports/sentry/v2"
- "go.loglayer.dev/v2"
+ sentrytransport "go.loglayer.dev/transports/sentry/v3"
+ "go.loglayer.dev/v3"
)
// New forwards entries to a caller-supplied sentry.Logger. The Logger
diff --git a/transports/sentry/go.mod b/transports/sentry/go.mod
index 0143876..8564691 100644
--- a/transports/sentry/go.mod
+++ b/transports/sentry/go.mod
@@ -1,14 +1,15 @@
-module go.loglayer.dev/transports/sentry/v2
+module go.loglayer.dev/transports/sentry/v3
go 1.25.0
require (
github.com/getsentry/sentry-go v0.46.1
github.com/goccy/go-json v0.10.6
- go.loglayer.dev/v2 v2.0.1
+ go.loglayer.dev/v3 v3.0.0
)
require (
- golang.org/x/sys v0.18.0 // indirect
- golang.org/x/text v0.14.0 // indirect
+ github.com/google/go-cmp v0.7.0 // indirect
+ golang.org/x/sys v0.43.0 // indirect
+ golang.org/x/text v0.36.0 // indirect
)
diff --git a/transports/sentry/go.sum b/transports/sentry/go.sum
index b423fdc..85250a2 100644
--- a/transports/sentry/go.sum
+++ b/transports/sentry/go.sum
@@ -6,8 +6,8 @@ github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxI
github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og=
github.com/goccy/go-json v0.10.6 h1:p8HrPJzOakx/mn/bQtjgNjdTcN+/S6FcG2CTtQOrHVU=
github.com/goccy/go-json v0.10.6/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
-github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
-github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
+github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
+github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4=
github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
@@ -16,13 +16,13 @@ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRI
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
-go.loglayer.dev/v2 v2.0.1 h1:B7oYpkfMky0UG/N8IdKeIiiTi6h7eyj5NvRyEth9DHI=
-go.loglayer.dev/v2 v2.0.1/go.mod h1:+BWhs5AyICvCLBz07qHnCE12W34tArUWfXOba0Ct/QI=
+go.loglayer.dev/v3 v3.0.0 h1:TEkgoAO21SAE0vKzrNa53G3VG88TJCKs/fMo177uY8s=
+go.loglayer.dev/v3 v3.0.0/go.mod h1:ghbgQp7Y/Vo4/jPCLiokMZTy2DrA3X8dfqtgcWhy3pg=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
-golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
-golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
-golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
-golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
+golang.org/x/sys v0.43.0 h1:Rlag2XtaFTxp19wS8MXlJwTvoh8ArU6ezoyFsMyCTNI=
+golang.org/x/sys v0.43.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
+golang.org/x/text v0.36.0 h1:JfKh3XmcRPqZPKevfXVpI1wXPTqbkE5f7JA92a55Yxg=
+golang.org/x/text v0.36.0/go.mod h1:NIdBknypM8iqVmPiuco0Dh6P5Jcdk8lJL0CUebqK164=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
diff --git a/transports/sentry/livetest_test.go b/transports/sentry/livetest_test.go
index 6bc04d7..f250bdb 100644
--- a/transports/sentry/livetest_test.go
+++ b/transports/sentry/livetest_test.go
@@ -26,9 +26,9 @@ import (
"github.com/getsentry/sentry-go"
- sentrytransport "go.loglayer.dev/transports/sentry/v2"
- "go.loglayer.dev/v2/transport/transporttest"
- "go.loglayer.dev/v2/utils/idgen"
+ sentrytransport "go.loglayer.dev/transports/sentry/v3"
+ "go.loglayer.dev/v3/transport/transporttest"
+ "go.loglayer.dev/v3/utils/idgen"
)
func TestLive_Sentry_SendsLog(t *testing.T) {
diff --git a/transports/sentry/sentry.go b/transports/sentry/sentry.go
index bc2b0a8..ad6c27a 100644
--- a/transports/sentry/sentry.go
+++ b/transports/sentry/sentry.go
@@ -21,8 +21,8 @@ import (
"github.com/getsentry/sentry-go"
"github.com/goccy/go-json"
- "go.loglayer.dev/v2"
- "go.loglayer.dev/v2/transport"
+ "go.loglayer.dev/v3"
+ "go.loglayer.dev/v3/transport"
)
// Config holds configuration options for the Sentry transport.
diff --git a/transports/sentry/sentry_test.go b/transports/sentry/sentry_test.go
index 627f492..d47e4a2 100644
--- a/transports/sentry/sentry_test.go
+++ b/transports/sentry/sentry_test.go
@@ -9,9 +9,9 @@ import (
"github.com/getsentry/sentry-go"
"github.com/getsentry/sentry-go/attribute"
- sentrytransport "go.loglayer.dev/transports/sentry/v2"
- "go.loglayer.dev/v2"
- "go.loglayer.dev/v2/transport"
+ sentrytransport "go.loglayer.dev/transports/sentry/v3"
+ "go.loglayer.dev/v3"
+ "go.loglayer.dev/v3/transport"
)
// fakeLogger satisfies sentry.Logger and records every entry built
@@ -197,7 +197,7 @@ func TestSentry_ErrorAttachesUnderErrKey(t *testing.T) {
}
}
-func TestSentry_MapMetadataFlattens(t *testing.T) {
+func TestSentry_MapMetadataNestsUnderKey(t *testing.T) {
fake := newFakeLogger()
log := newLogger(t, fake)
log.WithMetadata(loglayer.Metadata{"durationMs": 42, "op": "load"}).Info("did")
@@ -205,12 +205,12 @@ func TestSentry_MapMetadataFlattens(t *testing.T) {
if len(fake.entries) != 1 {
t.Fatalf("expected 1 entry, got %d", len(fake.entries))
}
- attrs := fake.entries[0].attrs
- if attrs["durationMs"] != 42 {
- t.Errorf("durationMs: got %v", attrs["durationMs"])
+ got, ok := fake.entries[0].attrs["metadata"].(string)
+ if !ok {
+ t.Fatalf("metadata: got %T, want string", fake.entries[0].attrs["metadata"])
}
- if attrs["op"] != "load" {
- t.Errorf("op: got %v", attrs["op"])
+ if got != `{"durationMs":42,"op":"load"}` {
+ t.Errorf("metadata: got %q, want %q", got, `{"durationMs":42,"op":"load"}`)
}
}
diff --git a/transports/slog/example_test.go b/transports/slog/example_test.go
index 14a0a1f..8731d66 100644
--- a/transports/slog/example_test.go
+++ b/transports/slog/example_test.go
@@ -3,8 +3,8 @@ package slog_test
import (
"io"
- llslog "go.loglayer.dev/transports/slog/v2"
- "go.loglayer.dev/v2"
+ llslog "go.loglayer.dev/transports/slog/v3"
+ "go.loglayer.dev/v3"
)
// New wraps a *slog.Logger. When Logger is nil a default logger is
diff --git a/transports/slog/go.mod b/transports/slog/go.mod
index 400cedc..310dc62 100644
--- a/transports/slog/go.mod
+++ b/transports/slog/go.mod
@@ -1,7 +1,7 @@
-module go.loglayer.dev/transports/slog/v2
+module go.loglayer.dev/transports/slog/v3
go 1.25.0
-require go.loglayer.dev/v2 v2.0.1
+require go.loglayer.dev/v3 v3.0.0
require github.com/goccy/go-json v0.10.6 // indirect
diff --git a/transports/slog/go.sum b/transports/slog/go.sum
index c00692c..42ec334 100644
--- a/transports/slog/go.sum
+++ b/transports/slog/go.sum
@@ -1,6 +1,6 @@
github.com/goccy/go-json v0.10.6 h1:p8HrPJzOakx/mn/bQtjgNjdTcN+/S6FcG2CTtQOrHVU=
github.com/goccy/go-json v0.10.6/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
-go.loglayer.dev/v2 v2.0.1 h1:B7oYpkfMky0UG/N8IdKeIiiTi6h7eyj5NvRyEth9DHI=
-go.loglayer.dev/v2 v2.0.1/go.mod h1:+BWhs5AyICvCLBz07qHnCE12W34tArUWfXOba0Ct/QI=
+go.loglayer.dev/v3 v3.0.0 h1:TEkgoAO21SAE0vKzrNa53G3VG88TJCKs/fMo177uY8s=
+go.loglayer.dev/v3 v3.0.0/go.mod h1:ghbgQp7Y/Vo4/jPCLiokMZTy2DrA3X8dfqtgcWhy3pg=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
diff --git a/transports/slog/slog.go b/transports/slog/slog.go
index aba96ae..1bafa2f 100644
--- a/transports/slog/slog.go
+++ b/transports/slog/slog.go
@@ -13,8 +13,8 @@ import (
"io"
"log/slog"
- "go.loglayer.dev/v2"
- "go.loglayer.dev/v2/transport"
+ "go.loglayer.dev/v3"
+ "go.loglayer.dev/v3/transport"
)
// Config holds configuration options for the slog transport.
diff --git a/transports/slog/slog_test.go b/transports/slog/slog_test.go
index 50b3802..64ab0ff 100644
--- a/transports/slog/slog_test.go
+++ b/transports/slog/slog_test.go
@@ -6,10 +6,10 @@ import (
stdlibslog "log/slog"
"testing"
- llslog "go.loglayer.dev/transports/slog/v2"
- "go.loglayer.dev/v2"
- "go.loglayer.dev/v2/transport"
- "go.loglayer.dev/v2/transport/transporttest"
+ llslog "go.loglayer.dev/transports/slog/v3"
+ "go.loglayer.dev/v3"
+ "go.loglayer.dev/v3/transport"
+ "go.loglayer.dev/v3/transport/transporttest"
)
func factory(opts transporttest.FactoryOpts) (*loglayer.LogLayer, *bytes.Buffer) {
diff --git a/transports/structured/bench_test.go b/transports/structured/bench_test.go
index f7da3bb..a7a44fd 100644
--- a/transports/structured/bench_test.go
+++ b/transports/structured/bench_test.go
@@ -7,10 +7,10 @@ package structured_test
import (
"testing"
- "go.loglayer.dev/transports/structured/v2"
- "go.loglayer.dev/v2"
- "go.loglayer.dev/v2/transport"
- "go.loglayer.dev/v2/transport/benchtest"
+ "go.loglayer.dev/transports/structured/v3"
+ "go.loglayer.dev/v3"
+ "go.loglayer.dev/v3/transport"
+ "go.loglayer.dev/v3/transport/benchtest"
)
func BenchmarkRender_Structured_SimpleMessage(b *testing.B) {
diff --git a/transports/structured/example_test.go b/transports/structured/example_test.go
index 02b37c6..dbfb30c 100644
--- a/transports/structured/example_test.go
+++ b/transports/structured/example_test.go
@@ -1,8 +1,8 @@
package structured_test
import (
- "go.loglayer.dev/transports/structured/v2"
- "go.loglayer.dev/v2"
+ "go.loglayer.dev/transports/structured/v3"
+ "go.loglayer.dev/v3"
)
// New builds a structured-JSON transport. DateFn returns a fixed
@@ -16,5 +16,5 @@ func ExampleNew() {
DisableFatalExit: true,
})
log.WithMetadata(loglayer.Metadata{"durationMs": 42}).Info("served")
- // Output: {"level":"info","time":"2026-04-26T12:00:00Z","msg":"served","durationMs":42}
+ // Output: {"level":"info","time":"2026-04-26T12:00:00Z","msg":"served","metadata":{"durationMs":42}}
}
diff --git a/transports/structured/go.mod b/transports/structured/go.mod
index 180dee7..8d9d371 100644
--- a/transports/structured/go.mod
+++ b/transports/structured/go.mod
@@ -1,8 +1,8 @@
-module go.loglayer.dev/transports/structured/v2
+module go.loglayer.dev/transports/structured/v3
go 1.25.0
require (
github.com/goccy/go-json v0.10.6
- go.loglayer.dev/v2 v2.0.1
+ go.loglayer.dev/v3 v3.0.0
)
diff --git a/transports/structured/go.sum b/transports/structured/go.sum
index c00692c..42ec334 100644
--- a/transports/structured/go.sum
+++ b/transports/structured/go.sum
@@ -1,6 +1,6 @@
github.com/goccy/go-json v0.10.6 h1:p8HrPJzOakx/mn/bQtjgNjdTcN+/S6FcG2CTtQOrHVU=
github.com/goccy/go-json v0.10.6/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
-go.loglayer.dev/v2 v2.0.1 h1:B7oYpkfMky0UG/N8IdKeIiiTi6h7eyj5NvRyEth9DHI=
-go.loglayer.dev/v2 v2.0.1/go.mod h1:+BWhs5AyICvCLBz07qHnCE12W34tArUWfXOba0Ct/QI=
+go.loglayer.dev/v3 v3.0.0 h1:TEkgoAO21SAE0vKzrNa53G3VG88TJCKs/fMo177uY8s=
+go.loglayer.dev/v3 v3.0.0/go.mod h1:ghbgQp7Y/Vo4/jPCLiokMZTy2DrA3X8dfqtgcWhy3pg=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
diff --git a/transports/structured/structured.go b/transports/structured/structured.go
index d7a58d7..4332106 100644
--- a/transports/structured/structured.go
+++ b/transports/structured/structured.go
@@ -12,8 +12,9 @@ import (
"github.com/goccy/go-json"
- "go.loglayer.dev/v2"
- "go.loglayer.dev/v2/transport"
+ "go.loglayer.dev/v3"
+ "go.loglayer.dev/v3/transport"
+ "go.loglayer.dev/v3/utils/sanitize"
)
// Config holds configuration options for Transport.
@@ -121,6 +122,15 @@ func (s *Transport) GetLoggerInstance() any { return nil }
// entries in iteration order. Per-value marshaling uses goccy/go-json so
// structs, slices, and json.Marshaler types render exactly as encoding/json
// would.
+//
+// The message and top-level keys and string values are run through
+// sanitize.Message before encoding, so untrusted input can't forge log lines
+// (CR / LF), smuggle ANSI escape sequences (ESC), or spoof text direction
+// (bidi overrides). Sanitization is top-level only: string values nested
+// inside structs, slices, or maps, and values with a custom json.Marshaler,
+// remain the JSON encoder's domain, which escapes control characters but
+// passes other printable-but-formatting runes
+// (e.g. bidi overrides) through untouched.
func (s *Transport) SendToLogger(params loglayer.TransportParams) {
if !s.ShouldProcess(params.LogLevel) {
return
@@ -137,16 +147,36 @@ func (s *Transport) SendToLogger(params loglayer.TransportParams) {
buf.Reset()
defer putBuffer(buf)
+ // AssembleMessage sanitizes per element and preserves authored line
+ // boundaries inside *loglayer.MultilineMessage values, so multiline
+ // messages survive while ANSI/control bytes are still stripped.
+ msg := transport.AssembleMessage(messages, sanitize.Message)
+
buf.Write(s.levelOpen)
s.writeLevel(buf, params.LogLevel)
buf.Write(s.dateOpen)
s.writeDate(buf)
- buf.Write(s.msgOpen)
- writeJSONString(buf, transport.JoinMessages(messages))
+ if msg != "" {
+ buf.Write(s.msgOpen)
+ writeJSONString(buf, msg)
+ }
+ // When metadata nests under the schema key, a Data key that collides
+ // with that key is dropped so the JSON object can't contain duplicate
+ // keys; the nested metadata value wins (matching transport.MergeIntoMap).
+ // The comparison is on the sanitized key (what writeKeyValue emits), so
+ // a hostile raw key that sanitizes into the nest key can't slip through.
+ skipKey := ""
+ if params.Metadata != nil {
+ skipKey = params.Schema.MetadataFieldName
+ }
for k, v := range params.Data {
+ sk := sanitize.Message(k)
+ if sk == skipKey {
+ continue
+ }
buf.WriteByte(',')
- if err := writeKeyValue(buf, k, v); err != nil {
+ if err := writeKeyValue(buf, sk, v); err != nil {
s.writeMarshalError(err)
return
}
@@ -155,14 +185,14 @@ func (s *Transport) SendToLogger(params loglayer.TransportParams) {
if key := params.Schema.MetadataFieldName; key != "" {
// Whole metadata value nests under the configured key.
buf.WriteByte(',')
- if err := writeKeyValue(buf, key, params.Metadata); err != nil {
+ if err := writeKeyValue(buf, sanitize.Message(key), params.Metadata); err != nil {
s.writeMarshalError(err)
return
}
} else {
for k, v := range transport.MetadataAsMap(params.Metadata) {
buf.WriteByte(',')
- if err := writeKeyValue(buf, k, v); err != nil {
+ if err := writeKeyValue(buf, sanitize.Message(k), v); err != nil {
s.writeMarshalError(err)
return
}
@@ -217,6 +247,13 @@ func writeJSONString(buf *bytes.Buffer, s string) {
func writeKeyValue(buf *bytes.Buffer, key string, value any) error {
writeJSONString(buf, key)
buf.WriteByte(':')
+ if s, ok := value.(string); ok {
+ // User-controlled strings (fields, metadata, error messages) get the
+ // same ANSI/bidi/control-char sanitization as the message; non-string
+ // values are the JSON encoder's domain.
+ writeJSONString(buf, sanitize.Message(s))
+ return nil
+ }
b, err := json.Marshal(value)
if err != nil {
return err
diff --git a/transports/structured/structured_test.go b/transports/structured/structured_test.go
index 8a56f04..896dac6 100644
--- a/transports/structured/structured_test.go
+++ b/transports/structured/structured_test.go
@@ -6,10 +6,10 @@ import (
"strings"
"testing"
- "go.loglayer.dev/transports/structured/v2"
- "go.loglayer.dev/v2"
- "go.loglayer.dev/v2/transport"
- "go.loglayer.dev/v2/transport/transporttest"
+ "go.loglayer.dev/transports/structured/v3"
+ "go.loglayer.dev/v3"
+ "go.loglayer.dev/v3/transport"
+ "go.loglayer.dev/v3/transport/transporttest"
)
func newLogger(cfg structured.Config) (*loglayer.LogLayer, *bytes.Buffer) {
@@ -79,8 +79,8 @@ func TestStructuredWithMetadataMap(t *testing.T) {
log, buf := newLogger(structured.Config{})
log.WithMetadata(map[string]any{"requestId": "xyz"}).Info("req")
obj := transporttest.ParseJSONLine(t, buf)
- if obj["requestId"] != "xyz" {
- t.Errorf("requestId: got %v", obj["requestId"])
+ if obj["metadata"].(map[string]any)["requestId"] != "xyz" {
+ t.Errorf("metadata.requestId: got %v", obj["metadata"])
}
}
@@ -92,11 +92,12 @@ func TestStructuredWithMetadataStruct(t *testing.T) {
log, buf := newLogger(structured.Config{})
log.WithMetadata(user{ID: 7, Name: "Alice"}).Info("hi")
obj := transporttest.ParseJSONLine(t, buf)
- if obj["id"] != float64(7) {
- t.Errorf("id: got %v", obj["id"])
+ md := obj["metadata"].(map[string]any)
+ if md["id"] != float64(7) {
+ t.Errorf("metadata.id: got %v", md["id"])
}
- if obj["name"] != "Alice" {
- t.Errorf("name: got %v", obj["name"])
+ if md["name"] != "Alice" {
+ t.Errorf("metadata.name: got %v", md["name"])
}
}
@@ -206,6 +207,134 @@ func TestStructuredSourceFieldRendered(t *testing.T) {
}
}
+// emit runs a log call through a fresh logger and returns the raw output line.
+// No access to the Write hook, but the assembly and encoding paths are
+// exercised exactly as in production.
+func emit(cfg structured.Config, fn func(*loglayer.LogLayer)) string {
+ log, buf := newLogger(cfg)
+ fn(log)
+ return strings.TrimSpace(buf.String())
+}
+
+// ANSI escapes, CR/LF, and bidi overrides (Trojan Source, CVE-2021-42574) in
+// the message are stripped so a log line can't smuggle terminal control
+// sequences or hide content. DateFn pins the timestamp so the whole line is
+// compared byte-for-byte.
+func TestStructuredSanitizesInjectionInMessage(t *testing.T) {
+ cfg := structured.Config{DateFn: func() string { return "2026-04-26T12:00:00Z" }}
+ if got := emit(cfg, func(log *loglayer.LogLayer) {
+ log.Info("line1\r\nline2\x1b[31mreduser\u202eevil")
+ }); got != `{"level":"info","time":"2026-04-26T12:00:00Z","msg":"line1line2[31mreduserevil"}` {
+ t.Errorf("got %q", got)
+ }
+}
+
+// Same guard on keys and string values when metadata flattens: each metadata
+// entry becomes a top-level key/value, so both are sanitized. Non-string
+// entries pass through untouched. The output shape is pinned here (metadata at
+// root under the original key, sanitized) so the sanitize-vs-flatten interplay
+// can't regress silently.
+func TestStructuredSanitizesInjectionInFlattenedMetadata(t *testing.T) {
+ buf := &bytes.Buffer{}
+ t1 := structured.New(structured.Config{Writer: buf})
+ log := loglayer.New(loglayer.Config{
+ Transport: t1,
+ FlattenMetadata: true,
+ DisableFatalExit: true,
+ })
+ log.WithMetadata(map[string]any{
+ "note\r\n\x1b[": "line1\r\nline2\x1b[31mred",
+ "count": 7,
+ }).Info("hi")
+ obj := transporttest.ParseJSONLine(t, buf)
+ if obj["note["] != "line1line2[31mred" {
+ t.Errorf("note[: got %v", obj["note["])
+ }
+ if obj["count"] != float64(7) {
+ t.Errorf("count: got %v", obj["count"])
+ }
+
+ // Pin the full flattened line: metadata at root, key and value sanitized.
+ fieldWriter := &bytes.Buffer{}
+ t2 := structured.New(structured.Config{
+ Writer: fieldWriter,
+ DateFn: func() string { return "2026-04-26T12:00:00Z" },
+ })
+ log2 := loglayer.New(loglayer.Config{
+ Transport: t2,
+ FlattenMetadata: true,
+ DisableFatalExit: true,
+ })
+ log2.WithMetadata(map[string]any{
+ "note\u202e": "line1\r\nline2\x1b[31mred",
+ }).Info("hi")
+ if got := strings.TrimSpace(fieldWriter.String()); got != `{"level":"info","time":"2026-04-26T12:00:00Z","msg":"hi","note":"line1line2[31mred"}` {
+ t.Errorf("flatten full line: got %q", got)
+ }
+}
+
+// String-typed top-level values (WithFields entries) sanitize the same way,
+// including the keys; non-string values pass through untouched.
+func TestStructuredSanitizesInjectionInTopLevelStringValues(t *testing.T) {
+ log, buf := newLogger(structured.Config{})
+ log = log.WithFields(loglayer.Fields{
+ "user\r\n": "user\u202eevil",
+ "cleanInt": 42,
+ "cleanBool": true,
+ "cleanFloat": 1.5,
+ })
+ log.Info("ok")
+ obj := transporttest.ParseJSONLine(t, buf)
+ if obj["user"] != "userevil" {
+ t.Errorf("user: got %v", obj["user"])
+ }
+ if obj["cleanInt"] != float64(42) {
+ t.Errorf("cleanInt: got %v", obj["cleanInt"])
+ }
+ if obj["cleanBool"] != true {
+ t.Errorf("cleanBool: got %v", obj["cleanBool"])
+ }
+ if obj["cleanFloat"] != 1.5 {
+ t.Errorf("cleanFloat: got %v", obj["cleanFloat"])
+ }
+}
+
+// An empty joined message omits the msg key entirely, so
+// WithFields(...)+Info("") renders level, time, and the fields only. A
+// message that sanitizes to nothing (e.g. bare escape bytes) is omitted the
+// same way.
+func TestStructuredOmitsMsgWhenEmpty(t *testing.T) {
+ log, buf := newLogger(structured.Config{})
+ log = log.WithFields(loglayer.Fields{"service": "api"})
+ log.Info("")
+ obj := transporttest.ParseJSONLine(t, buf)
+ if _, ok := obj["msg"]; ok {
+ t.Errorf("msg should be omitted for empty message, got: %v", obj)
+ }
+ if obj["service"] != "api" {
+ t.Errorf("service: got %v", obj["service"])
+ }
+
+ buf.Reset()
+ log.Info("\x1b\x1b")
+ obj = transporttest.ParseJSONLine(t, buf)
+ if _, ok := obj["msg"]; ok {
+ t.Errorf("msg should be omitted when sanitized message is empty, got: %v", obj)
+ }
+}
+
+// Authored Multiline messages keep their line boundaries in structured while
+// ANSI/control bytes inside each line are still stripped (the same contract
+// cli/pretty/console honor via transport.AssembleMessage).
+func TestStructuredPreservesMultilineBoundaries(t *testing.T) {
+ log, buf := newLogger(structured.Config{})
+ log.Info(loglayer.Multiline("line1\x1b[31m", "line2"))
+ obj := transporttest.ParseJSONLine(t, buf)
+ if obj["msg"] != "line1[31m\nline2" {
+ t.Errorf("msg: got %q, want authored newline preserved", obj["msg"])
+ }
+}
+
// SourceFieldName overrides the rendered key.
func TestStructuredSourceFieldNameOverride(t *testing.T) {
buf := &bytes.Buffer{}
@@ -229,3 +358,20 @@ func TestStructuredSourceFieldNameOverride(t *testing.T) {
t.Errorf("expected source under 'caller': %v", obj)
}
}
+
+// A Data/field key that collides with the metadata nest key is dropped so
+// the JSON object can't contain duplicate "metadata" keys; the nested
+// metadata value wins (matches transport.MergeIntoMap).
+func TestStructuredMetadataKeyCollisionDropped(t *testing.T) {
+ log, buf := newLogger(structured.Config{})
+ log = log.WithFields(loglayer.Fields{"metadata": "user-value"})
+ log.WithMetadata(loglayer.Metadata{"inner": 1}).Info("hi")
+ line := strings.TrimSpace(buf.String())
+ if strings.Count(line, `"metadata"`) != 1 {
+ t.Errorf("expected exactly one metadata key, got: %q", line)
+ }
+ obj := transporttest.ParseJSONLine(t, buf)
+ if _, ok := obj["metadata"].(map[string]any); !ok {
+ t.Errorf("metadata should be the nested value, got: %v", obj["metadata"])
+ }
+}
diff --git a/transports/testing/bench_test.go b/transports/testing/bench_test.go
index 4647496..072b383 100644
--- a/transports/testing/bench_test.go
+++ b/transports/testing/bench_test.go
@@ -7,10 +7,10 @@ package testing_test
import (
"testing"
- lltest "go.loglayer.dev/transports/testing/v2"
- "go.loglayer.dev/v2"
- "go.loglayer.dev/v2/transport"
- "go.loglayer.dev/v2/transport/benchtest"
+ lltest "go.loglayer.dev/transports/testing/v3"
+ "go.loglayer.dev/v3"
+ "go.loglayer.dev/v3/transport"
+ "go.loglayer.dev/v3/transport/benchtest"
)
func BenchmarkRender_Testing_SimpleMessage(b *testing.B) {
diff --git a/transports/testing/example_test.go b/transports/testing/example_test.go
index 779987e..27a254c 100644
--- a/transports/testing/example_test.go
+++ b/transports/testing/example_test.go
@@ -3,8 +3,8 @@ package testing_test
import (
"fmt"
- lltesting "go.loglayer.dev/transports/testing/v2"
- "go.loglayer.dev/v2"
+ lltesting "go.loglayer.dev/transports/testing/v3"
+ "go.loglayer.dev/v3"
)
// New builds an in-memory capture transport. Tests drive the logger,
diff --git a/transports/testing/go.mod b/transports/testing/go.mod
index 525f2dc..33615e5 100644
--- a/transports/testing/go.mod
+++ b/transports/testing/go.mod
@@ -1,7 +1,7 @@
-module go.loglayer.dev/transports/testing/v2
+module go.loglayer.dev/transports/testing/v3
go 1.25.0
-require go.loglayer.dev/v2 v2.0.1
+require go.loglayer.dev/v3 v3.0.0
require github.com/goccy/go-json v0.10.6 // indirect
diff --git a/transports/testing/go.sum b/transports/testing/go.sum
index c00692c..42ec334 100644
--- a/transports/testing/go.sum
+++ b/transports/testing/go.sum
@@ -1,6 +1,6 @@
github.com/goccy/go-json v0.10.6 h1:p8HrPJzOakx/mn/bQtjgNjdTcN+/S6FcG2CTtQOrHVU=
github.com/goccy/go-json v0.10.6/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
-go.loglayer.dev/v2 v2.0.1 h1:B7oYpkfMky0UG/N8IdKeIiiTi6h7eyj5NvRyEth9DHI=
-go.loglayer.dev/v2 v2.0.1/go.mod h1:+BWhs5AyICvCLBz07qHnCE12W34tArUWfXOba0Ct/QI=
+go.loglayer.dev/v3 v3.0.0 h1:TEkgoAO21SAE0vKzrNa53G3VG88TJCKs/fMo177uY8s=
+go.loglayer.dev/v3 v3.0.0/go.mod h1:ghbgQp7Y/Vo4/jPCLiokMZTy2DrA3X8dfqtgcWhy3pg=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
diff --git a/transports/testing/testing.go b/transports/testing/testing.go
index 769a721..bcfc0e1 100644
--- a/transports/testing/testing.go
+++ b/transports/testing/testing.go
@@ -8,8 +8,8 @@ import (
"context"
"sync"
- "go.loglayer.dev/v2"
- "go.loglayer.dev/v2/transport"
+ "go.loglayer.dev/v3"
+ "go.loglayer.dev/v3/transport"
)
// LogLine is a single captured log entry. Fields are exposed directly so tests
diff --git a/transports/testing/testing_test.go b/transports/testing/testing_test.go
index 0045de8..2c5a34d 100644
--- a/transports/testing/testing_test.go
+++ b/transports/testing/testing_test.go
@@ -3,9 +3,9 @@ package testing_test
import (
"testing"
- lltest "go.loglayer.dev/transports/testing/v2"
- "go.loglayer.dev/v2"
- "go.loglayer.dev/v2/transport"
+ lltest "go.loglayer.dev/transports/testing/v3"
+ "go.loglayer.dev/v3"
+ "go.loglayer.dev/v3/transport"
)
func newLogger() (*loglayer.LogLayer, *lltest.TestLoggingLibrary) {
diff --git a/transports/zap/bench_test.go b/transports/zap/bench_test.go
index 307c43c..9a3edd5 100644
--- a/transports/zap/bench_test.go
+++ b/transports/zap/bench_test.go
@@ -6,10 +6,10 @@ import (
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
- llzap "go.loglayer.dev/transports/zap/v2"
- "go.loglayer.dev/v2"
- "go.loglayer.dev/v2/transport"
- "go.loglayer.dev/v2/transport/benchtest"
+ llzap "go.loglayer.dev/transports/zap/v3"
+ "go.loglayer.dev/v3"
+ "go.loglayer.dev/v3/transport"
+ "go.loglayer.dev/v3/transport/benchtest"
)
func newDirect() *zap.Logger {
diff --git a/transports/zap/example_test.go b/transports/zap/example_test.go
index 2b6648c..3ba3d9e 100644
--- a/transports/zap/example_test.go
+++ b/transports/zap/example_test.go
@@ -3,8 +3,8 @@ package zap_test
import (
"io"
- "go.loglayer.dev/transports/zap/v2"
- "go.loglayer.dev/v2"
+ "go.loglayer.dev/transports/zap/v3"
+ "go.loglayer.dev/v3"
)
// New wraps a *zap.Logger. When Logger is nil a default logger is
diff --git a/transports/zap/go.mod b/transports/zap/go.mod
index e43a331..cdbac3b 100644
--- a/transports/zap/go.mod
+++ b/transports/zap/go.mod
@@ -1,9 +1,9 @@
-module go.loglayer.dev/transports/zap/v2
+module go.loglayer.dev/transports/zap/v3
go 1.25.0
require (
- go.loglayer.dev/v2 v2.0.1
+ go.loglayer.dev/v3 v3.0.0
go.uber.org/zap v1.27.1
)
diff --git a/transports/zap/go.sum b/transports/zap/go.sum
index ce7d58f..14f8c4b 100644
--- a/transports/zap/go.sum
+++ b/transports/zap/go.sum
@@ -6,8 +6,8 @@ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRI
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
-go.loglayer.dev/v2 v2.0.1 h1:B7oYpkfMky0UG/N8IdKeIiiTi6h7eyj5NvRyEth9DHI=
-go.loglayer.dev/v2 v2.0.1/go.mod h1:+BWhs5AyICvCLBz07qHnCE12W34tArUWfXOba0Ct/QI=
+go.loglayer.dev/v3 v3.0.0 h1:TEkgoAO21SAE0vKzrNa53G3VG88TJCKs/fMo177uY8s=
+go.loglayer.dev/v3 v3.0.0/go.mod h1:ghbgQp7Y/Vo4/jPCLiokMZTy2DrA3X8dfqtgcWhy3pg=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
diff --git a/transports/zap/zap.go b/transports/zap/zap.go
index 8c40cb6..c43e4b2 100644
--- a/transports/zap/zap.go
+++ b/transports/zap/zap.go
@@ -9,8 +9,8 @@ import (
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
- "go.loglayer.dev/v2"
- "go.loglayer.dev/v2/transport"
+ "go.loglayer.dev/v3"
+ "go.loglayer.dev/v3/transport"
)
// Config holds configuration options for the zap transport.
diff --git a/transports/zap/zap_test.go b/transports/zap/zap_test.go
index 8bab565..2ce019e 100644
--- a/transports/zap/zap_test.go
+++ b/transports/zap/zap_test.go
@@ -7,10 +7,10 @@ import (
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
- llzap "go.loglayer.dev/transports/zap/v2"
- "go.loglayer.dev/v2"
- "go.loglayer.dev/v2/transport"
- "go.loglayer.dev/v2/transport/transporttest"
+ llzap "go.loglayer.dev/transports/zap/v3"
+ "go.loglayer.dev/v3"
+ "go.loglayer.dev/v3/transport"
+ "go.loglayer.dev/v3/transport/transporttest"
)
func factory(opts transporttest.FactoryOpts) (*loglayer.LogLayer, *bytes.Buffer) {
diff --git a/transports/zerolog/bench_test.go b/transports/zerolog/bench_test.go
index 77aad76..e869100 100644
--- a/transports/zerolog/bench_test.go
+++ b/transports/zerolog/bench_test.go
@@ -5,10 +5,10 @@ import (
zlog "github.com/rs/zerolog"
- llzero "go.loglayer.dev/transports/zerolog/v2"
- "go.loglayer.dev/v2"
- "go.loglayer.dev/v2/transport"
- "go.loglayer.dev/v2/transport/benchtest"
+ llzero "go.loglayer.dev/transports/zerolog/v3"
+ "go.loglayer.dev/v3"
+ "go.loglayer.dev/v3/transport"
+ "go.loglayer.dev/v3/transport/benchtest"
)
func newWrapped() *loglayer.LogLayer {
diff --git a/transports/zerolog/example_test.go b/transports/zerolog/example_test.go
index d6cd794..6b2f7d3 100644
--- a/transports/zerolog/example_test.go
+++ b/transports/zerolog/example_test.go
@@ -3,8 +3,8 @@ package zerolog_test
import (
"io"
- "go.loglayer.dev/transports/zerolog/v2"
- "go.loglayer.dev/v2"
+ "go.loglayer.dev/transports/zerolog/v3"
+ "go.loglayer.dev/v3"
)
// New wraps a *zerolog.Logger. When Logger is nil a default logger is
diff --git a/transports/zerolog/go.mod b/transports/zerolog/go.mod
index a3bd8a6..c170f53 100644
--- a/transports/zerolog/go.mod
+++ b/transports/zerolog/go.mod
@@ -1,15 +1,15 @@
-module go.loglayer.dev/transports/zerolog/v2
+module go.loglayer.dev/transports/zerolog/v3
go 1.25.0
require (
github.com/rs/zerolog v1.35.1
- go.loglayer.dev/v2 v2.0.1
+ go.loglayer.dev/v3 v3.0.0
)
require (
github.com/goccy/go-json v0.10.6 // indirect
github.com/mattn/go-colorable v0.1.14 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
- golang.org/x/sys v0.42.0 // indirect
+ golang.org/x/sys v0.43.0 // indirect
)
diff --git a/transports/zerolog/go.sum b/transports/zerolog/go.sum
index 065ebb2..4e5890f 100644
--- a/transports/zerolog/go.sum
+++ b/transports/zerolog/go.sum
@@ -6,10 +6,10 @@ github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWE
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/rs/zerolog v1.35.1 h1:m7xQeoiLIiV0BCEY4Hs+j2NG4Gp2o2KPKmhnnLiazKI=
github.com/rs/zerolog v1.35.1/go.mod h1:EjML9kdfa/RMA7h/6z6pYmq1ykOuA8/mjWaEvGI+jcw=
-go.loglayer.dev/v2 v2.0.1 h1:B7oYpkfMky0UG/N8IdKeIiiTi6h7eyj5NvRyEth9DHI=
-go.loglayer.dev/v2 v2.0.1/go.mod h1:+BWhs5AyICvCLBz07qHnCE12W34tArUWfXOba0Ct/QI=
+go.loglayer.dev/v3 v3.0.0 h1:TEkgoAO21SAE0vKzrNa53G3VG88TJCKs/fMo177uY8s=
+go.loglayer.dev/v3 v3.0.0/go.mod h1:ghbgQp7Y/Vo4/jPCLiokMZTy2DrA3X8dfqtgcWhy3pg=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo=
-golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
+golang.org/x/sys v0.43.0 h1:Rlag2XtaFTxp19wS8MXlJwTvoh8ArU6ezoyFsMyCTNI=
+golang.org/x/sys v0.43.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
diff --git a/transports/zerolog/zerolog.go b/transports/zerolog/zerolog.go
index b18648b..76a0c80 100644
--- a/transports/zerolog/zerolog.go
+++ b/transports/zerolog/zerolog.go
@@ -14,8 +14,8 @@ import (
zlog "github.com/rs/zerolog"
- "go.loglayer.dev/v2"
- "go.loglayer.dev/v2/transport"
+ "go.loglayer.dev/v3"
+ "go.loglayer.dev/v3/transport"
)
// Config holds configuration options for the zerolog transport.
diff --git a/transports/zerolog/zerolog_test.go b/transports/zerolog/zerolog_test.go
index 1a1d130..f8476ac 100644
--- a/transports/zerolog/zerolog_test.go
+++ b/transports/zerolog/zerolog_test.go
@@ -6,10 +6,10 @@ import (
zlog "github.com/rs/zerolog"
- llzero "go.loglayer.dev/transports/zerolog/v2"
- "go.loglayer.dev/v2"
- "go.loglayer.dev/v2/transport"
- "go.loglayer.dev/v2/transport/transporttest"
+ llzero "go.loglayer.dev/transports/zerolog/v3"
+ "go.loglayer.dev/v3"
+ "go.loglayer.dev/v3/transport"
+ "go.loglayer.dev/v3/transport/transporttest"
)
func factory(opts transporttest.FactoryOpts) (*loglayer.LogLayer, *bytes.Buffer) {