updates #207
| @@ -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")))) | ||||||
|   | |||||||
| @@ -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 | ||||||
| @@ -30,18 +43,29 @@ type Options struct { | |||||||
| // NewOptions creates new options struct | // NewOptions creates new options struct | ||||||
| func NewOptions(opts ...Option) Options { | func NewOptions(opts ...Option) Options { | ||||||
| 	options := Options{ | 	options := Options{ | ||||||
| 		Level:           DefaultLevel, | 		Level:            DefaultLevel, | ||||||
| 		Fields:          make([]interface{}, 0, 6), | 		Fields:           make([]interface{}, 0, 6), | ||||||
| 		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" | ||||||
|  | 	} | ||||||
|  | } | ||||||
|   | |||||||
| @@ -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 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 | ||||||
| @@ -69,14 +62,10 @@ 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 | 	opts    logger.Options | ||||||
| 	messageKey string | 	mu      sync.RWMutex | ||||||
| 	sourceKey  string |  | ||||||
| 	timeKey    string |  | ||||||
| 	opts       logger.Options |  | ||||||
| 	mu         sync.RWMutex |  | ||||||
| } | } | ||||||
|  |  | ||||||
| func (s *slogLogger) Clone(opts ...logger.Option) logger.Logger { | func (s *slogLogger) Clone(opts ...logger.Option) logger.Logger { | ||||||
| @@ -88,24 +77,7 @@ 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) | ||||||
| } | } | ||||||
| @@ -399,24 +373,9 @@ 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 | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user