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]>
102 lines
2.6 KiB
Go
102 lines
2.6 KiB
Go
package logging
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"io"
|
|
"strings"
|
|
)
|
|
|
|
// Level represents a logging severity level. Lower values are more verbose.
|
|
type Level int
|
|
|
|
const (
|
|
TraceLevel Level = iota - 1 // -1, more verbose than Debug; reserved for per-event/per-request logs.
|
|
DebugLevel // 0
|
|
InfoLevel // 1
|
|
ErrorLevel // 2
|
|
)
|
|
|
|
// ParseLevel parses a textual level (case-insensitive) into a Level.
|
|
func ParseLevel(s string) (Level, error) {
|
|
switch strings.ToLower(strings.TrimSpace(s)) {
|
|
case "trace":
|
|
return TraceLevel, nil
|
|
case "debug":
|
|
return DebugLevel, nil
|
|
case "info":
|
|
return InfoLevel, nil
|
|
case "error":
|
|
return ErrorLevel, nil
|
|
default:
|
|
return 0, fmt.Errorf("unknown log level: %q (expected trace|debug|info|error)", s)
|
|
}
|
|
}
|
|
|
|
// String returns the textual representation of a Level.
|
|
func (l Level) String() string {
|
|
switch l {
|
|
case TraceLevel:
|
|
return "trace"
|
|
case DebugLevel:
|
|
return "debug"
|
|
case InfoLevel:
|
|
return "info"
|
|
case ErrorLevel:
|
|
return "error"
|
|
default:
|
|
return fmt.Sprintf("level(%d)", int(l))
|
|
}
|
|
}
|
|
|
|
//go:generate mockgen -source logging.go -destination logging_generated.go -package logging . Logger
|
|
type Logger interface {
|
|
Tracef(fmt string, args ...any)
|
|
Debugf(fmt string, args ...any)
|
|
Infof(fmt string, args ...any)
|
|
Errorf(fmt string, args ...any)
|
|
Trace(args ...any)
|
|
Debug(args ...any)
|
|
Info(args ...any)
|
|
Error(args ...any)
|
|
WithFields(map[string]any) Logger
|
|
WithField(key string, value any) Logger
|
|
WithContext(ctx context.Context) Logger
|
|
Writer() io.Writer
|
|
// Enabled reports whether the logger will emit logs at the given level.
|
|
// Use this to guard expensive log calls and avoid unnecessary allocations:
|
|
//
|
|
// if logger.Enabled(logging.DebugLevel) {
|
|
// logger.WithFields(map[string]any{...}).Debugf("...")
|
|
// }
|
|
Enabled(level Level) bool
|
|
}
|
|
|
|
func Tracef(format string, args ...any) {
|
|
FromContext(context.TODO()).Tracef(format, args...)
|
|
}
|
|
func Debugf(format string, args ...any) {
|
|
FromContext(context.TODO()).Debugf(format, args...)
|
|
}
|
|
func Infof(format string, args ...any) {
|
|
FromContext(context.TODO()).Infof(format, args...)
|
|
}
|
|
func Errorf(format string, args ...any) {
|
|
FromContext(context.TODO()).Errorf(format, args...)
|
|
}
|
|
func Trace(args ...any) {
|
|
FromContext(context.TODO()).Trace(args...)
|
|
}
|
|
func Debug(args ...any) {
|
|
FromContext(context.TODO()).Debug(args...)
|
|
}
|
|
func Info(args ...any) {
|
|
FromContext(context.TODO()).Info(args...)
|
|
}
|
|
func Error(args ...any) {
|
|
FromContext(context.TODO()).Error(args...)
|
|
}
|
|
func WithFields(fields map[string]any) Logger {
|
|
return FromContext(context.TODO()).WithFields(fields)
|
|
}
|