logger/slog: backport default logger keys from master #311

Merged
vtolstov merged 1 commits from v3-logger into v3 2024-03-05 01:54:19 +03:00
4 changed files with 108 additions and 108 deletions

View File

@ -6,6 +6,10 @@ import (
"os" "os"
) )
type ContextAttrFunc func(ctx context.Context) []interface{}
var DefaultContextAttrFuncs []ContextAttrFunc
var ( var (
// DefaultLogger variable // DefaultLogger variable
DefaultLogger = NewLogger(WithLevel(ParseLevel(os.Getenv("MICRO_LOG_LEVEL")))) DefaultLogger = NewLogger(WithLevel(ParseLevel(os.Getenv("MICRO_LOG_LEVEL"))))

View File

@ -3,6 +3,7 @@ package logger
import ( import (
"context" "context"
"io" "io"
"log/slog"
"os" "os"
) )
@ -21,6 +22,18 @@ type Options struct {
Fields []interface{} Fields []interface{}
// CallerSkipCount number of frmaes to skip // CallerSkipCount number of frmaes to skip
CallerSkipCount int CallerSkipCount int
// ContextAttrFuncs contains funcs that executed before log func on context
ContextAttrFuncs []ContextAttrFunc
// TimeKey is the key used for the time of the log call
TimeKey string
// LevelKey is the key used for the level of the log call
LevelKey string
// MessageKey is the key used for the message of the log call
MessageKey string
// SourceKey is the key used for the source file and line of the log call
SourceKey string
// StacktraceKey is the key used for the stacktrace
StacktraceKey string
// Stacktrace controls writing of stacktaces on error // Stacktrace controls writing of stacktaces on error
Stacktrace bool Stacktrace bool
// The logging level the logger should log // The logging level the logger should log
@ -35,13 +48,24 @@ func NewOptions(opts ...Option) Options {
Out: os.Stderr, Out: os.Stderr,
CallerSkipCount: DefaultCallerSkipCount, CallerSkipCount: DefaultCallerSkipCount,
Context: context.Background(), Context: context.Background(),
ContextAttrFuncs: DefaultContextAttrFuncs,
} }
WithMicroKeys()(&options)
for _, o := range opts { for _, o := range opts {
o(&options) o(&options)
} }
return options return options
} }
// WithContextAttrFuncs appends default funcs for the context arrts filler
func WithContextAttrFuncs(fncs ...ContextAttrFunc) Option {
return func(o *Options) {
o.ContextAttrFuncs = append(o.ContextAttrFuncs, fncs...)
}
}
// WithFields set default fields for the logger // WithFields set default fields for the logger
func WithFields(fields ...interface{}) Option { func WithFields(fields ...interface{}) Option {
return func(o *Options) { return func(o *Options) {
@ -90,3 +114,43 @@ func WithName(n string) Option {
o.Name = n o.Name = n
} }
} }
func WithZapKeys() Option {
return func(o *Options) {
o.TimeKey = "@timestamp"
o.LevelKey = "level"
o.MessageKey = "msg"
o.SourceKey = "caller"
o.StacktraceKey = "stacktrace"
}
}
func WithZerologKeys() Option {
return func(o *Options) {
o.TimeKey = "time"
o.LevelKey = "level"
o.MessageKey = "message"
o.SourceKey = "caller"
o.StacktraceKey = "stacktrace"
}
}
func WithSlogKeys() Option {
return func(o *Options) {
o.TimeKey = slog.TimeKey
o.LevelKey = slog.LevelKey
o.MessageKey = slog.MessageKey
o.SourceKey = slog.SourceKey
o.StacktraceKey = "stacktrace"
}
}
func WithMicroKeys() Option {
return func(o *Options) {
o.TimeKey = "timestamp"
o.LevelKey = "level"
o.MessageKey = "msg"
o.SourceKey = "caller"
o.StacktraceKey = "stacktrace"
}
}

View File

@ -1,27 +0,0 @@
package slog
import "go.unistack.org/micro/v3/logger"
type sourceKey struct{}
func WithSourceKey(v string) logger.Option {
return logger.SetOption(sourceKey{}, v)
}
type timeKey struct{}
func WithTimeKey(v string) logger.Option {
return logger.SetOption(timeKey{}, v)
}
type messageKey struct{}
func WithMessageKey(v string) logger.Option {
return logger.SetOption(messageKey{}, v)
}
type levelKey struct{}
func WithLevelKey(v string) logger.Option {
return logger.SetOption(levelKey{}, v)
}

View File

@ -17,13 +17,6 @@ import (
var reTrace = regexp.MustCompile(`.*/slog/logger\.go.*\n`) var reTrace = regexp.MustCompile(`.*/slog/logger\.go.*\n`)
var (
DefaultSourceKey = slog.SourceKey
DefaultTimeKey = slog.TimeKey
DefaultMessageKey = slog.MessageKey
DefaultLevelKey = slog.LevelKey
)
var ( var (
traceValue = slog.StringValue("trace") traceValue = slog.StringValue("trace")
debugValue = slog.StringValue("debug") debugValue = slog.StringValue("debug")
@ -38,15 +31,15 @@ func (s *slogLogger) renameAttr(_ []string, a slog.Attr) slog.Attr {
case slog.SourceKey: case slog.SourceKey:
source := a.Value.Any().(*slog.Source) source := a.Value.Any().(*slog.Source)
a.Value = slog.StringValue(source.File + ":" + strconv.Itoa(source.Line)) a.Value = slog.StringValue(source.File + ":" + strconv.Itoa(source.Line))
a.Key = s.sourceKey a.Key = s.opts.SourceKey
case slog.TimeKey: case slog.TimeKey:
a.Key = s.timeKey a.Key = s.opts.TimeKey
case slog.MessageKey: case slog.MessageKey:
a.Key = s.messageKey a.Key = s.opts.MessageKey
case slog.LevelKey: case slog.LevelKey:
level := a.Value.Any().(slog.Level) level := a.Value.Any().(slog.Level)
lvl := slogToLoggerLevel(level) lvl := slogToLoggerLevel(level)
a.Key = s.levelKey a.Key = s.opts.LevelKey
switch { switch {
case lvl < logger.DebugLevel: case lvl < logger.DebugLevel:
a.Value = traceValue a.Value = traceValue
@ -71,10 +64,6 @@ func (s *slogLogger) renameAttr(_ []string, a slog.Attr) slog.Attr {
type slogLogger struct { type slogLogger struct {
slog *slog.Logger slog *slog.Logger
leveler *slog.LevelVar leveler *slog.LevelVar
levelKey string
messageKey string
sourceKey string
timeKey string
opts logger.Options opts logger.Options
mu sync.RWMutex mu sync.RWMutex
} }
@ -89,23 +78,6 @@ func (s *slogLogger) Clone(opts ...logger.Option) logger.Logger {
l := &slogLogger{ l := &slogLogger{
opts: options, opts: options,
levelKey: s.levelKey,
messageKey: s.messageKey,
sourceKey: s.sourceKey,
timeKey: s.timeKey,
}
if v, ok := l.opts.Context.Value(levelKey{}).(string); ok && v != "" {
l.levelKey = v
}
if v, ok := l.opts.Context.Value(messageKey{}).(string); ok && v != "" {
l.messageKey = v
}
if v, ok := l.opts.Context.Value(sourceKey{}).(string); ok && v != "" {
l.sourceKey = v
}
if v, ok := l.opts.Context.Value(timeKey{}).(string); ok && v != "" {
l.timeKey = v
} }
l.leveler = new(slog.LevelVar) l.leveler = new(slog.LevelVar)
@ -137,13 +109,7 @@ func (s *slogLogger) Options() logger.Options {
func (s *slogLogger) Fields(attrs ...interface{}) logger.Logger { func (s *slogLogger) Fields(attrs ...interface{}) logger.Logger {
s.mu.RLock() s.mu.RLock()
nl := &slogLogger{ nl := &slogLogger{opts: s.opts}
opts: s.opts,
levelKey: s.levelKey,
messageKey: s.messageKey,
sourceKey: s.sourceKey,
timeKey: s.timeKey,
}
nl.leveler = new(slog.LevelVar) nl.leveler = new(slog.LevelVar)
nl.leveler.Set(s.leveler.Level()) nl.leveler.Set(s.leveler.Level())
@ -163,21 +129,13 @@ func (s *slogLogger) Fields(attrs ...interface{}) logger.Logger {
func (s *slogLogger) Init(opts ...logger.Option) error { func (s *slogLogger) Init(opts ...logger.Option) error {
s.mu.Lock() s.mu.Lock()
for _, o := range opts {
o(&s.opts) if len(s.opts.ContextAttrFuncs) == 0 {
s.opts.ContextAttrFuncs = logger.DefaultContextAttrFuncs
} }
if v, ok := s.opts.Context.Value(levelKey{}).(string); ok && v != "" { for _, o := range opts {
s.levelKey = v o(&s.opts)
}
if v, ok := s.opts.Context.Value(messageKey{}).(string); ok && v != "" {
s.messageKey = v
}
if v, ok := s.opts.Context.Value(sourceKey{}).(string); ok && v != "" {
s.sourceKey = v
}
if v, ok := s.opts.Context.Value(timeKey{}).(string); ok && v != "" {
s.timeKey = v
} }
s.leveler = new(slog.LevelVar) s.leveler = new(slog.LevelVar)
@ -190,8 +148,6 @@ func (s *slogLogger) Init(opts ...logger.Option) error {
handler := slog.NewJSONHandler(s.opts.Out, handleOpt) handler := slog.NewJSONHandler(s.opts.Out, handleOpt)
s.slog = slog.New(handler).With(s.opts.Fields...) s.slog = slog.New(handler).With(s.opts.Fields...)
slog.SetDefault(s.slog)
s.mu.Unlock() s.mu.Unlock()
return nil return nil
@ -204,6 +160,15 @@ func (s *slogLogger) Log(ctx context.Context, lvl logger.Level, attrs ...interfa
var pcs [1]uintptr var pcs [1]uintptr
runtime.Callers(s.opts.CallerSkipCount, pcs[:]) // skip [Callers, Infof] runtime.Callers(s.opts.CallerSkipCount, pcs[:]) // skip [Callers, Infof]
r := slog.NewRecord(time.Now(), loggerToSlogLevel(lvl), fmt.Sprintf("%s", attrs[0]), pcs[0]) r := slog.NewRecord(time.Now(), loggerToSlogLevel(lvl), fmt.Sprintf("%s", attrs[0]), pcs[0])
if s.opts.Stacktrace && lvl == logger.ErrorLevel {
stackInfo := make([]byte, 1024*1024)
if stackSize := runtime.Stack(stackInfo, false); stackSize > 0 {
traceLines := reTrace.Split(string(stackInfo[:stackSize]), -1)
if len(traceLines) != 0 {
r.AddAttrs(slog.String(s.opts.StacktraceKey, traceLines[len(traceLines)-1]))
}
}
}
// r.Add(attrs[1:]...) // r.Add(attrs[1:]...)
_ = s.slog.Handler().Handle(ctx, r) _ = s.slog.Handler().Handle(ctx, r)
} }
@ -215,6 +180,15 @@ func (s *slogLogger) Logf(ctx context.Context, lvl logger.Level, msg string, att
var pcs [1]uintptr var pcs [1]uintptr
runtime.Callers(s.opts.CallerSkipCount, pcs[:]) // skip [Callers, Infof] runtime.Callers(s.opts.CallerSkipCount, pcs[:]) // skip [Callers, Infof]
r := slog.NewRecord(time.Now(), loggerToSlogLevel(lvl), fmt.Sprintf(msg, attrs...), pcs[0]) r := slog.NewRecord(time.Now(), loggerToSlogLevel(lvl), fmt.Sprintf(msg, attrs...), pcs[0])
if s.opts.Stacktrace && lvl == logger.ErrorLevel {
stackInfo := make([]byte, 1024*1024)
if stackSize := runtime.Stack(stackInfo, false); stackSize > 0 {
traceLines := reTrace.Split(string(stackInfo[:stackSize]), -1)
if len(traceLines) != 0 {
r.AddAttrs(slog.String(s.opts.StacktraceKey, traceLines[len(traceLines)-1]))
}
}
}
// r.Add(attrs...) // r.Add(attrs...)
_ = s.slog.Handler().Handle(ctx, r) _ = s.slog.Handler().Handle(ctx, r)
} }
@ -400,23 +374,8 @@ func (s *slogLogger) String() string {
func NewLogger(opts ...logger.Option) logger.Logger { func NewLogger(opts ...logger.Option) logger.Logger {
s := &slogLogger{ s := &slogLogger{
opts: logger.NewOptions(opts...), opts: logger.NewOptions(opts...),
sourceKey: DefaultSourceKey,
timeKey: DefaultTimeKey,
messageKey: DefaultMessageKey,
levelKey: DefaultLevelKey,
}
if v, ok := s.opts.Context.Value(levelKey{}).(string); ok && v != "" {
s.levelKey = v
}
if v, ok := s.opts.Context.Value(messageKey{}).(string); ok && v != "" {
s.messageKey = v
}
if v, ok := s.opts.Context.Value(sourceKey{}).(string); ok && v != "" {
s.sourceKey = v
}
if v, ok := s.opts.Context.Value(timeKey{}).(string); ok && v != "" {
s.timeKey = v
} }
return s return s
} }