logcore

package
v0.4.0 Latest Latest
Warning

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

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

Documentation

Overview

Package logcore is the zerolog-backed logger implementation shared by the two public logging surfaces: the root package ax and the import-isolated package logging. Both alias the types declared here, so a logger obtained from either surface is the same type backed by the same construction path, the same backend, and the same trace-correlation hook.

logcore is NOT a public surface. The Go toolchain forbids any module outside github.com/rshade/ax-go from importing it, and that path restriction is what carries Constitution Principle VI's no-pluggable-backend guardrail: Sink and LabelSanctioner must be exported so lokiWriter (which stays in package ax) can satisfy them across the package boundary, and an exported interface would otherwise invite an external backend registration. No external consumer can reach these names, so no external backend can be registered.

The guardrail is therefore path-enforced rather than type-enforced. That is a deliberate, recorded narrowing: an ax-go maintainer could add a second backend without a compiler complaint, where an unexported interface would have objected. Review holds that line. Adding a second logger implementation or a second backend remains forbidden by Principle VI.

logcore must never import root ax, and its dependency set is closed: stdlib, github.com/rs/zerolog, the OpenTelemetry trace API (never the SDK), and github.com/rshade/ax-go/contract for the zero-value ID constants. It contains no Loki-specific identifier; the direct-push addon reaches it only through the generic Sink and LabelSanctioner seams.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Flush

func Flush(ctx context.Context, l Logger) error

Flush performs a best-effort, non-destructive drain of any buffered entries held by l's sinks. It returns nil — performing no work — when l is nil or when l holds no drainable sinks, so a caller never has to test before calling.

Errors from individual sinks are joined; errors.Is against a specific sink's error works on the result. A drain failure is reported to the caller but must never change the process exit code.

Types

type Config

type Config struct {
	Ctx             context.Context //nolint:containedctx // carried to bound sink goroutines to the logger lifetime
	Writer          io.Writer
	Level           zerolog.Level
	Labels          Labels
	AdditionalSinks []Sink
}

Config is the accumulated construction state an Option mutates.

Its fields are exported rather than hidden behind accessors because the Loki direct-push addon lives in package ax and must register its sink across the package boundary. In particular the addon takes the ADDRESS of Writer, so a WithWriter applied after the sink is registered is still observed by the sink's diagnostic path — that aliasing is what makes option order irrelevant.

Config is named in Option's signature (so godoc for logging.LoggerOption mentions this internal type), but surfacecheck inventories aliases by target name rather than expanded signature and therefore does not record Config or its fields. Field-set discipline is review and convention, not baseline drift; adding a field here does not require a surface-check regeneration.

type LabelSanctioner

type LabelSanctioner interface {
	SanctionLabels(labels Labels)
}

LabelSanctioner is the optional capability by which a sink is told which label pairs may be promoted from a log line into stream labels.

It is deliberately separate from Sink rather than folded into it. The sink seam must stay fully generic: a file rotator or ring-buffer sink has no label concept and must not be forced to implement one. New asserts the capability and leaves sinks without it alone, which is what keeps the core logger free of any knowledge of who implements it.

It is exported for the same cross-package satisfaction reason as Sink.Drain.

type Labels

type Labels struct {
	Environment string
	Application string
	Host        string
	Version     string
}

Labels are low-cardinality descriptors attached to every log line and eligible for promotion to log-aggregation stream labels by a sink that implements LabelSanctioner.

The set is closed by design (Constitution Principle VIII's cardinality split): environment, application, host, and version are indexed labels; trace_id, span_id, user_id, durations, and resource IDs are payload and must never be promoted. Adding a field here is a public-surface change, because both public logging surfaces alias this type.

type Logger

type Logger interface {
	Debug(ctx context.Context) *zerolog.Event
	Info(ctx context.Context) *zerolog.Event
	Warn(ctx context.Context) *zerolog.Event
	Error(ctx context.Context) *zerolog.Event
	WithLabels(labels Labels) Logger
	Zerolog() *zerolog.Logger
}

Logger is the canonical structured-logging surface, backed by zerolog. The single-backend guardrail (this interface is a migration seam, not a pluggable-backend selector) and the trace-correlation contract are governed by Constitution Principles VI and VIII.

Every emitted line carries trace_id and span_id: the active span's hex values when one is present, and the zero-value valid hex constants when none is, so a consumer parser never has to branch on absence.

func New

func New(ctx context.Context, opts ...Option) Logger

New returns a Logger backed by zerolog and wired for trace correlation. It never returns nil.

The construction order is contractual. Every Option is applied before any sink is sanctioned, so stream-label promotion follows the FINAL label set regardless of the order the caller passed its options in. When additional sinks are registered, each log line is fanned out to all of them alongside the primary writer via io.MultiWriter.

A sink that implements LabelSanctioner is told the label set; one that does not is left alone and never rejected, keeping the sink seam generic for destinations with no label concept.

type Option

type Option func(*Config)

Option configures New. Both public surfaces alias this type, so an option manufactured by one surface is accepted by the other's constructor.

func WithLabels

func WithLabels(labels Labels) Option

WithLabels attaches low-cardinality labels to every log line. Empty fields are omitted entirely rather than emitted as empty strings.

func WithLevel

func WithLevel(level zerolog.Level) Option

WithLevel sets the minimum zerolog level. Events below it never construct, so a filtered call costs nothing beyond the level comparison.

func WithWriter

func WithWriter(w io.Writer) Option

WithWriter sets the primary logger output writer. Defaults to stderr, which keeps log output on the diagnostic stream (Constitution Principle I).

type Sink

type Sink interface {
	Write(p []byte) (int, error)
	Drain(ctx context.Context) error
}

Sink is a write-through log destination that can drain buffered entries at shutdown. Every sink is an io.Writer — New fans output out to all of them via io.MultiWriter alongside the primary writer — and exposes a context-aware Drain so shutdown cannot hang.

Drain is exported, and the interface with it, for a language reason rather than a design preference: Go qualifies an unexported method name by its defining package, so an interface requiring a lowercase method can only ever be satisfied from inside logcore. The Loki direct-push writer stays in package ax (Constitution Principle VIII forbids coupling log shipping into the core logger), so it must satisfy this contract across a package boundary, and an unexported method makes that uncompilable.

Exporting the name does not open the seam to external registration: logcore lives under internal/, which the toolchain forbids any other module from importing, and neither public logging surface re-exports Sink.

Jump to

Keyboard shortcuts

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