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
23 changes: 21 additions & 2 deletions docs/src/public/llms-full.txt
Original file line number Diff line number Diff line change
Expand Up @@ -194,10 +194,28 @@ Callbacks may be invoked concurrently across goroutines if the same `*LogLayer`
terminal-renderer transports (cli, pretty, console) interpret as
authored "\n" boundaries. JSON sinks and wrapper transports flatten
via Stringer/MarshalJSON. Each authored line is still sanitized
individually. v1 is messages-only; metadata/fields values still
collapse to one line in terminal renderers.
individually. The wrapper is honored only as a positional message
argument; values placed inside Fields or Metadata still collapse
to one line in terminal renderers.
https://go.loglayer.dev/logging-api/multiline

## Log Sanitization

- The cli, pretty, and console transports strip control bytes
(CR / LF / ANSI ESC / bidi controls / zero-width chars) from
user-controlled message strings before writing, to defeat log
forging, terminal-escape smuggling, and Trojan Source attacks.
Untrusted input cannot become multi-line by accident; the
loglayer.Multiline wrapper is the developer's explicit opt-in.
- structured and every wrapper transport (zerolog, zap, slog,
logrus, charmlog, phuslu, sentry, otellog, gcplogging, http,
datadog, testing) do NOT call sanitize.Message; their JSON
encoders escape control bytes automatically.
- Helpers: utils/sanitize.Message(string) string strips per-rune;
transport.AssembleMessage(messages, sanitize) handles per-line
sanitize with Multiline-aware joining for terminal transports.
https://go.loglayer.dev/log-sanitization

## Fields (persistent data across all log entries)

