From c346ac43dd331a519ca29a62b419610b941a674d Mon Sep 17 00:00:00 2001 From: Evstigneev Denis Date: Sat, 16 Sep 2023 14:19:21 +0300 Subject: [PATCH] add google/slog logger --- go.mod | 3 +- go.sum | 3 + logger/slog.go | 259 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 264 insertions(+), 1 deletion(-) create mode 100644 logger/slog.go diff --git a/go.mod b/go.mod index 2a89480a..75077ed4 100644 --- a/go.mod +++ b/go.mod @@ -9,12 +9,13 @@ require ( github.com/patrickmn/go-cache v2.1.0+incompatible github.com/silas/dag v0.0.0-20220518035006-a7e85ada93c5 golang.org/x/sync v0.1.0 - golang.org/x/sys v0.7.0 + golang.org/x/sys v0.12.0 google.golang.org/grpc v1.54.0 google.golang.org/protobuf v1.30.0 ) require ( github.com/golang/protobuf v1.5.3 // indirect + golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect ) diff --git a/go.sum b/go.sum index 1d7674b4..2b961310 100644 --- a/go.sum +++ b/go.sum @@ -13,11 +13,14 @@ github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaR github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= github.com/silas/dag v0.0.0-20220518035006-a7e85ada93c5 h1:G/FZtUu7a6NTWl3KUHMV9jkLAh/Rvtf03NWMHaEDl+E= github.com/silas/dag v0.0.0-20220518035006-a7e85ada93c5/go.mod h1:7RTUFBdIRC9nZ7/3RyRNH1bdqIShrDejd1YbLwgPS+I= +golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g= +golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k= golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ= golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU= golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 h1:KpwkzHKEF7B9Zxg18WzOa7djJ+Ha5DzthMyZYQfEn2A= diff --git a/logger/slog.go b/logger/slog.go new file mode 100644 index 00000000..483edd3e --- /dev/null +++ b/logger/slog.go @@ -0,0 +1,259 @@ +package logger + +import ( + "context" + "fmt" + "go.unistack.org/micro/v4/options" + "log" + "sync" + + "golang.org/x/exp/slog" +) + +const ( + slogName = "slog" +) + +type slogLogger struct { + slog *slog.Logger + fields map[string]interface{} + opts Options + + sync.RWMutex +} + +//TODO:!!!! + +func (s *slogLogger) Clone(opts ...options.Option) Logger { + //TODO implement me + panic("implement me") +} + +func (s *slogLogger) V(level Level) bool { + //TODO implement me + panic("implement me") +} + +func (s *slogLogger) Level(level Level) { + //TODO implement me + panic("implement me") +} + +func (s *slogLogger) Options() Options { + //TODO implement me + panic("implement me") +} + +func (s *slogLogger) Fields(fields ...interface{}) Logger { + //TODO implement me + panic("implement me") +} + +func (s *slogLogger) Trace(ctx context.Context, args ...interface{}) { + //TODO implement me + panic("implement me") +} + +func (s *slogLogger) Tracef(ctx context.Context, msg string, args ...interface{}) { + //TODO implement me + panic("implement me") +} + +func (s *slogLogger) Fatalf(ctx context.Context, msg string, args ...interface{}) { + //TODO implement me + panic("implement me") +} + +func (s *slogLogger) Init(opts ...options.Option) error { + for _, o := range opts { + if err := o(&s.opts); err != nil { + return err + } + } + + if slog, ok := s.opts.Context.Value(loggerKey{}).(*slog.Logger); ok { + s.slog = slog + return nil + } + + handleOpt := &slog.HandlerOptions{ + ReplaceAttr: renameTime, + Level: loggerToSlogLevel(s.opts.Level), + } + + attr := fieldsToAttr(s.fields) + + handler := slog.NewJSONHandler(s.opts.Out, handleOpt).WithAttrs(attr) + + s.slog = slog.New(handler) + + return nil +} + +func (s *slogLogger) Log(ctx context.Context, lvl Level, args ...any) { + slvl := loggerToSlogLevel(lvl) + + s.RLock() + attr := fieldsToAttr(s.fields) + s.RUnlock() + + msg := fmt.Sprint(args...) + + if lvl == FatalLevel { + log.Fatalln(msg, attr) + } + + if slvl == slog.LevelError { + l := s.slog.With(slog.Any("ProcStatus", "ERROR"), slog.Any("ErrorText", msg)) + l.LogAttrs(ctx, slvl, "", attr...) + return + } + + s.slog.LogAttrs(ctx, slvl, msg, attr...) +} + +func (s *slogLogger) Logf(ctx context.Context, lvl Level, format string, args ...any) { + slvl := loggerToSlogLevel(lvl) + + s.RLock() + attr := fieldsToAttr(s.fields) + s.RUnlock() + + msg := fmt.Sprintf(format, args...) + + if lvl == FatalLevel { + log.Fatalln(msg, attr) + } + + if slvl == slog.LevelError { + l := s.slog.With(slog.Any("ProcStatus", "ERROR"), slog.Any("ErrorText", msg)) + l.LogAttrs(ctx, slvl, "", attr...) + return + } + + s.slog.LogAttrs(ctx, slvl, msg, attr...) +} + +func (s *slogLogger) Info(ctx context.Context, args ...any) { + s.Log(ctx, InfoLevel, args...) +} + +func (s *slogLogger) Infof(ctx context.Context, format string, args ...interface{}) { + s.Logf(ctx, InfoLevel, format, args...) +} + +func (s *slogLogger) Debug(ctx context.Context, args ...any) { + s.Log(ctx, DebugLevel, args...) +} + +func (s *slogLogger) Debugf(ctx context.Context, format string, args ...any) { + s.Logf(ctx, DebugLevel, format, args...) +} + +func (s *slogLogger) Error(ctx context.Context, args ...any) { + s.Log(ctx, ErrorLevel, args...) +} + +func (s *slogLogger) Errorf(ctx context.Context, format string, args ...any) { + s.Logf(ctx, ErrorLevel, format, args...) +} + +func (s *slogLogger) Fatal(ctx context.Context, args ...any) { + s.Log(ctx, FatalLevel, args...) +} + +func (s *slogLogger) Warn(ctx context.Context, args ...any) { + s.Log(ctx, WarnLevel, args...) +} + +func (s *slogLogger) Warnf(ctx context.Context, format string, args ...any) { + s.Logf(ctx, WarnLevel, format, args...) +} + +func (s *slogLogger) String() string { + return slogName +} + +/* +func (s *slogLogger) Fields(fields map[string]interface{}) Logger { + nfields := make(map[string]interface{}, len(s.fields)) + + s.Lock() + for k, v := range s.fields { + nfields[k] = v + } + s.Unlock() + + for k, v := range fields { + nfields[k] = v + } + + keys := make([]string, 0, len(nfields)) + for k := range nfields { + keys = append(keys, k) + } + sort.Strings(keys) + + attr := make([]slog.Attr, 0, len(nfields)) + for _, k := range keys { + attr = append(attr, slog.Any(k, fields[k])) + } + + handleOpt := &slog.HandlerOptions{ + ReplaceAttr: renameTime, + Level: loggerToSlogLevel(s.opts.Level), + } + + handler := slog.NewJSONHandler(s.opts.Out, handleOpt).WithAttrs(attr) + + zl := &slogLogger{ + slog: slog.New(handler), + opts: s.opts, + fields: make(map[string]interface{}), + } + + return zl +} + +*/ + +func NewSlogLogger(opts ...options.Option) (Logger, error) { + l := &slogLogger{ + opts: NewOptions(opts...), + } + err := l.Init() + return l, err +} + +func renameTime(groups []string, a slog.Attr) slog.Attr { + if a.Key == slog.TimeKey { + a.Key = "@timestamp" + } + if a.Key == slog.MessageKey { + a.Key = "message" + } + + return a +} + +func loggerToSlogLevel(level Level) slog.Level { + switch level { + case TraceLevel, DebugLevel: + return slog.LevelDebug + case WarnLevel: + return slog.LevelWarn + case ErrorLevel, FatalLevel: + return slog.LevelError + default: + return slog.LevelInfo + } +} + +func fieldsToAttr(m map[string]any) []slog.Attr { + data := make([]slog.Attr, 0, len(m)) + for k, v := range m { + data = append(data, slog.Any(k, v)) + } + + return data +}