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 ¶
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 ¶
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 ¶
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 ¶
WithLabels attaches low-cardinality labels to every log line. Empty fields are omitted entirely rather than emitted as empty strings.
func WithLevel ¶
WithLevel sets the minimum zerolog level. Events below it never construct, so a filtered call costs nothing beyond the level comparison.
func WithWriter ¶
WithWriter sets the primary logger output writer. Defaults to stderr, which keeps log output on the diagnostic stream (Constitution Principle I).
type Sink ¶
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.