142 lines
3.1 KiB
Go
142 lines
3.1 KiB
Go
package rlog
|
|
|
|
import (
|
|
"context"
|
|
"log/slog"
|
|
"os"
|
|
"runtime"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/rs/zerolog"
|
|
slogmulti "github.com/samber/slog-multi"
|
|
slogzerolog "github.com/samber/slog-zerolog"
|
|
)
|
|
|
|
var (
|
|
zl = zerolog.New(&zerolog.ConsoleWriter{Out: os.Stderr})
|
|
handlers = []slog.Handler{
|
|
slogzerolog.Option{Logger: &zl}.NewZerologHandler(),
|
|
}
|
|
handler = slogmulti.Fanout(handlers...)
|
|
defaultLogger = slog.New(handler)
|
|
)
|
|
|
|
func init() {
|
|
slog.SetDefault(defaultLogger)
|
|
}
|
|
|
|
func AddHandler(nh slog.Handler) {
|
|
handlers = append(handlers, nh)
|
|
handler = slogmulti.Fanout(handlers...)
|
|
defaultLogger = slog.New(handler)
|
|
slog.SetDefault(defaultLogger)
|
|
}
|
|
|
|
type Logger struct {
|
|
handler slog.Handler
|
|
callNesting int
|
|
component []string
|
|
}
|
|
|
|
const functionKey = "function"
|
|
|
|
const componentKey = "component"
|
|
const componentSep = "."
|
|
|
|
func (l *Logger) log(ctx context.Context, level slog.Level, msg string, attrs ...slog.Attr) {
|
|
var pcs [1]uintptr
|
|
runtime.Callers(3+l.callNesting, pcs[:])
|
|
pc := pcs[0]
|
|
f := runtime.FuncForPC(pc)
|
|
if f != nil {
|
|
attrs = append(attrs, slog.String(functionKey, f.Name()))
|
|
}
|
|
|
|
if len(l.component) > 0 {
|
|
attrs = append(attrs, slog.String(componentKey, strings.Join(l.component, componentSep)))
|
|
}
|
|
|
|
r := slog.NewRecord(time.Now(), level, msg, pc)
|
|
r.AddAttrs(attrs...)
|
|
if ctx == nil {
|
|
ctx = context.Background()
|
|
}
|
|
|
|
_ = l.handler.Handle(ctx, r)
|
|
}
|
|
|
|
func (l *Logger) Debug(ctx context.Context, msg string, attrs ...slog.Attr) {
|
|
l.log(ctx, slog.LevelDebug, msg, attrs...)
|
|
}
|
|
|
|
func (l *Logger) Info(ctx context.Context, msg string, attrs ...slog.Attr) {
|
|
l.log(ctx, slog.LevelInfo, msg, attrs...)
|
|
}
|
|
|
|
func (l *Logger) Warn(ctx context.Context, msg string, attrs ...slog.Attr) {
|
|
l.log(ctx, slog.LevelWarn, msg, attrs...)
|
|
}
|
|
|
|
func (l *Logger) Error(ctx context.Context, msg string, attrs ...slog.Attr) {
|
|
l.log(ctx, slog.LevelError, msg, attrs...)
|
|
}
|
|
|
|
func (log *Logger) WithComponent(name string) *Logger {
|
|
return &Logger{
|
|
handler: log.handler,
|
|
component: append(log.component, name),
|
|
}
|
|
}
|
|
|
|
func (l *Logger) With(attrs ...slog.Attr) *Logger {
|
|
return &Logger{
|
|
handler: l.handler.WithAttrs(attrs),
|
|
component: l.component,
|
|
}
|
|
}
|
|
|
|
func (l *Logger) Nested(callNesting int) *Logger {
|
|
return &Logger{
|
|
handler: l.handler,
|
|
component: l.component,
|
|
callNesting: callNesting,
|
|
}
|
|
}
|
|
|
|
// returns a new slog logger with the same attribures as the original logger
|
|
// TODO currently not logging function name
|
|
func (l *Logger) Slog() *slog.Logger {
|
|
return slog.New(l.handler)
|
|
}
|
|
|
|
const errKey = "error"
|
|
|
|
func Error(err error) slog.Attr {
|
|
return slog.Attr{Key: errKey, Value: errValue(err)}
|
|
}
|
|
|
|
// errValue returns a slog.GroupValue with keys "msg" and "trace". If the error
|
|
// does not implement interface { StackTrace() errors.StackTrace }, the "trace"
|
|
// key is omitted.
|
|
func errValue(err error) slog.Value {
|
|
if err == nil {
|
|
return slog.AnyValue(nil)
|
|
}
|
|
|
|
var groupValues []slog.Attr
|
|
|
|
groupValues = append(groupValues,
|
|
slog.String("msg", err.Error()),
|
|
slog.Any("value", err),
|
|
)
|
|
|
|
return slog.GroupValue(groupValues...)
|
|
}
|
|
|
|
func Component(name ...string) *Logger {
|
|
return &Logger{
|
|
handler: handler,
|
|
component: name,
|
|
}
|
|
}
|