logger/slog: backport default logger keys from master #311
| @@ -6,6 +6,10 @@ import ( | ||||
| 	"os" | ||||
| ) | ||||
|  | ||||
| type ContextAttrFunc func(ctx context.Context) []interface{} | ||||
|  | ||||
| var DefaultContextAttrFuncs []ContextAttrFunc | ||||
|  | ||||
| var ( | ||||
| 	// DefaultLogger variable | ||||
| 	DefaultLogger = NewLogger(WithLevel(ParseLevel(os.Getenv("MICRO_LOG_LEVEL")))) | ||||
|   | ||||
| @@ -3,6 +3,7 @@ package logger | ||||
| import ( | ||||
| 	"context" | ||||
| 	"io" | ||||
| 	"log/slog" | ||||
| 	"os" | ||||
| ) | ||||
|  | ||||
| @@ -21,6 +22,18 @@ type Options struct { | ||||
| 	Fields []interface{} | ||||
| 	// CallerSkipCount number of frmaes to skip | ||||
| 	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 bool | ||||
| 	// The logging level the logger should log | ||||
| @@ -30,18 +43,29 @@ type Options struct { | ||||
| // NewOptions creates new options struct | ||||
| func NewOptions(opts ...Option) Options { | ||||
| 	options := Options{ | ||||
| 		Level:           DefaultLevel, | ||||
| 		Fields:          make([]interface{}, 0, 6), | ||||
| 		Out:             os.Stderr, | ||||
| 		CallerSkipCount: DefaultCallerSkipCount, | ||||
| 		Context:         context.Background(), | ||||
| 		Level:            DefaultLevel, | ||||
| 		Fields:           make([]interface{}, 0, 6), | ||||
| 		Out:              os.Stderr, | ||||
| 		CallerSkipCount:  DefaultCallerSkipCount, | ||||
| 		Context:          context.Background(), | ||||
| 		ContextAttrFuncs: DefaultContextAttrFuncs, | ||||
| 	} | ||||
|  | ||||
| 	WithMicroKeys()(&options) | ||||
|  | ||||
| 	for _, o := range opts { | ||||
| 		o(&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 | ||||
| func WithFields(fields ...interface{}) Option { | ||||
| 	return func(o *Options) { | ||||
| @@ -90,3 +114,43 @@ func WithName(n string) Option { | ||||
| 		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" | ||||
| 	} | ||||
| } | ||||
|   | ||||
| @@ -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) | ||||
| } | ||||
| @@ -17,13 +17,6 @@ import ( | ||||
|  | ||||
| var reTrace = regexp.MustCompile(`.*/slog/logger\.go.*\n`) | ||||
|  | ||||
| var ( | ||||
| 	DefaultSourceKey  = slog.SourceKey | ||||
| 	DefaultTimeKey    = slog.TimeKey | ||||
| 	DefaultMessageKey = slog.MessageKey | ||||
| 	DefaultLevelKey   = slog.LevelKey | ||||
| ) | ||||
|  | ||||
| var ( | ||||
| 	traceValue = slog.StringValue("trace") | ||||
| 	debugValue = slog.StringValue("debug") | ||||
| @@ -38,15 +31,15 @@ func (s *slogLogger) renameAttr(_ []string, a slog.Attr) slog.Attr { | ||||
| 	case slog.SourceKey: | ||||
| 		source := a.Value.Any().(*slog.Source) | ||||
| 		a.Value = slog.StringValue(source.File + ":" + strconv.Itoa(source.Line)) | ||||
| 		a.Key = s.sourceKey | ||||
| 		a.Key = s.opts.SourceKey | ||||
| 	case slog.TimeKey: | ||||
| 		a.Key = s.timeKey | ||||
| 		a.Key = s.opts.TimeKey | ||||
| 	case slog.MessageKey: | ||||
| 		a.Key = s.messageKey | ||||
| 		a.Key = s.opts.MessageKey | ||||
| 	case slog.LevelKey: | ||||
| 		level := a.Value.Any().(slog.Level) | ||||
| 		lvl := slogToLoggerLevel(level) | ||||
| 		a.Key = s.levelKey | ||||
| 		a.Key = s.opts.LevelKey | ||||
| 		switch { | ||||
| 		case lvl < logger.DebugLevel: | ||||
| 			a.Value = traceValue | ||||
| @@ -69,14 +62,10 @@ func (s *slogLogger) renameAttr(_ []string, a slog.Attr) slog.Attr { | ||||
| } | ||||
|  | ||||
| type slogLogger struct { | ||||
| 	slog       *slog.Logger | ||||
| 	leveler    *slog.LevelVar | ||||
| 	levelKey   string | ||||
| 	messageKey string | ||||
| 	sourceKey  string | ||||
| 	timeKey    string | ||||
| 	opts       logger.Options | ||||
| 	mu         sync.RWMutex | ||||
| 	slog    *slog.Logger | ||||
| 	leveler *slog.LevelVar | ||||
| 	opts    logger.Options | ||||
| 	mu      sync.RWMutex | ||||
| } | ||||
|  | ||||
| func (s *slogLogger) Clone(opts ...logger.Option) logger.Logger { | ||||
| @@ -88,24 +77,7 @@ func (s *slogLogger) Clone(opts ...logger.Option) logger.Logger { | ||||
| 	} | ||||
|  | ||||
| 	l := &slogLogger{ | ||||
| 		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 | ||||
| 		opts: options, | ||||
| 	} | ||||
|  | ||||
| 	l.leveler = new(slog.LevelVar) | ||||
| @@ -137,13 +109,7 @@ func (s *slogLogger) Options() logger.Options { | ||||
|  | ||||
| func (s *slogLogger) Fields(attrs ...interface{}) logger.Logger { | ||||
| 	s.mu.RLock() | ||||
| 	nl := &slogLogger{ | ||||
| 		opts:       s.opts, | ||||
| 		levelKey:   s.levelKey, | ||||
| 		messageKey: s.messageKey, | ||||
| 		sourceKey:  s.sourceKey, | ||||
| 		timeKey:    s.timeKey, | ||||
| 	} | ||||
| 	nl := &slogLogger{opts: s.opts} | ||||
| 	nl.leveler = new(slog.LevelVar) | ||||
| 	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 { | ||||
| 	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 != "" { | ||||
| 		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 | ||||
| 	for _, o := range opts { | ||||
| 		o(&s.opts) | ||||
| 	} | ||||
|  | ||||
| 	s.leveler = new(slog.LevelVar) | ||||
| @@ -190,8 +148,6 @@ func (s *slogLogger) Init(opts ...logger.Option) error { | ||||
| 	handler := slog.NewJSONHandler(s.opts.Out, handleOpt) | ||||
| 	s.slog = slog.New(handler).With(s.opts.Fields...) | ||||
|  | ||||
| 	slog.SetDefault(s.slog) | ||||
|  | ||||
| 	s.mu.Unlock() | ||||
|  | ||||
| 	return nil | ||||
| @@ -204,6 +160,15 @@ func (s *slogLogger) Log(ctx context.Context, lvl logger.Level, attrs ...interfa | ||||
| 	var pcs [1]uintptr | ||||
| 	runtime.Callers(s.opts.CallerSkipCount, pcs[:]) // skip [Callers, Infof] | ||||
| 	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:]...) | ||||
| 	_ = 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 | ||||
| 	runtime.Callers(s.opts.CallerSkipCount, pcs[:]) // skip [Callers, Infof] | ||||
| 	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...) | ||||
| 	_ = s.slog.Handler().Handle(ctx, r) | ||||
| } | ||||
| @@ -399,24 +373,9 @@ func (s *slogLogger) String() string { | ||||
|  | ||||
| func NewLogger(opts ...logger.Option) logger.Logger { | ||||
| 	s := &slogLogger{ | ||||
| 		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 | ||||
| 		opts: logger.NewOptions(opts...), | ||||
| 	} | ||||
|  | ||||
| 	return s | ||||
| } | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user