pl

package module
v0.4.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 27, 2026 License: Apache-2.0 Imports: 13 Imported by: 0

README

pl Go Reference codecov experimental

pl tails and pretty-prints JSONL logs produced by zap (the zap.NewProductionConfig JSON encoder, as used by go-faster/sdk).

It also understands OpenTelemetry log records in the logs data model JSON form (the Severity/Body/Attributes/Scope shape emitted by go-faster/sdk's console log exporter). Both formats are detected per line and rendered the same way, so a stream that mixes them — as oteldb emits — reads uniformly:

  • Body becomes the message, the instrumentation Scope name the logger;
  • SeverityText (or the numeric SeverityNumber) becomes the level;
  • Attributes become key=value fields, with the code.* ones folded into the caller and exception.message/exception.stacktrace shown as the error;
  • non-zero TraceID/SpanID are surfaced as trace_id/span_id for correlation, while resource attributes and zero ids are omitted as noise.

When go-faster/sdk's zctx runs in otelzap mode (zctx.WithOpenTelemetryZap), zap lines carry the trace correlation as a reflected ctx object rather than flat fields. pl flattens that object so span_id/trace_id (and any other context-scoped members) read as ordinary fields — identical to zctx's default mode — again dropping all-zero ids.

Install

go install github.com/go-faster/pl/cmd/pl@latest

Usage

Pipe logs in:

my-service 2>&1 | pl

Follow a file (tail -f style):

pl -f service.log

Read a file once and exit:

pl service.log

Output is colorized when stdout is a terminal. Lines in none of the formats below are passed through untouched, so mixed output is safe. Disable colors with --no-color or by setting NO_COLOR.

Plain-text formats

Besides the two JSON formats, pl renders the plain-text logs that surround a Kubernetes deployment, so kubectl logs output reads like the rest:

  • logfmt (logrus, Grafana, Loki, log15, …) — level/lvl/severity, t/ts/time/timestamp, msg/message, logger, caller, err/error and the trace id keys map onto the same rendering as zap's; every other pair becomes a key=value field, with numbers and booleans kept as such. A line is only treated as logfmt when it carries at least two of level, timestamp and message — plain prose is passed through rather than mangled into fields. Text before the first pair — journalctl's Jul 26 12:28:52 host unit[1063]: prefix, say — is kept verbatim ahead of the entry instead of being scanned into fields.
  • klog (kube-apiserver, kubelet, and everything else built on k8s.io/klog) — the header supplies the level, timestamp, thread.id and caller; structured klog's quoted message and trailing key="value" pairs are parsed like logfmt, so err= shows up as the error. The year, which klog omits, is taken from the current date.
$ kubectl logs kubelet | pl
01:22:17.141 E Startup probe already exists for container containerName=cilium-envoy pod=kube-system/cilium-envoy-vxgzg thread.id=1386 (prober_manager.go:197)

JSON lines from log/slog's handler, which names its fields time and msg, are rendered like zap's.

Timestamps in these formats are parsed from RFC3339 or from a numeric epoch, whose unit (seconds, milliseconds, microseconds or nanoseconds) is deduced from its magnitude.

Isolate a single trace with --trace-id; it keeps only lines whose trace_id matches (case-insensitively), whichever format they arrive in — a flat zap trace_id, zctx's reflected ctx object, or an OTEL TraceID:

pl --trace-id a30d8906e0e519424360816608e11188 service.log
Flags
-f, --follow         follow the file, waiting for new lines (like tail -f)
    --no-color       disable ANSI colors
    --no-time        omit timestamps from the output
    --level          minimum level to display (debug|info|warn|error)
    --trace-id       show only lines whose trace_id matches (case-insensitive)
    --timezone       convert timestamps to this timezone (e.g. UTC, Local, America/New_York)
    --otel-resource  include OpenTelemetry resource attributes (OTEL logs only)
    --otel-func      include the function name in the caller (OTEL logs only)

--otel-resource prints the resource attributes on their own indented lines below the entry, in a color distinct from the inline fields, so they stay readable instead of crowding the message:

20:02:07.563 I app ClickHouse disabled (oteldb/app.go:114)
	host.name=198f0b5f008f
	service.name=oteldb
	telemetry.sdk.version=1.44.0
Level styles

Levels render as a single colored character by default — D, I, W, E, and C for dpanic/panic/fatal:

03:00:00.099 D verbose detail
03:00:00.200 I metrics Starting attempt=3
03:00:00.299 W disk low note="needs attention"
03:00:00.400 E boom err=x
03:00:00.500 C giving up

When used as a library, override per-level label and color via Formatter.LevelStyles (levels absent from the map keep their defaults):

f := &pl.Formatter{
    Color: true,
    LevelStyles: map[zapcore.Level]pl.LevelStyle{
        zapcore.WarnLevel: {Label: "WARN", Color: "\033[33m"},
    },
}

Documentation

Overview

Package pl tails and pretty-prints JSONL logs produced by zap (the zap.NewProductionConfig JSON encoder used by go-faster/sdk) as well as OpenTelemetry log records in the logs data model JSON form, plus the plain-text logfmt and klog formats. Each line is detected and normalized onto a shared rendering path, so a stream mixing the formats reads uniformly.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func DefaultLevelStyles

func DefaultLevelStyles() map[zapcore.Level]LevelStyle

DefaultLevelStyles returns the built-in level styles: a single-character label and a color per level (D/I/W/E, and C for dpanic/panic/fatal).

Types

type Formatter

type Formatter struct {
	// Color enables ANSI colors in the output.
	Color bool
	// MinLevel, when set, drops lines below this level.
	MinLevel zapcore.Level
	// TraceID, when set, drops every line whose trace_id does not match,
	// isolating a single trace across a stream. The comparison is
	// case-insensitive and matches whether the id came from a zap trace_id
	// field, zctx's reflected ctx object, or an OTEL TraceID. Lines that carry
	// no trace_id — including non-JSON passthrough lines — are dropped.
	TraceID string
	// TimeFormat is the layout for the timestamp. Defaults to "15:04:05.000".
	TimeFormat string
	// NoTime omits the timestamp from the output entirely.
	NoTime bool
	// Location, when set, converts timestamps to this timezone before
	// formatting. A nil Location uses the timestamp's own location.
	Location *time.Location
	// LevelStyles overrides how levels are rendered. Levels absent from the map
	// fall back to DefaultLevelStyles; a nil map uses the defaults entirely.
	LevelStyles map[zapcore.Level]LevelStyle
	// OTELResource includes OpenTelemetry resource attributes (service.name,
	// host.name, ...) as fields. They are dropped by default because they repeat
	// identically on every line. Only affects OTEL log records.
	OTELResource bool
	// OTELFunc includes the function name (the code.function.name attribute) in
	// the caller of OpenTelemetry log records. Dropped by default as it
	// duplicates the source location.
	OTELFunc bool
	// contains filtered or unexported fields
}

Formatter renders a single zap JSON log line into a human-readable form.

func (*Formatter) Follow

func (f *Formatter) Follow(ctx context.Context, path string, w io.Writer) error

Follow tails the file at path (like tail -f), formatting new lines as they are appended. It handles truncation/rotation by reopening when the file shrinks. It blocks until ctx is canceled.

func (*Formatter) Format

func (f *Formatter) Format(line []byte) (out string, ok bool)

Format parses a single log line and returns its pretty representation.

The returned bool reports whether the line should be printed. Lines in none of the supported formats — zap and OTEL JSON, logfmt, klog — are returned unchanged (and ok is true) so that mixed output is preserved. Lines below MinLevel are dropped (ok is false).

func (*Formatter) Process

func (f *Formatter) Process(ctx context.Context, r io.Reader, w io.Writer) error

Process reads newline-delimited logs from r, formats each line with f and writes the result to w until r is exhausted or ctx is canceled.

func (*Formatter) SetMinLevel

func (f *Formatter) SetMinLevel(l zapcore.Level)

SetMinLevel sets the minimum level to display.

type LevelStyle

type LevelStyle struct {
	// Label is the text shown for the level (e.g. "I" for info).
	Label string
	// Color is the ANSI escape sequence applied to the label. Empty means no
	// color. Ignored when the Formatter has Color disabled.
	Color string
}

LevelStyle controls how a single log level is rendered.

Directories

Path Synopsis
cmd
pl command
Command pl tails and pretty-prints JSONL logs produced by zap (the zap.NewProductionConfig JSON encoder used by go-faster/sdk).
Command pl tails and pretty-prints JSONL logs produced by zap (the zap.NewProductionConfig JSON encoder used by go-faster/sdk).

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL