pkg/logger/logger.go (151 lines of code) (raw):
package logger
import (
"context"
"fmt"
"log/slog"
"os"
"runtime"
"time"
"github.com/lmittmann/tint"
"github.com/mattn/go-isatty"
)
const timeFormat = "2006-01-02T15:04:05.000000Z07:00"
var (
levelVar = new(slog.LevelVar)
debugmsg = slog.StringValue("DBG")
infomsg = slog.StringValue("INF")
warnmsg = slog.StringValue("WRN")
errormsg = slog.StringValue("ERR")
)
func init() {
var logger *slog.Logger
if !isatty.IsTerminal(os.Stdout.Fd()) {
logger = slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{Level: levelVar, ReplaceAttr: replaceAttr}))
} else {
logger = slog.New(tint.NewHandler(os.Stdout, &tint.Options{Level: levelVar, TimeFormat: timeFormat}))
}
slog.SetDefault(logger)
level := os.Getenv("LOG_LEVEL")
if level == "" {
return
}
switch level {
case "ERROR":
levelVar.Set(slog.LevelError)
case "WARN":
levelVar.Set(slog.LevelWarn)
case "INFO":
levelVar.Set(slog.LevelInfo)
case "DEBUG":
levelVar.Set(slog.LevelDebug)
case "TRACE":
levelVar.Set(slog.LevelDebug)
default:
fmt.Printf("Unknown log level: %s != [ERROR,WARN,INFO,DEBUG,TRACE]\n", level)
}
}
func SetLevel(level slog.Level) {
levelVar.Set(level)
}
func IsDebug() bool {
return slog.Default().Enabled(context.Background(), slog.LevelDebug)
}
func IsInfo() bool {
return slog.Default().Enabled(context.Background(), slog.LevelInfo)
}
func IsWarn() bool {
return slog.Default().Enabled(context.Background(), slog.LevelWarn)
}
var (
Debug = slog.Debug
Info = slog.Info
Warn = slog.Warn
Error = slog.Error
// Fatal defined below
)
func Logger() *slog.Logger {
return slog.Default()
}
// For these wrappers, we need to capture the caller's PC to allow source line functionality to work.
// This does not add any allocs and does not affect performance.
func Debugf(format string, args ...interface{}) {
logger := slog.Default()
if !logger.Enabled(context.Background(), slog.LevelDebug) {
return
}
var pcs [1]uintptr
runtime.Callers(2, pcs[:]) // skip [Callers, Infof]
r := slog.NewRecord(time.Now(), slog.LevelDebug, fmt.Sprintf(format, args...), pcs[0])
_ = logger.Handler().Handle(context.Background(), r)
}
func Infof(format string, args ...interface{}) {
logger := slog.Default()
if !logger.Enabled(context.Background(), slog.LevelInfo) {
return
}
var pcs [1]uintptr
runtime.Callers(2, pcs[:]) // skip [Callers, Infof]
r := slog.NewRecord(time.Now(), slog.LevelInfo, fmt.Sprintf(format, args...), pcs[0])
_ = logger.Handler().Handle(context.Background(), r)
}
func Warnf(format string, args ...interface{}) {
logger := slog.Default()
if !logger.Enabled(context.Background(), slog.LevelWarn) {
return
}
var pcs [1]uintptr
runtime.Callers(2, pcs[:]) // skip [Callers, Infof]
r := slog.NewRecord(time.Now(), slog.LevelWarn, fmt.Sprintf(format, args...), pcs[0])
_ = logger.Handler().Handle(context.Background(), r)
}
func Errorf(format string, args ...interface{}) {
logger := slog.Default()
if !logger.Enabled(context.Background(), slog.LevelError) {
return
}
var pcs [1]uintptr
runtime.Callers(2, pcs[:]) // skip [Callers, Infof]
r := slog.NewRecord(time.Now(), slog.LevelError, fmt.Sprintf(format, args...), pcs[0])
_ = logger.Handler().Handle(context.Background(), r)
}
func Fatal(msg string, attrs ...slog.Attr) {
logger := slog.Default()
if !logger.Enabled(context.Background(), slog.LevelError) {
return
}
var pcs [1]uintptr
runtime.Callers(2, pcs[:]) // skip [Callers, Infof]
r := slog.NewRecord(time.Now(), slog.LevelError, msg, pcs[0])
r.AddAttrs(attrs...)
_ = logger.Handler().Handle(context.Background(), r)
os.Exit(1)
}
func Fatalf(format string, args ...interface{}) {
logger := slog.Default()
if !logger.Enabled(context.Background(), slog.LevelError) {
return
}
var pcs [1]uintptr
runtime.Callers(2, pcs[:]) // skip [Callers, Infof]
r := slog.NewRecord(time.Now(), slog.LevelError, fmt.Sprintf(format, args...), pcs[0])
_ = logger.Handler().Handle(context.Background(), r)
os.Exit(1)
}
func replaceAttr(groups []string, a slog.Attr) slog.Attr {
if a.Key == slog.TimeKey {
a.Key = "ts"
a.Value = slog.AnyValue(a.Value.Any().(time.Time).UTC().Format(timeFormat))
}
if a.Key == slog.LevelKey {
a.Key = "lvl"
level := a.Value.Any().(slog.Level)
switch {
case level < slog.LevelInfo:
a.Value = debugmsg
case level < slog.LevelWarn:
a.Value = infomsg
case level < slog.LevelError:
a.Value = warnmsg
default:
a.Value = errormsg
}
}
return a
}