Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions .changeset/transport-sweep-v3-paths.md
Original file line number Diff line number Diff line change
@@ -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.
25 changes: 1 addition & 24 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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 ./...

Expand All @@ -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
Expand Down Expand Up @@ -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
Expand Down
17 changes: 8 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

<p align="center">
<a href="https://github.com/loglayer/loglayer-go/releases"><img src="https://img.shields.io/github/v/tag/loglayer/loglayer-go?filter=v*&sort=date&label=version&style=flat-square&color=blue" alt="Latest version"></a>
<a href="https://pkg.go.dev/go.loglayer.dev/v2"><img src="https://pkg.go.dev/badge/go.loglayer.dev/v2.svg" alt="Go Reference"></a>
<a href="https://pkg.go.dev/go.loglayer.dev/v3"><img src="https://pkg.go.dev/badge/go.loglayer.dev/v3.svg" alt="Go Reference"></a>
<a href="https://opensource.org/licenses/MIT"><img src="https://img.shields.io/badge/License-MIT-blue.svg" alt="License: MIT"></a>
</p>

Expand All @@ -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
Expand Down Expand Up @@ -84,7 +83,7 @@ log.WithPrefix("[my-app]").
## Install

```sh
go get go.loglayer.dev/v2
go get go.loglayer.dev/v3
```

## Documentation
Expand All @@ -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).
Expand Down
8 changes: 2 additions & 6 deletions doc.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@
// 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/<name>, 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
//
// # Quickstart
//
// import (
// "go.loglayer.dev/v3"
// "go.loglayer.dev/transports/structured/v2"
// "go.loglayer.dev/transports/structured/v3"
// )
//
// log := loglayer.New(loglayer.Config{
Expand All @@ -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
Expand Down
8 changes: 4 additions & 4 deletions docs/src/benchmarks.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
14 changes: 5 additions & 9 deletions docs/src/cheatsheet.md
Original file line number Diff line number Diff line change
Expand Up @@ -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{})})
Expand Down Expand Up @@ -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())

Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion docs/src/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -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{}),
Expand Down
12 changes: 6 additions & 6 deletions docs/src/for-typescript-developers.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down Expand Up @@ -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"},
Expand Down Expand Up @@ -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{
Expand Down
16 changes: 5 additions & 11 deletions docs/src/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,17 @@ 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

LogLayer ships as a multi-module repo: the core lives at `go.loglayer.dev/v3`, and every transport and plugin is its own independently-versioned sub-module. You install the core plus only the transports you actually use.

```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`:
Expand All @@ -34,7 +28,7 @@ import (
"fmt"

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

func main() {
Expand Down Expand Up @@ -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
Expand All @@ -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()
Expand Down
6 changes: 1 addition & 5 deletions docs/src/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,18 +39,14 @@ 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

import (
"errors"

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

func main() {
Expand Down
Loading
Loading