mirror of
https://github.com/hanzo-fi/go-libs.git
synced 2026-10-30 20:53:44 +00:00
* feat(logging): add Trace level to Logger Add a TraceLevel constant (value -1, more verbose than Debug) and Trace/Tracef methods to the Logger interface. Trace is intended for per-event/per-request logging that is too noisy for Debug. - TraceLevel is exposed alongside Debug/Info/Error in the Level enum. - LogrusLogger forwards Trace/Tracef to logrus's native Trace level. - NewDefaultLoggerWithLevel lets callers pick a precise level (the existing NewDefaultLogger(w, debug, ...) wrapper is preserved for backward compatibility). - ParseLevel(string) and Level.String() are added for CLI parsing and structured logging. - Package-level Trace/Tracef helpers mirror the existing Debug/Info helpers and delegate to FromContext. - The generated mock is updated; the local mockLogger in pkg/authn/licence/licence_test.go now implements Trace/Tracef. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]> * feat(logging): add ZapLogger adapter with Trace support Add a zap-backed implementation of the Logger interface, mirroring the existing LogrusLogger but built on go.uber.org/zap. Trace records are emitted at a custom zapcore.Level (-2, below DebugLevel) so cores that opt out of Trace — typically OTel exporter bridges wrapped with MinLevelCore — silently drop them. Public surface: - ZapLogger struct, NewZap(sugar), NopZap() — drop-in Logger backed by *zap.SugaredLogger. - (*ZapLogger).Zap() exposes the underlying *zap.Logger for libraries that require it directly (etcd WAL, gRPC instrumentation, ...). - ToZapLevel(Level) and EncodeLevelWithTrace(base) help build pipelines that handle the custom Trace level correctly. - MinLevelCore wraps a zapcore.Core to enforce a minimum level — meant to keep Trace records off the OTel bridge while letting them flow to the console core. This backport unblocks services (ledger-v3) that already maintain a local copy of this adapter; centralising it here avoids divergence and keeps the Trace semantics consistent across the platform. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]> * chore(logging): regenerate mock to match mockgen output The hand-edited mock placed Trace/Tracef before Writer instead of in the order mockgen produces. Running `go generate ./...` in CI flagged the diff via the "Dirty" check. Regenerated via mockgen to match. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]> * test(logging): cover Trace level, ParseLevel, and MinLevelCore Add unit tests for the new public surface so codecov picks up the patch coverage: - ParseLevel: accepts trace|debug|info|error case-insensitively, rejects unknown strings. - Level.String: round-trips the supported levels and falls back to a readable form for unknown values. - ZapLogger: Trace records are emitted when the configured level is TraceLevel and silently dropped at DebugLevel; Enabled returns the expected truth table across all four levels; WithFields propagates. - NopZap: never emits and reports Enabled=false. - MinLevelCore: the canonical pipeline (console accepts Trace, OTel core wrapped with MinLevelCore at DebugLevel) routes Trace to the console only and lets Debug through both cores. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]> --------- Co-authored-by: Claude Opus 4.7 (1M context) <[email protected]>
72 lines
2.1 KiB
Go
72 lines
2.1 KiB
Go
package logging
|
|
|
|
import (
|
|
"go.uber.org/zap/zapcore"
|
|
)
|
|
|
|
// zapTraceLevel is the custom zapcore.Level used to emit TraceLevel records.
|
|
// It sits at -2, one notch below zapcore.DebugLevel (-1), so any zapcore.Core
|
|
// configured to accept Trace will also accept Debug and above.
|
|
const zapTraceLevel zapcore.Level = -2
|
|
|
|
// ToZapLevel maps a logging.Level to its zapcore counterpart. TraceLevel
|
|
// maps to the custom zapTraceLevel; unknown levels fall back to InfoLevel.
|
|
func ToZapLevel(level Level) zapcore.Level {
|
|
switch level {
|
|
case TraceLevel:
|
|
return zapTraceLevel
|
|
case DebugLevel:
|
|
return zapcore.DebugLevel
|
|
case InfoLevel:
|
|
return zapcore.InfoLevel
|
|
case ErrorLevel:
|
|
return zapcore.ErrorLevel
|
|
default:
|
|
return zapcore.InfoLevel
|
|
}
|
|
}
|
|
|
|
// EncodeLevelWithTrace wraps a zapcore.LevelEncoder so the custom Trace level
|
|
// is rendered as "trace" instead of falling back to zap's default formatting
|
|
// (which would print something like "Level(-2)").
|
|
func EncodeLevelWithTrace(base zapcore.LevelEncoder) zapcore.LevelEncoder {
|
|
return func(l zapcore.Level, enc zapcore.PrimitiveArrayEncoder) {
|
|
if l == zapTraceLevel {
|
|
enc.AppendString("trace")
|
|
return
|
|
}
|
|
base(l, enc)
|
|
}
|
|
}
|
|
|
|
// MinLevelCore wraps a zapcore.Core and rejects log entries below a minimum
|
|
// level. Typical use: prevent Trace records from reaching an OTel exporter
|
|
// core while still allowing them on the console core. Compose with
|
|
// zapcore.NewTee to build multi-destination logging pipelines.
|
|
//
|
|
// consoleCore := zapcore.NewCore(enc, console, logging.ToZapLevel(level))
|
|
// otelCore := &logging.MinLevelCore{Core: otelBridge, Min: zapcore.DebugLevel}
|
|
// root := zap.New(zapcore.NewTee(consoleCore, otelCore))
|
|
type MinLevelCore struct {
|
|
zapcore.Core
|
|
Min zapcore.Level
|
|
}
|
|
|
|
func (c *MinLevelCore) Enabled(lvl zapcore.Level) bool {
|
|
if lvl < c.Min {
|
|
return false
|
|
}
|
|
return c.Core.Enabled(lvl)
|
|
}
|
|
|
|
func (c *MinLevelCore) Check(ent zapcore.Entry, ce *zapcore.CheckedEntry) *zapcore.CheckedEntry {
|
|
if ent.Level < c.Min {
|
|
return ce
|
|
}
|
|
return c.Core.Check(ent, ce)
|
|
}
|
|
|
|
func (c *MinLevelCore) With(fields []zapcore.Field) zapcore.Core {
|
|
return &MinLevelCore{Core: c.Core.With(fields), Min: c.Min}
|
|
}
|