Fields ride on every emission from the logger they're set on. `WithFields` returns a new `*LogLayer`; always assign the result.
Expand Down Expand Up @@ -1157,6 +1175,7 @@ Full module list: [`monorel.toml`](https://github.com/loglayer/loglayer-go/blob/
- [Thread Safety](https://go.loglayer.dev/logging-api/thread-safety)
- [Raw Logging](https://go.loglayer.dev/logging-api/raw)
- [Multi-line messages](https://go.loglayer.dev/logging-api/multiline)
- [Log Sanitization](https://go.loglayer.dev/log-sanitization)
- [Mocking](https://go.loglayer.dev/logging-api/mocking)
- [For TypeScript Developers](https://go.loglayer.dev/for-typescript-developers)
- [Transport Overview](https://go.loglayer.dev/transports/)
Expand Down
1 change: 1 addition & 0 deletions docs/src/public/llms.txt
Original file line number Diff line number Diff line change
Expand Up @@ -404,6 +404,7 @@ lines := lib.Lines() // []lltest.LogLine; assert on Level, Messages, Data, Meta
- [Adjusting Log Levels](https://go.loglayer.dev/logging-api/adjusting-log-levels): Three-tier level control
- [Raw Logging](https://go.loglayer.dev/logging-api/raw): `Raw(RawLogEntry)` bypasses the builder
- [Multi-line messages](https://go.loglayer.dev/logging-api/multiline): `loglayer.Multiline` for authored multi-line output
- [Log Sanitization](https://go.loglayer.dev/log-sanitization): What gets sanitized, where, and the transport-author decision tree
- [Mocking](https://go.loglayer.dev/logging-api/mocking): `loglayer.NewMock()` and `transports/testing`
- [Transport Overview](https://go.loglayer.dev/transports/): All transports
- [Plugins Overview](https://go.loglayer.dev/plugins/): Plugin system and hooks
Expand Down
7 changes: 6 additions & 1 deletion docs/src/transports/creating-transports.md
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,12 @@ func (t *Transport) SendToLogger(p loglayer.TransportParams) {

The core does NOT prepend `params.Prefix` into `Messages[0]`. Each transport decides how to render the prefix:

- **Fold the prefix into the message** (simplest): call `transport.JoinPrefixAndMessages(params.Prefix, params.Messages)` at the top of your `SendToLogger`. The helper returns `Messages` unchanged when `Prefix` is empty (fast path) or when `Messages[0]` isn't a string; otherwise it returns a fresh slice with `prefix + " "` prepended to `Messages[0]`. The output reads as one blob (`"[prefix] message body"`) which is what most renderer / wrapper transports want.
- **Fold the prefix into the message** (simplest): call `transport.JoinPrefixAndMessages(params.Prefix, params.Messages)` at the top of your `SendToLogger`. The helper returns `Messages` unchanged only when `Prefix` is empty (fast path); otherwise it folds the prefix in front of `Messages[0]` based on the element's type:
- `string`: prepend `prefix + " "` directly.
- `*loglayer.MultilineMessage`: prepend `prefix + " "` to the first authored line and rebuild the wrapper, so multi-line content renders with the prefix on line 1 only.
- any other value: format with `fmt.Sprintf("%v", v)` and prepend the prefix in front, so types implementing `Stringer` flow correctly.

The output reads as one blob (`"[prefix] message body"`) which is what most renderer / wrapper transports want.
- **Render the prefix separately**: read `params.Prefix` directly and render it however suits your transport. A renderer can color the prefix differently from the message body; a structured transport can emit it as its own top-level field; a wrapper transport can forward it to the underlying logger's structured-field API (`zerolog.Event.Str("prefix", p.Prefix)`, etc.). Don't call `JoinPrefixAndMessages` in this path.

```go
Expand Down
42 changes: 31 additions & 11 deletions docs/src/whats-new.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,34 +7,54 @@ description: Latest features and improvements in LogLayer for Go.

- See the [main `CHANGELOG.md`](https://github.com/loglayer/loglayer-go/blob/main/CHANGELOG.md) for the auto-generated per-release log.

## May 02, 2026

`v2.0.0`:

**Breaking: import paths bump to `/v2`.** The loglayer core no longer mutates `Messages[0]` to fold the `WithPrefix` value into the message text. The prefix flows through `TransportParams.Prefix` and each transport decides how to render it. Built-in transports preserve v1 user-visible output via the new `transport.JoinPrefixAndMessages` helper; the cli transport opts into smart rendering (dim-grey user prefix separate from level color). See [Migrating to v2](/migrating-to-v2) for the upgrade checklist.

`loglayer`:
## May 03, 2026

`Prefix` is now exposed as a separate field on `TransportParams` and on every dispatch-time plugin hook param struct (`BeforeDataOutParams`, `BeforeMessageOutParams`, `TransformLogLevelParams`, `ShouldSendParams`). Transports and plugins can render or react to the prefix independently from the message string. The legacy "prepend prefix into `Messages[0]`" auto-mutation in v1.7.x stays in place for backwards compatibility within the v1 line; v2.0.0 removes it.
`v2.1.0`:

**`loglayer.Multiline(lines ...any)`** is a new value-wrapper that lets terminal transports preserve authored "\n" boundaries. The cli, pretty, and console transports collapse bare-string newlines to one line for security (log-forging, terminal-escape smuggling); the wrapper is a per-call developer-issued opt-in to that defense. Each authored line is still individually sanitized; only the boundaries between them are honored. JSON sinks and wrapper transports flatten via `Stringer` / `MarshalJSON` with no code change. See [Multi-line messages](/logging-api/multiline).

The change also fixes a pre-existing bug in `transport.JoinPrefixAndMessages` where `WithPrefix` was silently dropped when `Messages[0]` was not a string. The prefix now folds in front of the `%v`-formatted first message.

`transports/cli`:
A new [Log Sanitization](/log-sanitization) reference page covers what gets sanitized where, the threat model (log forging, terminal escape smuggling, Trojan Source), and the decision tree for transport authors.

Initial release. New [CLI transport](/transports/cli) tuned for command-line app output: short level prefixes, stdout / stderr routing, TTY-detected ANSI color, no timestamps. Includes table rendering for slice-of-map metadata so the same call site emits a CLI table and a JSON array depending on the transport.
`transports/cli` `v2.2.0`:

The cli transport now honors `loglayer.Multiline` values: authored multi-line content renders across rows on stdout / stderr while bare-string newlines are still stripped. Per-line sanitization for ANSI / CR / bidi / ZWSP is preserved within each authored line.

`transports/cli` `v2.1.0`:

New `Config.TableColumnOrder []string` knob pins the leading column order for slice-of-map metadata table rendering. Keys named here render in the listed order; the rest sort lexicographically and follow. Empty / nil keeps the previous fully-lexicographic behavior. See [Pinning column order](/transports/cli#pinning-column-order).

`transports/http`:
`transports/pretty` `v2.1.0` and `transports/console` `v2.1.0`:

Same Multiline support as cli: authored multi-line content renders across rows; bare-string newlines still strip; per-line sanitization preserved.

`transports/http` `v2.1.0`:

New `Config.String()` redacts `Headers` values so an accidental `log.Info(cfg)` or `fmt.Sprintf("%v", cfg)` can't leak credentials passed via `Authorization` / `X-API-Key` / similar headers. Header keys stay visible for debuggability. Mirrors the redaction shape already used by `transports/datadog`.

`defaultCheckRedirect` now compares hosts case-insensitively, so legitimate same-host redirects with mixed-case URLs aren't refused. Cross-host refusal still applies; ports are still compared exactly.

New `Config.ShutdownTimeout` (default 5s) bounds how long `Close` waits for in-flight requests to finish during shutdown. When the timeout elapses, the worker's outbound HTTP requests are cancelled via context so `Close` can return even if the endpoint is wedged; previously a stuck endpoint could pin `Close` for up to the per-request `Client.Timeout` (30s default), and the parent `flushTransports`'s 5s timeout would leak the close goroutine.

`v2.0.1`:

Republished every module with a clean `go.mod`. The v2.0.0 cascade shipped sub-module `go.mod` files containing dev-only `replace` directives and placeholder pseudo-version requires; downstream consumers saw `go mod tidy` 404 on the placeholders. No API changes; re-`go get` to pick up the cleaned modules.

## May 02, 2026

`v2.0.0`:

**Breaking: import paths bump to `/v2`.** The loglayer core no longer mutates `Messages[0]` to fold the `WithPrefix` value into the message text. The prefix flows through `TransportParams.Prefix` and each transport decides how to render it. Built-in transports preserve v1 user-visible output via the new `transport.JoinPrefixAndMessages` helper; the cli transport opts into smart rendering (dim-grey user prefix separate from level color). See [Migrating to v2](/migrating-to-v2) for the upgrade checklist.

`loglayer`:

`Prefix` is now exposed as a separate field on `TransportParams` and on every dispatch-time plugin hook param struct (`BeforeDataOutParams`, `BeforeMessageOutParams`, `TransformLogLevelParams`, `ShouldSendParams`). Transports and plugins can render or react to the prefix independently from the message string. The legacy "prepend prefix into `Messages[0]`" auto-mutation in v1.7.x stays in place for backwards compatibility within the v1 line; v2.0.0 removes it.

`transports/cli`:

Initial release. New [CLI transport](/transports/cli) tuned for command-line app output: short level prefixes, stdout / stderr routing, TTY-detected ANSI color, no timestamps. Includes table rendering for slice-of-map metadata so the same call site emits a CLI table and a JSON array depending on the transport.

## Apr 30, 2026

`transports/gcplogging`:
Expand Down
Loading