Files
hauler/pkg/log/log.go
T
2026-08-05 16:34:41 -04:00

111 lines
2.8 KiB
Go

package log
import (
"context"
"io"
"github.com/rs/zerolog"
"github.com/rs/zerolog/log"
"hauler.dev/go/hauler/v2/pkg/consts"
)
// Logger provides an interface for all used logger features regardless of logging backend
type Logger interface {
SetLevel(string)
With(Fields) *logger
WithContext(context.Context) context.Context
Errorf(string, ...interface{})
Infof(string, ...interface{})
Warnf(string, ...interface{})
Debugf(string, ...interface{})
}
type logger struct {
zl zerolog.Logger
}
// Fields defines fields to attach to log msgs
type Fields map[string]string
// NewLogger returns a new Logger
func NewLogger(out io.Writer) Logger {
zerolog.TimeFieldFormat = consts.CustomTimeFormat
// out is bound once here, captured in the ConsoleWriter's closure, so
// this logger keeps writing to its original writer even after
// CaptureOutput swaps the process-global os.Stdout/os.Stderr during a
// Helm chart traversal -- why CaptureOutput never captures hauler's own
// log lines.
//
// NoColor is decided from the real os.Stdout, not out, matching
// ShouldShowProgress: the live-region path wraps a Renderer around the
// real os.Stdout, whose terminal-ness ShouldShowProgress has already
// confirmed. out itself is often a *bytes.Buffer (tests) or the
// Renderer wrapper, neither a terminal, so isatty on out would say no.
output := zerolog.ConsoleWriter{
Out: zerolog.SyncWriter(out),
TimeFormat: consts.CustomTimeFormat,
NoColor: !ShouldUseColor(),
}
l := log.Output(output)
return &logger{
zl: l.With().Timestamp().Logger(),
}
}
// FromContext returns a Logger from a context if it exists
func FromContext(ctx context.Context) Logger {
zl := zerolog.Ctx(ctx)
return &logger{
zl: *zl,
}
}
// SetLevel sets the global log level
func (l *logger) SetLevel(level string) {
lvl, err := zerolog.ParseLevel(level)
if err != nil {
lvl, _ = zerolog.ParseLevel("info")
}
zerolog.SetGlobalLevel(lvl)
}
// WithContext stores the Logger in the given context and returns it
func (l *logger) WithContext(ctx context.Context) context.Context {
return l.zl.WithContext(ctx)
}
// With attaches Fields to a Logger
func (l *logger) With(fields Fields) *logger {
zl := l.zl.With()
for k, v := range fields {
zl = zl.Str(k, v)
}
return &logger{
zl: zl.Logger(),
}
}
// Errorf prints a formatted ERR message
func (l *logger) Errorf(format string, args ...interface{}) {
l.zl.Error().Msgf(format, args...)
}
// Infof prints a formatted INFO message
func (l *logger) Infof(format string, args ...interface{}) {
l.zl.Info().Msgf(format, args...)
}
// Warnf prints a formatted WARN message
func (l *logger) Warnf(format string, args ...interface{}) {
l.zl.Warn().Msgf(format, args...)
}
// Debugf prints a formatted DBG message
func (l *logger) Debugf(format string, args ...interface{}) {
l.zl.Debug().Msgf(format, args...)
}