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]>
87 lines
1.9 KiB
Go
87 lines
1.9 KiB
Go
package logging
|
|
|
|
import (
|
|
"testing"
|
|
)
|
|
|
|
func TestParseLevel(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
cases := []struct {
|
|
in string
|
|
want Level
|
|
wantErr bool
|
|
}{
|
|
{"trace", TraceLevel, false},
|
|
{"TRACE", TraceLevel, false},
|
|
{" Trace ", TraceLevel, false},
|
|
{"debug", DebugLevel, false},
|
|
{"Debug", DebugLevel, false},
|
|
{"info", InfoLevel, false},
|
|
{"INFO", InfoLevel, false},
|
|
{"error", ErrorLevel, false},
|
|
{"Error", ErrorLevel, false},
|
|
{"warn", 0, true},
|
|
{"", 0, true},
|
|
{"verbose", 0, true},
|
|
}
|
|
|
|
for _, tc := range cases {
|
|
t.Run(tc.in, func(t *testing.T) {
|
|
t.Parallel()
|
|
got, err := ParseLevel(tc.in)
|
|
if tc.wantErr {
|
|
if err == nil {
|
|
t.Fatalf("ParseLevel(%q) = %v, want error", tc.in, got)
|
|
}
|
|
return
|
|
}
|
|
if err != nil {
|
|
t.Fatalf("ParseLevel(%q) returned error: %v", tc.in, err)
|
|
}
|
|
if got != tc.want {
|
|
t.Fatalf("ParseLevel(%q) = %v, want %v", tc.in, got, tc.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestLevelString(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
cases := []struct {
|
|
level Level
|
|
want string
|
|
}{
|
|
{TraceLevel, "trace"},
|
|
{DebugLevel, "debug"},
|
|
{InfoLevel, "info"},
|
|
{ErrorLevel, "error"},
|
|
{Level(42), "level(42)"},
|
|
}
|
|
|
|
for _, tc := range cases {
|
|
got := tc.level.String()
|
|
if got != tc.want {
|
|
t.Errorf("Level(%d).String() = %q, want %q", int(tc.level), got, tc.want)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestLevelOrdering(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
// Trace is more verbose than Debug, which is more verbose than Info, etc.
|
|
// The numeric ordering matters because callers compare levels directly
|
|
// and adapters map them to backend levels.
|
|
if !(TraceLevel < DebugLevel) {
|
|
t.Errorf("TraceLevel (%d) should be < DebugLevel (%d)", TraceLevel, DebugLevel)
|
|
}
|
|
if !(DebugLevel < InfoLevel) {
|
|
t.Errorf("DebugLevel (%d) should be < InfoLevel (%d)", DebugLevel, InfoLevel)
|
|
}
|
|
if !(InfoLevel < ErrorLevel) {
|
|
t.Errorf("InfoLevel (%d) should be < ErrorLevel (%d)", InfoLevel, ErrorLevel)
|
|
}
|
|
}
|