From c346ac43dd331a519ca29a62b419610b941a674d Mon Sep 17 00:00:00 2001 From: Evstigneev Denis Date: Sat, 16 Sep 2023 14:19:21 +0300 Subject: [PATCH 1/4] 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 +} From fd471a89e231650a842e0e2403f1780c99670b41 Mon Sep 17 00:00:00 2001 From: Evstigneev Denis Date: Mon, 18 Sep 2023 10:59:21 +0300 Subject: [PATCH 2/4] deleted unnecessary operations --- logger/slog.go | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/logger/slog.go b/logger/slog.go index 483edd3e..9b174ffa 100644 --- a/logger/slog.go +++ b/logger/slog.go @@ -103,12 +103,6 @@ func (s *slogLogger) Log(ctx context.Context, lvl Level, args ...any) { 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...) } @@ -125,12 +119,6 @@ func (s *slogLogger) Logf(ctx context.Context, lvl Level, format string, args .. 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...) } @@ -229,9 +217,6 @@ 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 } From f600195ee2ac2a812ee21453b27e82eb8cd1d594 Mon Sep 17 00:00:00 2001 From: Evstigneev Denis Date: Mon, 18 Sep 2023 10:59:46 +0300 Subject: [PATCH 3/4] go mod tidy --- go.mod | 2 +- go.sum | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 75077ed4..b91f98e2 100644 --- a/go.mod +++ b/go.mod @@ -8,6 +8,7 @@ require ( github.com/imdario/mergo v0.3.15 github.com/patrickmn/go-cache v2.1.0+incompatible github.com/silas/dag v0.0.0-20220518035006-a7e85ada93c5 + golang.org/x/exp v0.0.0-20230905200255-921286631fa9 golang.org/x/sync v0.1.0 golang.org/x/sys v0.12.0 google.golang.org/grpc v1.54.0 @@ -16,6 +17,5 @@ require ( 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 2b961310..17e5865d 100644 --- a/go.sum +++ b/go.sum @@ -18,8 +18,7 @@ golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqR 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 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= 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= From 0df97183f88a7baac15bc810f4ca4b4afbfeea78 Mon Sep 17 00:00:00 2001 From: Vasiliy Tolstov Date: Sat, 14 Oct 2023 16:58:40 +0300 Subject: [PATCH 4/4] tracer: update wrapper Signed-off-by: Vasiliy Tolstov --- go.mod | 9 +- go.sum | 20 +-- logger/default.go | 232 ---------------------------------- logger/logger_test.go | 34 ++--- logger/slog.go | 255 +++++++++++++++++++------------------- metadata/metadata.go | 2 + tracer/options.go | 24 +++- tracer/wrapper/wrapper.go | 103 +++++++++------ 8 files changed, 251 insertions(+), 428 deletions(-) delete mode 100644 logger/default.go diff --git a/go.mod b/go.mod index b91f98e2..3216d4c6 100644 --- a/go.mod +++ b/go.mod @@ -9,13 +9,14 @@ require ( github.com/patrickmn/go-cache v2.1.0+incompatible github.com/silas/dag v0.0.0-20220518035006-a7e85ada93c5 golang.org/x/exp v0.0.0-20230905200255-921286631fa9 - golang.org/x/sync v0.1.0 + golang.org/x/sync v0.3.0 golang.org/x/sys v0.12.0 - google.golang.org/grpc v1.54.0 - google.golang.org/protobuf v1.30.0 + google.golang.org/grpc v1.57.0 + google.golang.org/protobuf v1.31.0 + ) require ( github.com/golang/protobuf v1.5.3 // indirect - google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234030-28d5490b6b19 // indirect ) diff --git a/go.sum b/go.sum index 17e5865d..812c2f7d 100644 --- a/go.sum +++ b/go.sum @@ -15,21 +15,21 @@ github.com/silas/dag v0.0.0-20220518035006-a7e85ada93c5 h1:G/FZtUu7a6NTWl3KUHMV9 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/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM= +golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= +golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68= +golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= 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= -google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1/go.mod h1:nKE/iIaLqn2bQwXBg8f1g2Ylh6r5MN5CmZvuzZCgsCU= -google.golang.org/grpc v1.54.0 h1:EhTqbhiYeixwWQtAEZAxmV9MGqcjEU2mFx52xCzNyag= -google.golang.org/grpc v1.54.0/go.mod h1:PUSEXI6iWghWaB6lXM4knEgpJNu2qUcKfDtNci3EC2g= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234030-28d5490b6b19 h1:0nDDozoAU19Qb2HwhXadU8OcsiO/09cnTqhUtq2MEOM= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234030-28d5490b6b19/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA= +google.golang.org/grpc v1.57.0 h1:kfzNeI/klCGD2YPMUlaGNT3pxvYfga7smW3Vth8Zsiw= +google.golang.org/grpc v1.57.0/go.mod h1:Sd+9RMTACXwmub0zcNY2c4arhtrbBYD1AUHI/dt16Mo= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= -google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= +google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/logger/default.go b/logger/default.go deleted file mode 100644 index f747ee9d..00000000 --- a/logger/default.go +++ /dev/null @@ -1,232 +0,0 @@ -package logger - -import ( - "context" - "encoding/json" - "fmt" - "os" - "runtime" - "strings" - "sync" - "time" - - "go.unistack.org/micro/v4/options" -) - -type defaultLogger struct { - enc *json.Encoder - opts Options - sync.RWMutex -} - -// Init(opts...) should only overwrite provided options -func (l *defaultLogger) Init(opts ...options.Option) error { - l.Lock() - for _, o := range opts { - o(&l.opts) - } - l.enc = json.NewEncoder(l.opts.Out) - // wrap the Log func - l.Unlock() - return nil -} - -func (l *defaultLogger) String() string { - return "micro" -} - -func (l *defaultLogger) Clone(opts ...options.Option) Logger { - newopts := NewOptions(opts...) - oldopts := l.opts - for _, o := range opts { - o(&newopts) - o(&oldopts) - } - - l.Lock() - cl := &defaultLogger{opts: oldopts, enc: json.NewEncoder(l.opts.Out)} - l.Unlock() - - return cl -} - -func (l *defaultLogger) V(level Level) bool { - l.RLock() - ok := l.opts.Level.Enabled(level) - l.RUnlock() - return ok -} - -func (l *defaultLogger) Level(level Level) { - l.Lock() - l.opts.Level = level - l.Unlock() -} - -func (l *defaultLogger) Fields(fields ...interface{}) Logger { - l.RLock() - nl := &defaultLogger{opts: l.opts, enc: l.enc} - if len(fields) == 0 { - l.RUnlock() - return nl - } else if len(fields)%2 != 0 { - fields = fields[:len(fields)-1] - } - nl.opts.Fields = copyFields(l.opts.Fields) - nl.opts.Fields = append(nl.opts.Fields, fields...) - l.RUnlock() - return nl -} - -func copyFields(src []interface{}) []interface{} { - dst := make([]interface{}, len(src)) - copy(dst, src) - return dst -} - -// logCallerfilePath returns a package/file:line description of the caller, -// preserving only the leaf directory name and file name. -func logCallerfilePath(loggingFilePath string) string { - // To make sure we trim the path correctly on Windows too, we - // counter-intuitively need to use '/' and *not* os.PathSeparator here, - // because the path given originates from Go stdlib, specifically - // runtime.Caller() which (as of Mar/17) returns forward slashes even on - // Windows. - // - // See https://github.com/golang/go/issues/3335 - // and https://github.com/golang/go/issues/18151 - // - // for discussion on the issue on Go side. - idx := strings.LastIndexByte(loggingFilePath, '/') - if idx == -1 { - return loggingFilePath - } - idx = strings.LastIndexByte(loggingFilePath[:idx], '/') - if idx == -1 { - return loggingFilePath - } - return loggingFilePath[idx+1:] -} - -func (l *defaultLogger) Info(ctx context.Context, args ...interface{}) { - l.Log(ctx, InfoLevel, args...) -} - -func (l *defaultLogger) Error(ctx context.Context, args ...interface{}) { - l.Log(ctx, ErrorLevel, args...) -} - -func (l *defaultLogger) Debug(ctx context.Context, args ...interface{}) { - l.Log(ctx, DebugLevel, args...) -} - -func (l *defaultLogger) Warn(ctx context.Context, args ...interface{}) { - l.Log(ctx, WarnLevel, args...) -} - -func (l *defaultLogger) Trace(ctx context.Context, args ...interface{}) { - l.Log(ctx, TraceLevel, args...) -} - -func (l *defaultLogger) Fatal(ctx context.Context, args ...interface{}) { - l.Log(ctx, FatalLevel, args...) - os.Exit(1) -} - -func (l *defaultLogger) Infof(ctx context.Context, msg string, args ...interface{}) { - l.Logf(ctx, InfoLevel, msg, args...) -} - -func (l *defaultLogger) Errorf(ctx context.Context, msg string, args ...interface{}) { - l.Logf(ctx, ErrorLevel, msg, args...) -} - -func (l *defaultLogger) Debugf(ctx context.Context, msg string, args ...interface{}) { - l.Logf(ctx, DebugLevel, msg, args...) -} - -func (l *defaultLogger) Warnf(ctx context.Context, msg string, args ...interface{}) { - l.Logf(ctx, WarnLevel, msg, args...) -} - -func (l *defaultLogger) Tracef(ctx context.Context, msg string, args ...interface{}) { - l.Logf(ctx, TraceLevel, msg, args...) -} - -func (l *defaultLogger) Fatalf(ctx context.Context, msg string, args ...interface{}) { - l.Logf(ctx, FatalLevel, msg, args...) - os.Exit(1) -} - -func (l *defaultLogger) Log(ctx context.Context, level Level, args ...interface{}) { - if !l.V(level) { - return - } - - l.RLock() - fields := copyFields(l.opts.Fields) - l.RUnlock() - - fields = append(fields, "level", level.String()) - - if _, file, line, ok := runtime.Caller(l.opts.CallerSkipCount); ok { - fields = append(fields, "caller", fmt.Sprintf("%s:%d", logCallerfilePath(file), line)) - } - fields = append(fields, "timestamp", time.Now().Format("2006-01-02 15:04:05")) - - if len(args) > 0 { - fields = append(fields, "msg", fmt.Sprint(args...)) - } - - out := make(map[string]interface{}, len(fields)/2) - for i := 0; i < len(fields); i += 2 { - out[fields[i].(string)] = fields[i+1] - } - l.RLock() - _ = l.enc.Encode(out) - l.RUnlock() -} - -func (l *defaultLogger) Logf(ctx context.Context, level Level, msg string, args ...interface{}) { - if !l.V(level) { - return - } - - l.RLock() - fields := copyFields(l.opts.Fields) - l.RUnlock() - - fields = append(fields, "level", level.String()) - - if _, file, line, ok := runtime.Caller(l.opts.CallerSkipCount); ok { - fields = append(fields, "caller", fmt.Sprintf("%s:%d", logCallerfilePath(file), line)) - } - - fields = append(fields, "timestamp", time.Now().Format("2006-01-02 15:04:05")) - if len(args) > 0 { - fields = append(fields, "msg", fmt.Sprintf(msg, args...)) - } else if msg != "" { - fields = append(fields, "msg", msg) - } - - out := make(map[string]interface{}, len(fields)/2) - for i := 0; i < len(fields); i += 2 { - out[fields[i].(string)] = fields[i+1] - } - l.RLock() - _ = l.enc.Encode(out) - l.RUnlock() -} - -func (l *defaultLogger) Options() Options { - return l.opts -} - -// NewLogger builds a new logger based on options -func NewLogger(opts ...options.Option) Logger { - l := &defaultLogger{ - opts: NewOptions(opts...), - } - l.enc = json.NewEncoder(l.opts.Out) - return l -} diff --git a/logger/logger_test.go b/logger/logger_test.go index b9b8146d..1fcf2050 100644 --- a/logger/logger_test.go +++ b/logger/logger_test.go @@ -87,14 +87,14 @@ func TestClone(t *testing.T) { func TestRedirectStdLogger(t *testing.T) { buf := bytes.NewBuffer(nil) - l := NewLogger(WithLevel(TraceLevel), WithOutput(buf)) + l := NewLogger(WithLevel(ErrorLevel), WithOutput(buf)) if err := l.Init(); err != nil { t.Fatal(err) } fn := RedirectStdLogger(l, ErrorLevel) defer fn() log.Print("test") - if !bytes.Contains(buf.Bytes(), []byte(`"level":"error","msg":"test","timestamp"`)) { + if !bytes.Contains(buf.Bytes(), []byte(`"level":"error","msg":"test"`)) { t.Fatalf("logger error, buf %s", buf.Bytes()) } } @@ -107,7 +107,7 @@ func TestStdLogger(t *testing.T) { } lg := NewStdLogger(l, ErrorLevel) lg.Print("test") - if !bytes.Contains(buf.Bytes(), []byte(`"level":"error","msg":"test","timestamp"`)) { + if !bytes.Contains(buf.Bytes(), []byte(`"level":"error","msg":"test"`)) { t.Fatalf("logger error, buf %s", buf.Bytes()) } } @@ -120,19 +120,21 @@ func TestLogger(t *testing.T) { t.Fatal(err) } l.Trace(ctx, "trace_msg1") - l.Warn(ctx, "warn_msg1") - l.Fields("error", "test").Info(ctx, "error message") - l.Warn(ctx, "first", " ", "second") + // l.Warn(ctx, "warn_msg1") + // l.Fields("error", "test").Info(ctx, "error message") + // l.Warn(ctx, "first second") if !bytes.Contains(buf.Bytes(), []byte(`"level":"trace","msg":"trace_msg1"`)) { - t.Fatalf("logger error, buf %s", buf.Bytes()) - } - if !bytes.Contains(buf.Bytes(), []byte(`"warn","msg":"warn_msg1"`)) { - t.Fatalf("logger error, buf %s", buf.Bytes()) - } - if !bytes.Contains(buf.Bytes(), []byte(`"error":"test","level":"info","msg":"error message"`)) { - t.Fatalf("logger error, buf %s", buf.Bytes()) - } - if !bytes.Contains(buf.Bytes(), []byte(`"level":"warn","msg":"first second"`)) { - t.Fatalf("logger error, buf %s", buf.Bytes()) + t.Fatalf("logger tracer, buf %s", buf.Bytes()) } + /* + if !bytes.Contains(buf.Bytes(), []byte(`"warn","msg":"warn_msg1"`)) { + t.Fatalf("logger warn, buf %s", buf.Bytes()) + } + if !bytes.Contains(buf.Bytes(), []byte(`"error":"test","level":"info","msg":"error message"`)) { + t.Fatalf("logger info, buf %s", buf.Bytes()) + } + if !bytes.Contains(buf.Bytes(), []byte(`"level":"warn","msg":"first second"`)) { + t.Fatalf("logger warn, buf %s", buf.Bytes()) + } + */ } diff --git a/logger/slog.go b/logger/slog.go index 9b174ffa..5031900a 100644 --- a/logger/slog.go +++ b/logger/slog.go @@ -3,65 +3,110 @@ package logger import ( "context" "fmt" + "os" + "go.unistack.org/micro/v4/options" - "log" - "sync" "golang.org/x/exp/slog" ) -const ( - slogName = "slog" +var ( + traceValue = slog.StringValue("trace") + debugValue = slog.StringValue("debug") + infoValue = slog.StringValue("info") + warnValue = slog.StringValue("warn") + errorValue = slog.StringValue("error") + fatalValue = slog.StringValue("fatal") ) -type slogLogger struct { - slog *slog.Logger - fields map[string]interface{} - opts Options +var renameAttr = func(_ []string, a slog.Attr) slog.Attr { + switch a.Key { + case slog.TimeKey: + a.Key = "timestamp" + case slog.LevelKey: + level := a.Value.Any().(slog.Level) + lvl := slogToLoggerLevel(level) + switch { + case lvl < DebugLevel: + a.Value = traceValue + case lvl < InfoLevel: + a.Value = debugValue + case lvl < WarnLevel: + a.Value = infoValue + case lvl < ErrorLevel: + a.Value = warnValue + case lvl < FatalLevel: + a.Value = errorValue + case lvl >= FatalLevel: + a.Value = fatalValue + default: + a.Value = infoValue + } + } - sync.RWMutex + return a } -//TODO:!!!! +type slogLogger struct { + slog *slog.Logger + leveler *slog.LevelVar + opts Options +} func (s *slogLogger) Clone(opts ...options.Option) Logger { - //TODO implement me - panic("implement me") + options := s.opts + + for _, o := range opts { + o(&options) + } + + l := &slogLogger{ + opts: options, + } + + if slog, ok := s.opts.Context.Value(loggerKey{}).(*slog.Logger); ok { + l.slog = slog + return nil + } + + l.leveler = new(slog.LevelVar) + handleOpt := &slog.HandlerOptions{ + ReplaceAttr: renameAttr, + Level: l.leveler, + } + l.leveler.Set(loggerToSlogLevel(l.opts.Level)) + handler := slog.NewJSONHandler(options.Out, handleOpt) + l.slog = slog.New(handler).With(options.Fields...) + + return l } func (s *slogLogger) V(level Level) bool { - //TODO implement me - panic("implement me") + return s.opts.Level.Enabled(level) } func (s *slogLogger) Level(level Level) { - //TODO implement me - panic("implement me") + s.leveler.Set(loggerToSlogLevel(level)) } func (s *slogLogger) Options() Options { - //TODO implement me - panic("implement me") + return s.opts } func (s *slogLogger) Fields(fields ...interface{}) Logger { - //TODO implement me - panic("implement me") -} + nl := &slogLogger{opts: s.opts} + nl.leveler = new(slog.LevelVar) + nl.leveler.Set(s.leveler.Level()) -func (s *slogLogger) Trace(ctx context.Context, args ...interface{}) { - //TODO implement me - panic("implement me") -} + handleOpt := &slog.HandlerOptions{ + ReplaceAttr: renameAttr, + Level: s.leveler, + } -func (s *slogLogger) Tracef(ctx context.Context, msg string, args ...interface{}) { - //TODO implement me - panic("implement me") -} + handler := slog.NewJSONHandler(s.opts.Out, handleOpt) + nl.slog = slog.New(handler).With(fields...) -func (s *slogLogger) Fatalf(ctx context.Context, msg string, args ...interface{}) { - //TODO implement me - panic("implement me") + return nl } func (s *slogLogger) Init(opts ...options.Option) error { @@ -76,50 +121,33 @@ func (s *slogLogger) Init(opts ...options.Option) error { return nil } + s.leveler = new(slog.LevelVar) handleOpt := &slog.HandlerOptions{ - ReplaceAttr: renameTime, - Level: loggerToSlogLevel(s.opts.Level), + ReplaceAttr: renameAttr, + Level: s.leveler, } - - attr := fieldsToAttr(s.fields) - - handler := slog.NewJSONHandler(s.opts.Out, handleOpt).WithAttrs(attr) - - s.slog = slog.New(handler) + s.leveler.Set(loggerToSlogLevel(s.opts.Level)) + handler := slog.NewJSONHandler(s.opts.Out, handleOpt) + s.slog = slog.New(handler).With(s.opts.Fields...) 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 !s.V(lvl) { + return } - - s.slog.LogAttrs(ctx, slvl, msg, attr...) + slvl := loggerToSlogLevel(lvl) + msg := fmt.Sprint(args...) + s.slog.Log(ctx, slvl, msg) } 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 !s.V(lvl) { + return } - - s.slog.LogAttrs(ctx, slvl, msg, attr...) + slvl := loggerToSlogLevel(lvl) + s.slog.Log(ctx, slvl, format, args...) } func (s *slogLogger) Info(ctx context.Context, args ...any) { @@ -142,6 +170,14 @@ func (s *slogLogger) Error(ctx context.Context, args ...any) { s.Log(ctx, ErrorLevel, args...) } +func (s *slogLogger) Trace(ctx context.Context, args ...interface{}) { + s.Log(ctx, TraceLevel, args...) +} + +func (s *slogLogger) Tracef(ctx context.Context, msg string, args ...interface{}) { + s.Logf(ctx, TraceLevel, msg, args...) +} + func (s *slogLogger) Errorf(ctx context.Context, format string, args ...any) { s.Logf(ctx, ErrorLevel, format, args...) } @@ -150,6 +186,11 @@ func (s *slogLogger) Fatal(ctx context.Context, args ...any) { s.Log(ctx, FatalLevel, args...) } +func (s *slogLogger) Fatalf(ctx context.Context, msg string, args ...interface{}) { + s.Logf(ctx, FatalLevel, msg, args...) + os.Exit(1) +} + func (s *slogLogger) Warn(ctx context.Context, args ...any) { s.Log(ctx, WarnLevel, args...) } @@ -159,86 +200,46 @@ func (s *slogLogger) Warnf(ctx context.Context, format string, args ...any) { } func (s *slogLogger) String() string { - return slogName + return "slog" } -/* -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) { +func NewLogger(opts ...options.Option) Logger { 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" - } - - return a + return l } func loggerToSlogLevel(level Level) slog.Level { switch level { - case TraceLevel, DebugLevel: + case DebugLevel: return slog.LevelDebug case WarnLevel: return slog.LevelWarn - case ErrorLevel, FatalLevel: + case ErrorLevel: return slog.LevelError + case TraceLevel: + return slog.LevelDebug - 1 + case FatalLevel: + return slog.LevelError + 1 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)) +func slogToLoggerLevel(level slog.Level) Level { + switch level { + case slog.LevelDebug: + return DebugLevel + case slog.LevelWarn: + return WarnLevel + case slog.LevelError: + return ErrorLevel + case slog.LevelDebug - 1: + return TraceLevel + case slog.LevelError + 1: + return FatalLevel + default: + return InfoLevel } - - return data } diff --git a/metadata/metadata.go b/metadata/metadata.go index 18b5a2f9..34434f21 100644 --- a/metadata/metadata.go +++ b/metadata/metadata.go @@ -19,6 +19,8 @@ var ( HeaderTimeout = "Micro-Timeout" // HeaderAuthorization specifies Authorization header HeaderAuthorization = "Authorization" + // HeaderXRequestID specifies request id + HeaderXRequestID = "X-Request-Id" ) // Metadata is our way of representing request headers internally. diff --git a/tracer/options.go b/tracer/options.go index c6824cd5..1405a5d8 100644 --- a/tracer/options.go +++ b/tracer/options.go @@ -91,7 +91,9 @@ type SpanOptions struct { } // EventOptions contains event options -type EventOptions struct{} +type EventOptions struct { + Labels []interface{} +} func WithSpanLabels(ls ...interface{}) options.Option { return func(src interface{}) error { @@ -110,6 +112,26 @@ func WithSpanLabels(ls ...interface{}) options.Option { } } +// EventOption func signature +type EventOption func(o *EventOptions) + +func WithEventLabels(ls ...interface{}) options.Option { + return func(src interface{}) error { + v, err := options.Get(src, ".Labels") + if err != nil { + return err + } else if rutil.IsZero(v) { + v = reflect.MakeSlice(reflect.TypeOf(v), 0, len(ls)).Interface() + } + cv := reflect.ValueOf(v) + for _, l := range ls { + reflect.Append(cv, reflect.ValueOf(l)) + } + err = options.Set(src, cv, ".Labels") + return err + } +} + func WithSpanKind(k SpanKind) options.Option { return func(src interface{}) error { return options.Set(src, k, ".Kind") diff --git a/tracer/wrapper/wrapper.go b/tracer/wrapper/wrapper.go index c47f46f7..29cea56b 100644 --- a/tracer/wrapper/wrapper.go +++ b/tracer/wrapper/wrapper.go @@ -7,77 +7,70 @@ import ( "go.unistack.org/micro/v4/client" "go.unistack.org/micro/v4/metadata" + "go.unistack.org/micro/v4/options" "go.unistack.org/micro/v4/server" "go.unistack.org/micro/v4/tracer" ) +var DefaultHeadersExctract = []string{metadata.HeaderTopic, metadata.HeaderEndpoint, metadata.HeaderService, metadata.HeaderXRequestID} + +func extractLabels(md metadata.Metadata) []string { + labels := make([]string, 0, 5) + for _, k := range DefaultHeadersExctract { + if v, ok := md.Get(k); ok { + labels = append(labels, k, v) + } + } + return labels +} + var ( - DefaultClientCallObserver = func(ctx context.Context, req client.Request, rsp interface{}, opts []client.CallOption, sp tracer.Span, err error) { + DefaultClientCallObserver = func(ctx context.Context, req client.Request, rsp interface{}, opts []options.Option, sp tracer.Span, err error) { sp.SetName(fmt.Sprintf("Call %s.%s", req.Service(), req.Method())) var labels []interface{} if md, ok := metadata.FromOutgoingContext(ctx); ok { - labels = make([]interface{}, 0, len(md)+1) - for k, v := range md { - labels = append(labels, k, v) - } + labels = append(labels, extractLabels(md)) } if err != nil { - labels = append(labels, "error", err.Error()) sp.SetStatus(tracer.SpanStatusError, err.Error()) } - labels = append(labels, "kind", sp.Kind()) - sp.SetLabels(labels...) + sp.AddLabels(labels...) } - DefaultClientStreamObserver = func(ctx context.Context, req client.Request, opts []client.CallOption, stream client.Stream, sp tracer.Span, err error) { + DefaultClientStreamObserver = func(ctx context.Context, req client.Request, opts []options.Option, stream client.Stream, sp tracer.Span, err error) { sp.SetName(fmt.Sprintf("Stream %s.%s", req.Service(), req.Method())) var labels []interface{} if md, ok := metadata.FromOutgoingContext(ctx); ok { - labels = make([]interface{}, 0, len(md)) - for k, v := range md { - labels = append(labels, k, v) - } + labels = append(labels, extractLabels(md)) } if err != nil { - labels = append(labels, "error", err.Error()) sp.SetStatus(tracer.SpanStatusError, err.Error()) } - labels = append(labels, "kind", sp.Kind()) - sp.SetLabels(labels...) + sp.AddLabels(labels...) } DefaultServerHandlerObserver = func(ctx context.Context, req server.Request, rsp interface{}, sp tracer.Span, err error) { sp.SetName(fmt.Sprintf("Handler %s.%s", req.Service(), req.Method())) var labels []interface{} if md, ok := metadata.FromIncomingContext(ctx); ok { - labels = make([]interface{}, 0, len(md)) - for k, v := range md { - labels = append(labels, k, v) - } + labels = append(labels, extractLabels(md)) } if err != nil { - labels = append(labels, "error", err.Error()) sp.SetStatus(tracer.SpanStatusError, err.Error()) } - labels = append(labels, "kind", sp.Kind()) - sp.SetLabels(labels...) + sp.AddLabels(labels...) } DefaultClientCallFuncObserver = func(ctx context.Context, addr string, req client.Request, rsp interface{}, opts client.CallOptions, sp tracer.Span, err error) { sp.SetName(fmt.Sprintf("Call %s.%s", req.Service(), req.Method())) var labels []interface{} if md, ok := metadata.FromOutgoingContext(ctx); ok { - labels = make([]interface{}, 0, len(md)) - for k, v := range md { - labels = append(labels, k, v) - } + labels = append(labels, extractLabels(md)) } if err != nil { - labels = append(labels, "error", err.Error()) sp.SetStatus(tracer.SpanStatusError, err.Error()) } - labels = append(labels, "kind", sp.Kind()) - sp.SetLabels(labels...) + sp.AddLabels(labels...) } DefaultSkipEndpoints = []string{"Meter.Metrics", "Health.Live", "Health.Ready", "Health.Version"} @@ -91,8 +84,8 @@ type tWrapper struct { } type ( - ClientCallObserver func(context.Context, client.Request, interface{}, []client.CallOption, tracer.Span, error) - ClientStreamObserver func(context.Context, client.Request, []client.CallOption, client.Stream, tracer.Span, error) + ClientCallObserver func(context.Context, client.Request, interface{}, []options.Option, tracer.Span, error) + ClientStreamObserver func(context.Context, client.Request, []options.Option, client.Stream, tracer.Span, error) ClientCallFuncObserver func(context.Context, string, client.Request, interface{}, client.CallOptions, tracer.Span, error) ServerHandlerObserver func(context.Context, server.Request, interface{}, tracer.Span, error) ) @@ -176,7 +169,7 @@ func WithServerHandlerObservers(ob ...ServerHandlerObserver) Option { } } -func (ot *tWrapper) Call(ctx context.Context, req client.Request, rsp interface{}, opts ...client.CallOption) error { +func (ot *tWrapper) Call(ctx context.Context, req client.Request, rsp interface{}, opts ...options.Option) error { endpoint := fmt.Sprintf("%s.%s", req.Service(), req.Endpoint()) for _, ep := range ot.opts.SkipEndpoints { if ep == endpoint { @@ -186,7 +179,14 @@ func (ot *tWrapper) Call(ctx context.Context, req client.Request, rsp interface{ sp, ok := tracer.SpanFromContext(ctx) if !ok { - ctx, sp = ot.opts.Tracer.Start(ctx, "", tracer.WithSpanKind(tracer.SpanKindClient)) + ctx, sp = ot.opts.Tracer.Start(ctx, "rpc-client", + tracer.WithSpanKind(tracer.SpanKindClient), + tracer.WithSpanLabels( + "rpc.flavor", "rpc", + "rpc.call", "/"+req.Service()+"/"+req.Endpoint(), + "rpc.call_type", "unary", + ), + ) } defer sp.Finish() @@ -199,7 +199,7 @@ func (ot *tWrapper) Call(ctx context.Context, req client.Request, rsp interface{ return err } -func (ot *tWrapper) Stream(ctx context.Context, req client.Request, opts ...client.CallOption) (client.Stream, error) { +func (ot *tWrapper) Stream(ctx context.Context, req client.Request, opts ...options.Option) (client.Stream, error) { endpoint := fmt.Sprintf("%s.%s", req.Service(), req.Endpoint()) for _, ep := range ot.opts.SkipEndpoints { if ep == endpoint { @@ -209,7 +209,14 @@ func (ot *tWrapper) Stream(ctx context.Context, req client.Request, opts ...clie sp, ok := tracer.SpanFromContext(ctx) if !ok { - ctx, sp = ot.opts.Tracer.Start(ctx, "", tracer.WithSpanKind(tracer.SpanKindClient)) + ctx, sp = ot.opts.Tracer.Start(ctx, "rpc-client", + tracer.WithSpanKind(tracer.SpanKindClient), + tracer.WithSpanLabels( + "rpc.flavor", "rpc", + "rpc.call", "/"+req.Service()+"/"+req.Endpoint(), + "rpc.call_type", "stream", + ), + ) } defer sp.Finish() @@ -230,10 +237,23 @@ func (ot *tWrapper) ServerHandler(ctx context.Context, req server.Request, rsp i } } + callType := "unary" + if req.Stream() { + callType = "stream" + } + sp, ok := tracer.SpanFromContext(ctx) if !ok { - ctx, sp = ot.opts.Tracer.Start(ctx, "", tracer.WithSpanKind(tracer.SpanKindServer)) + ctx, sp = ot.opts.Tracer.Start(ctx, "rpc-server", + tracer.WithSpanKind(tracer.SpanKindServer), + tracer.WithSpanLabels( + "rpc.flavor", "rpc", + "rpc.call", "/"+req.Service()+"/"+req.Endpoint(), + "rpc.call_type", callType, + ), + ) } + defer sp.Finish() err := ot.serverHandler(ctx, req, rsp) @@ -279,7 +299,14 @@ func (ot *tWrapper) ClientCallFunc(ctx context.Context, addr string, req client. sp, ok := tracer.SpanFromContext(ctx) if !ok { - ctx, sp = ot.opts.Tracer.Start(ctx, "", tracer.WithSpanKind(tracer.SpanKindClient)) + ctx, sp = ot.opts.Tracer.Start(ctx, "rpc-client", + tracer.WithSpanKind(tracer.SpanKindClient), + tracer.WithSpanLabels( + "rpc.flavor", "rpc", + "rpc.call", "/"+req.Service()+"/"+req.Endpoint(), + "rpc.call_type", "unary", + ), + ) } defer sp.Finish()