Compare commits
45 Commits
Author | SHA1 | Date | |
---|---|---|---|
7b9a4924ad | |||
40939e56a3 | |||
0df97183f8 | |||
08c40ff7d5 | |||
afc6f88852 | |||
dbcc2bc262 | |||
ccffa5cd72 | |||
|
3612f639d6 | ||
32d0b13f91 | |||
4ad61003b9 | |||
7647a0d3b3 | |||
c16961e852 | |||
968feb931c | |||
911a863313 | |||
fcaf323b59 | |||
52fecd1772 | |||
d5f35fe88a | |||
1952053c1b | |||
75b45d7313 | |||
|
eec350c897 | ||
|
360feb0f41 | ||
e1975ab66a | |||
2504345815 | |||
9a95fda93f | |||
7ee309a4ae | |||
a47b6b92c4 | |||
8d46a6498d | |||
65a1612b51 | |||
26783b341b | |||
ec21d4f307 | |||
a3ca5ebb97 | |||
f67ee26042 | |||
6b7bba709e | |||
8b082b90df | |||
b499fe99ca | |||
178e6e956d | |||
a52db1969b | |||
27bf1c3d84 | |||
58b687f134 | |||
8a354bd468 | |||
f600195ee2 | |||
fd471a89e2 | |||
c346ac43dd | |||
666176138b | |||
884a96ad32 |
3
.gitea/pkgdashcli.yaml
Normal file
3
.gitea/pkgdashcli.yaml
Normal file
@@ -0,0 +1,3 @@
|
||||
branches:
|
||||
- master
|
||||
- v3
|
24
.gitea/workflows/autoupdate.yml
Normal file
24
.gitea/workflows/autoupdate.yml
Normal file
@@ -0,0 +1,24 @@
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- 'main'
|
||||
- 'master'
|
||||
- 'v3'
|
||||
schedule:
|
||||
- cron: '* * * * *'
|
||||
#- cron: '@hourly'
|
||||
|
||||
jobs:
|
||||
autoupdate:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: setup-go
|
||||
uses: https://gitea.com/actions/setup-go@v3
|
||||
with:
|
||||
go-version: 1.21
|
||||
- name: checkout
|
||||
uses: https://gitea.com/actions/checkout@v3
|
||||
- name: get pkgdashcli
|
||||
run: GOPROXY=direct GONOSUMDB="git.unistack.org/*" GONOPROXY="git.unistack.org/*" GOBIN=/bin go install git.unistack.org/unistack-org/pkgdash/cmd/pkgdashcli@latest
|
||||
- name: pkgdashcli check
|
||||
run: /bin/pkgdashcli check
|
@@ -12,7 +12,7 @@ jobs:
|
||||
- name: setup-go
|
||||
uses: https://gitea.com/actions/setup-go@v3
|
||||
with:
|
||||
go-version: 1.18
|
||||
go-version: 1.21
|
||||
- name: checkout
|
||||
uses: https://gitea.com/actions/checkout@v3
|
||||
- name: deps
|
||||
|
@@ -14,7 +14,7 @@ jobs:
|
||||
- name: setup-go
|
||||
uses: https://gitea.com/actions/setup-go@v3
|
||||
with:
|
||||
go-version: 1.18
|
||||
go-version: 1.21
|
||||
- name: deps
|
||||
run: go get -v -t -d ./...
|
||||
- name: test
|
||||
|
@@ -8,7 +8,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/imdario/mergo"
|
||||
"dario.cat/mergo"
|
||||
"go.unistack.org/micro/v4/options"
|
||||
mid "go.unistack.org/micro/v4/util/id"
|
||||
rutil "go.unistack.org/micro/v4/util/reflect"
|
||||
|
@@ -63,9 +63,9 @@ func NewOptions(opts ...options.Option) Options {
|
||||
// LoadOptions struct
|
||||
type LoadOptions struct {
|
||||
Struct interface{}
|
||||
Context context.Context
|
||||
Override bool
|
||||
Append bool
|
||||
Context context.Context
|
||||
}
|
||||
|
||||
// NewLoadOptions create LoadOptions struct with provided opts
|
||||
|
13
go.mod
13
go.mod
@@ -3,19 +3,20 @@ module go.unistack.org/micro/v4
|
||||
go 1.20
|
||||
|
||||
require (
|
||||
dario.cat/mergo v1.0.0
|
||||
github.com/DATA-DOG/go-sqlmock v1.5.0
|
||||
github.com/google/uuid v1.3.0
|
||||
github.com/imdario/mergo v0.3.15
|
||||
github.com/google/uuid v1.3.1
|
||||
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.3.0
|
||||
golang.org/x/sys v0.11.0
|
||||
google.golang.org/grpc v1.57.0
|
||||
golang.org/x/sys v0.12.0
|
||||
google.golang.org/grpc v1.58.2
|
||||
google.golang.org/protobuf v1.31.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/golang/protobuf v1.5.3 // indirect
|
||||
golang.org/x/net v0.14.0 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20230526203410-71b5a4ffd15e // indirect
|
||||
golang.org/x/net v0.15.0 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98 // indirect
|
||||
)
|
||||
|
28
go.sum
28
go.sum
@@ -1,3 +1,5 @@
|
||||
dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk=
|
||||
dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
|
||||
github.com/DATA-DOG/go-sqlmock v1.5.0 h1:Shsta01QNfFxHCfpW6YH2STWB0MudeXXEWMr20OEh60=
|
||||
github.com/DATA-DOG/go-sqlmock v1.5.0/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
@@ -5,26 +7,26 @@ github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg
|
||||
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
||||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/imdario/mergo v0.3.15 h1:M8XP7IuFNsqUx6VPK2P9OSmsYsI/YFaGil0uD21V3dM=
|
||||
github.com/imdario/mergo v0.3.15/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY=
|
||||
github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4=
|
||||
github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
|
||||
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/net v0.14.0 h1:BONx9s002vGdD9umnlX1Po8vOZmrgH34qlHcD1MfK14=
|
||||
golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI=
|
||||
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.15.0 h1:ugBLEUaxABaB5AJqW9enI0ACdci2RUd4eP51NTBvuJ8=
|
||||
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
|
||||
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.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM=
|
||||
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/text v0.12.0 h1:k+n5B8goJNdU7hSvEtMUz3d1Q6D/XW4COJSJR6fN0mc=
|
||||
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.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20230526203410-71b5a4ffd15e h1:NumxXLPfHSndr3wBBdeKiVHjGVFzi9RX2HwwQke94iY=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20230526203410-71b5a4ffd15e/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/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98 h1:bVf09lpb+OJbByTj913DRJioFFAjf/ZGxEz7MajTp2U=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98/go.mod h1:TUfxEVdsvPg18p6AslUXFoLdpED4oBnGwyqk3dV1XzM=
|
||||
google.golang.org/grpc v1.58.2 h1:SXUpjxeVF3FKrTYQI4f4KvbGD5u2xccdYdurwowix5I=
|
||||
google.golang.org/grpc v1.58.2/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSsCJk0=
|
||||
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.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
|
||||
|
@@ -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
|
||||
}
|
@@ -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())
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
245
logger/slog.go
Normal file
245
logger/slog.go
Normal file
@@ -0,0 +1,245 @@
|
||||
package logger
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"go.unistack.org/micro/v4/options"
|
||||
|
||||
"golang.org/x/exp/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")
|
||||
)
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
return a
|
||||
}
|
||||
|
||||
type slogLogger struct {
|
||||
slog *slog.Logger
|
||||
leveler *slog.LevelVar
|
||||
opts Options
|
||||
}
|
||||
|
||||
func (s *slogLogger) Clone(opts ...options.Option) Logger {
|
||||
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 {
|
||||
return s.opts.Level.Enabled(level)
|
||||
}
|
||||
|
||||
func (s *slogLogger) Level(level Level) {
|
||||
s.leveler.Set(loggerToSlogLevel(level))
|
||||
}
|
||||
|
||||
func (s *slogLogger) Options() Options {
|
||||
return s.opts
|
||||
}
|
||||
|
||||
func (s *slogLogger) Fields(fields ...interface{}) Logger {
|
||||
nl := &slogLogger{opts: s.opts}
|
||||
nl.leveler = new(slog.LevelVar)
|
||||
nl.leveler.Set(s.leveler.Level())
|
||||
|
||||
handleOpt := &slog.HandlerOptions{
|
||||
ReplaceAttr: renameAttr,
|
||||
Level: s.leveler,
|
||||
}
|
||||
|
||||
handler := slog.NewJSONHandler(s.opts.Out, handleOpt)
|
||||
nl.slog = slog.New(handler).With(fields...)
|
||||
|
||||
return nl
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
s.leveler = new(slog.LevelVar)
|
||||
handleOpt := &slog.HandlerOptions{
|
||||
ReplaceAttr: renameAttr,
|
||||
Level: s.leveler,
|
||||
}
|
||||
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) {
|
||||
if !s.V(lvl) {
|
||||
return
|
||||
}
|
||||
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) {
|
||||
if !s.V(lvl) {
|
||||
return
|
||||
}
|
||||
slvl := loggerToSlogLevel(lvl)
|
||||
s.slog.Log(ctx, slvl, format, args...)
|
||||
}
|
||||
|
||||
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) 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...)
|
||||
}
|
||||
|
||||
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...)
|
||||
}
|
||||
|
||||
func (s *slogLogger) Warnf(ctx context.Context, format string, args ...any) {
|
||||
s.Logf(ctx, WarnLevel, format, args...)
|
||||
}
|
||||
|
||||
func (s *slogLogger) String() string {
|
||||
return "slog"
|
||||
}
|
||||
|
||||
func NewLogger(opts ...options.Option) Logger {
|
||||
l := &slogLogger{
|
||||
opts: NewOptions(opts...),
|
||||
}
|
||||
return l
|
||||
}
|
||||
|
||||
func loggerToSlogLevel(level Level) slog.Level {
|
||||
switch level {
|
||||
case DebugLevel:
|
||||
return slog.LevelDebug
|
||||
case WarnLevel:
|
||||
return slog.LevelWarn
|
||||
case ErrorLevel:
|
||||
return slog.LevelError
|
||||
case TraceLevel:
|
||||
return slog.LevelDebug - 1
|
||||
case FatalLevel:
|
||||
return slog.LevelError + 1
|
||||
default:
|
||||
return slog.LevelInfo
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
@@ -7,12 +7,13 @@ import (
|
||||
|
||||
"go.unistack.org/micro/v4/client"
|
||||
"go.unistack.org/micro/v4/logger"
|
||||
"go.unistack.org/micro/v4/options"
|
||||
"go.unistack.org/micro/v4/server"
|
||||
)
|
||||
|
||||
var (
|
||||
// DefaultClientCallObserver called by wrapper in client Call
|
||||
DefaultClientCallObserver = func(ctx context.Context, req client.Request, rsp interface{}, opts []client.CallOption, err error) []string {
|
||||
DefaultClientCallObserver = func(ctx context.Context, req client.Request, rsp interface{}, opts []options.Option, err error) []string {
|
||||
labels := []string{"service", req.Service(), "endpoint", req.Endpoint()}
|
||||
if err != nil {
|
||||
labels = append(labels, "error", err.Error())
|
||||
@@ -21,7 +22,7 @@ var (
|
||||
}
|
||||
|
||||
// DefaultClientStreamObserver called by wrapper in client Stream
|
||||
DefaultClientStreamObserver = func(ctx context.Context, req client.Request, opts []client.CallOption, stream client.Stream, err error) []string {
|
||||
DefaultClientStreamObserver = func(ctx context.Context, req client.Request, opts []options.Option, stream client.Stream, err error) []string {
|
||||
labels := []string{"service", req.Service(), "endpoint", req.Endpoint()}
|
||||
if err != nil {
|
||||
labels = append(labels, "error", err.Error())
|
||||
@@ -60,9 +61,9 @@ type lWrapper struct {
|
||||
|
||||
type (
|
||||
// ClientCallObserver func signature
|
||||
ClientCallObserver func(context.Context, client.Request, interface{}, []client.CallOption, error) []string
|
||||
ClientCallObserver func(context.Context, client.Request, interface{}, []options.Option, error) []string
|
||||
// ClientStreamObserver func signature
|
||||
ClientStreamObserver func(context.Context, client.Request, []client.CallOption, client.Stream, error) []string
|
||||
ClientStreamObserver func(context.Context, client.Request, []options.Option, client.Stream, error) []string
|
||||
// ClientCallFuncObserver func signature
|
||||
ClientCallFuncObserver func(context.Context, string, client.Request, interface{}, client.CallOptions, error) []string
|
||||
// ServerHandlerObserver func signature
|
||||
@@ -167,7 +168,7 @@ func SkipEndpoints(eps ...string) Option {
|
||||
}
|
||||
}
|
||||
|
||||
func (l *lWrapper) Call(ctx context.Context, req client.Request, rsp interface{}, opts ...client.CallOption) error {
|
||||
func (l *lWrapper) Call(ctx context.Context, req client.Request, rsp interface{}, opts ...options.Option) error {
|
||||
err := l.Client.Call(ctx, req, rsp, opts...)
|
||||
|
||||
endpoint := fmt.Sprintf("%s.%s", req.Service(), req.Endpoint())
|
||||
@@ -190,7 +191,7 @@ func (l *lWrapper) Call(ctx context.Context, req client.Request, rsp interface{}
|
||||
return err
|
||||
}
|
||||
|
||||
func (l *lWrapper) Stream(ctx context.Context, req client.Request, opts ...client.CallOption) (client.Stream, error) {
|
||||
func (l *lWrapper) Stream(ctx context.Context, req client.Request, opts ...options.Option) (client.Stream, error) {
|
||||
stream, err := l.Client.Stream(ctx, req, opts...)
|
||||
|
||||
endpoint := fmt.Sprintf("%s.%s", req.Service(), req.Endpoint())
|
||||
|
@@ -1,4 +1,4 @@
|
||||
package wrapper // import "go.unistack.org/micro/v4/meter/wrapper"
|
||||
package wrapper
|
||||
|
||||
import (
|
||||
"context"
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
|
||||
"go.unistack.org/micro/v4/client"
|
||||
"go.unistack.org/micro/v4/meter"
|
||||
"go.unistack.org/micro/v4/options"
|
||||
"go.unistack.org/micro/v4/semconv"
|
||||
"go.unistack.org/micro/v4/server"
|
||||
)
|
||||
@@ -24,7 +25,7 @@ var (
|
||||
// Options struct
|
||||
type Options struct {
|
||||
Meter meter.Meter
|
||||
lopts []meter.Option
|
||||
lopts []options.Option
|
||||
SkipEndpoints []string
|
||||
}
|
||||
|
||||
@@ -35,7 +36,7 @@ type Option func(*Options)
|
||||
func NewOptions(opts ...Option) Options {
|
||||
options := Options{
|
||||
Meter: meter.DefaultMeter,
|
||||
lopts: make([]meter.Option, 0, 5),
|
||||
lopts: make([]options.Option, 0, 5),
|
||||
SkipEndpoints: DefaultSkipEndpoints,
|
||||
}
|
||||
for _, o := range opts {
|
||||
@@ -137,7 +138,7 @@ func (w *wrapper) CallFunc(ctx context.Context, addr string, req client.Request,
|
||||
return err
|
||||
}
|
||||
|
||||
func (w *wrapper) Call(ctx context.Context, req client.Request, rsp interface{}, opts ...client.CallOption) error {
|
||||
func (w *wrapper) 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 w.opts.SkipEndpoints {
|
||||
if ep == endpoint {
|
||||
@@ -167,7 +168,7 @@ func (w *wrapper) Call(ctx context.Context, req client.Request, rsp interface{},
|
||||
return err
|
||||
}
|
||||
|
||||
func (w *wrapper) Stream(ctx context.Context, req client.Request, opts ...client.CallOption) (client.Stream, error) {
|
||||
func (w *wrapper) 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 w.opts.SkipEndpoints {
|
||||
if ep == endpoint {
|
||||
|
@@ -5,12 +5,13 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"go.unistack.org/micro/v4/options"
|
||||
"go.unistack.org/micro/v4/store"
|
||||
)
|
||||
|
||||
func TestMemoryReInit(t *testing.T) {
|
||||
s := store.NewStore(store.Namespace("aaa"))
|
||||
if err := s.Init(store.Namespace("")); err != nil {
|
||||
s := store.NewStore(options.Namespace("aaa"))
|
||||
if err := s.Init(options.Namespace("")); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(s.Options().Namespace) > 0 {
|
||||
@@ -28,7 +29,7 @@ func TestMemoryBasic(t *testing.T) {
|
||||
|
||||
func TestMemoryPrefix(t *testing.T) {
|
||||
s := store.NewStore()
|
||||
if err := s.Init(store.Namespace("some-prefix")); err != nil {
|
||||
if err := s.Init(options.Namespace("some-prefix")); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
basictest(s, t)
|
||||
@@ -36,7 +37,7 @@ func TestMemoryPrefix(t *testing.T) {
|
||||
|
||||
func TestMemoryNamespace(t *testing.T) {
|
||||
s := store.NewStore()
|
||||
if err := s.Init(store.Namespace("some-namespace")); err != nil {
|
||||
if err := s.Init(options.Namespace("some-namespace")); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
basictest(s, t)
|
||||
@@ -44,7 +45,7 @@ func TestMemoryNamespace(t *testing.T) {
|
||||
|
||||
func TestMemoryNamespacePrefix(t *testing.T) {
|
||||
s := store.NewStore()
|
||||
if err := s.Init(store.Namespace("some-namespace")); err != nil {
|
||||
if err := s.Init(options.Namespace("some-namespace")); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
basictest(s, t)
|
||||
|
@@ -2,9 +2,11 @@ package tracer
|
||||
|
||||
import (
|
||||
"context"
|
||||
"reflect"
|
||||
|
||||
"go.unistack.org/micro/v4/logger"
|
||||
"go.unistack.org/micro/v4/options"
|
||||
rutil "go.unistack.org/micro/v4/util/reflect"
|
||||
)
|
||||
|
||||
type SpanStatus int
|
||||
@@ -93,6 +95,43 @@ type EventOptions struct {
|
||||
Labels []interface{}
|
||||
}
|
||||
|
||||
func WithSpanLabels(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
|
||||
}
|
||||
}
|
||||
|
||||
// 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")
|
||||
|
@@ -13,7 +13,7 @@ import (
|
||||
"go.unistack.org/micro/v4/tracer"
|
||||
)
|
||||
|
||||
var DefaultHeadersExctract = []string{metadata.HeaderXRequestID}
|
||||
var DefaultHeadersExctract = []string{metadata.HeaderTopic, metadata.HeaderEndpoint, metadata.HeaderService, metadata.HeaderXRequestID}
|
||||
|
||||
func ExtractDefaultLabels(md metadata.Metadata) []interface{} {
|
||||
labels := make([]interface{}, 0, len(DefaultHeadersExctract))
|
||||
@@ -27,6 +27,7 @@ func ExtractDefaultLabels(md metadata.Metadata) []interface{} {
|
||||
|
||||
var (
|
||||
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 = append(labels, ExtractDefaultLabels(md)...)
|
||||
@@ -177,7 +178,7 @@ func (ot *tWrapper) Call(ctx context.Context, req client.Request, rsp interface{
|
||||
|
||||
nctx, sp := ot.opts.Tracer.Start(ctx, fmt.Sprintf("%s.%s rpc-client", req.Service(), req.Method()),
|
||||
tracer.WithSpanKind(tracer.SpanKindClient),
|
||||
tracer.WithSpanLabels(
|
||||
options.Labels(
|
||||
"rpc.service", req.Service(),
|
||||
"rpc.method", req.Method(),
|
||||
"rpc.flavor", "rpc",
|
||||
@@ -206,7 +207,7 @@ func (ot *tWrapper) Stream(ctx context.Context, req client.Request, opts ...opti
|
||||
|
||||
nctx, sp := ot.opts.Tracer.Start(ctx, fmt.Sprintf("%s.%s rpc-client", req.Service(), req.Method()),
|
||||
tracer.WithSpanKind(tracer.SpanKindClient),
|
||||
tracer.WithSpanLabels(
|
||||
options.Labels(
|
||||
"rpc.service", req.Service(),
|
||||
"rpc.method", req.Method(),
|
||||
"rpc.flavor", "rpc",
|
||||
@@ -240,7 +241,7 @@ func (ot *tWrapper) ServerHandler(ctx context.Context, req server.Request, rsp i
|
||||
|
||||
nctx, sp := ot.opts.Tracer.Start(ctx, fmt.Sprintf("%s.%s rpc-server", req.Service(), req.Method()),
|
||||
tracer.WithSpanKind(tracer.SpanKindServer),
|
||||
tracer.WithSpanLabels(
|
||||
options.Labels(
|
||||
"rpc.service", req.Service(),
|
||||
"rpc.method", req.Method(),
|
||||
"rpc.flavor", "rpc",
|
||||
@@ -293,7 +294,7 @@ func (ot *tWrapper) ClientCallFunc(ctx context.Context, addr string, req client.
|
||||
|
||||
nctx, sp := ot.opts.Tracer.Start(ctx, fmt.Sprintf("%s.%s rpc-client", req.Service(), req.Method()),
|
||||
tracer.WithSpanKind(tracer.SpanKindClient),
|
||||
tracer.WithSpanLabels(
|
||||
options.Labels(
|
||||
"rpc.service", req.Service(),
|
||||
"rpc.method", req.Method(),
|
||||
"rpc.flavor", "rpc",
|
||||
|
@@ -508,3 +508,74 @@ func FieldName(name string) string {
|
||||
|
||||
return string(newstr)
|
||||
}
|
||||
|
||||
func Equal(src interface{}, dst interface{}, excptFields ...string) bool {
|
||||
srcVal := reflect.ValueOf(src)
|
||||
dstVal := reflect.ValueOf(dst)
|
||||
|
||||
switch srcVal.Kind() {
|
||||
case reflect.Array, reflect.Slice:
|
||||
for i := 0; i < srcVal.Len(); i++ {
|
||||
e := srcVal.Index(i).Interface()
|
||||
a := dstVal.Index(i).Interface()
|
||||
if !Equal(e, a, excptFields...) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
case reflect.Map:
|
||||
for i := 0; i < len(srcVal.MapKeys()); i++ {
|
||||
key := srcVal.MapKeys()[i]
|
||||
keyStr := fmt.Sprintf("%v", key.Interface())
|
||||
if stringContains(keyStr, excptFields) {
|
||||
continue
|
||||
}
|
||||
s := srcVal.MapIndex(key)
|
||||
d := dstVal.MapIndex(key)
|
||||
if !Equal(s.Interface(), d.Interface(), excptFields...) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
case reflect.Struct, reflect.Interface:
|
||||
for i := 0; i < srcVal.NumField(); i++ {
|
||||
typeField := srcVal.Type().Field(i)
|
||||
if stringContains(typeField.Name, excptFields) {
|
||||
continue
|
||||
}
|
||||
s := srcVal.Field(i)
|
||||
d := dstVal.FieldByName(typeField.Name)
|
||||
if s.CanInterface() && d.CanInterface() {
|
||||
if !Equal(s.Interface(), d.Interface(), excptFields...) {
|
||||
return false
|
||||
}
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
case reflect.Ptr:
|
||||
if srcVal.IsNil() {
|
||||
return dstVal.IsNil()
|
||||
}
|
||||
s := srcVal.Elem()
|
||||
d := reflect.Indirect(dstVal)
|
||||
if s.CanInterface() && d.CanInterface() {
|
||||
return Equal(s.Interface(), d.Interface(), excptFields...)
|
||||
}
|
||||
return false
|
||||
case reflect.String, reflect.Int, reflect.Int64, reflect.Float32, reflect.Float64, reflect.Bool:
|
||||
return src == dst
|
||||
default:
|
||||
return srcVal.Interface() == dstVal.Interface()
|
||||
}
|
||||
}
|
||||
|
||||
func stringContains(a string, list []string) bool {
|
||||
for _, b := range list {
|
||||
if b == a {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
@@ -133,3 +133,16 @@ func TestMergeNested(t *testing.T) {
|
||||
t.Fatalf("merge error: %#+v", dst.Nested)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEqual(t *testing.T) {
|
||||
type tstr struct {
|
||||
Key1 string
|
||||
Key2 string
|
||||
}
|
||||
|
||||
src := &tstr{Key1: "val1", Key2: "micro:generate"}
|
||||
dst := &tstr{Key1: "val1", Key2: "val2"}
|
||||
if !Equal(src, dst, "Key2") {
|
||||
t.Fatal("invalid Equal test")
|
||||
}
|
||||
}
|
||||
|
@@ -20,6 +20,7 @@ import (
|
||||
"go.unistack.org/micro/v4/codec"
|
||||
"go.unistack.org/micro/v4/errors"
|
||||
"go.unistack.org/micro/v4/metadata"
|
||||
"go.unistack.org/micro/v4/options"
|
||||
"golang.org/x/sync/errgroup"
|
||||
"google.golang.org/grpc/status"
|
||||
"google.golang.org/protobuf/proto"
|
||||
@@ -226,7 +227,7 @@ func NewRequestFromFile(c client.Client, reqfile string) (client.Request, error)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
req := c.NewRequest("test", endpoint, &codec.Frame{Data: reqbuf}, client.RequestContentType(ct))
|
||||
req := c.NewRequest("test", endpoint, &codec.Frame{Data: reqbuf}, options.ContentType(ct))
|
||||
|
||||
return req, nil
|
||||
}
|
||||
@@ -373,7 +374,7 @@ func Run(ctx context.Context, c client.Client, m sqlmock.Sqlmock, dir string, ex
|
||||
data := &codec.Frame{}
|
||||
md := metadata.New(1)
|
||||
md.Set("X-Request-Id", xrid)
|
||||
cerr := c.Call(metadata.NewOutgoingContext(gctx, md), treq, data, client.WithContentType(treq.ContentType()))
|
||||
cerr := c.Call(metadata.NewOutgoingContext(gctx, md), treq, data, options.ContentType(treq.ContentType()))
|
||||
|
||||
var rspfile string
|
||||
|
||||
|
Reference in New Issue
Block a user