Compare commits
35 Commits
9d87bdbc80
...
v4.0.15
Author | SHA1 | Date | |
---|---|---|---|
bb0c415a77 | |||
8182cb008a | |||
0771fa0647 | |||
b1fd82adf8 | |||
4bc0e25017 | |||
aec552aa0b | |||
cc81bed81d | |||
7fde39fba5 | |||
79438f11e0 | |||
8d19abfebd | |||
77f3731329 | |||
dbcf6bb74a | |||
17698440ed | |||
eb8851ab58 | |||
69c6d78920 | |||
7568779db4 | |||
5c3fa31ad2 | |||
7b9a4924ad | |||
40939e56a3 | |||
0df97183f8 | |||
08c40ff7d5 | |||
afc6f88852 | |||
dbcc2bc262 | |||
ccffa5cd72 | |||
|
3612f639d6 | ||
32d0b13f91 | |||
4ad61003b9 | |||
7647a0d3b3 | |||
c16961e852 | |||
968feb931c | |||
911a863313 | |||
fcaf323b59 | |||
f600195ee2 | |||
fd471a89e2 | |||
c346ac43dd |
3
.gitea/pkgdashcli.yaml
Normal file
3
.gitea/pkgdashcli.yaml
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
branches:
|
||||||
|
- master
|
||||||
|
- v3
|
@@ -1,4 +1,9 @@
|
|||||||
on:
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- 'main'
|
||||||
|
- 'master'
|
||||||
|
- 'v3'
|
||||||
schedule:
|
schedule:
|
||||||
- cron: '* * * * *'
|
- cron: '* * * * *'
|
||||||
#- cron: '@hourly'
|
#- cron: '@hourly'
|
||||||
|
@@ -190,7 +190,7 @@ func (m *MemoryBroker) Publish(ctx context.Context, message interface{}, opts ..
|
|||||||
} else {
|
} else {
|
||||||
if sub.opts.AutoAck {
|
if sub.opts.AutoAck {
|
||||||
if err = p.Ack(); err != nil {
|
if err = p.Ack(); err != nil {
|
||||||
m.opts.Logger.Errorf(m.opts.Context, "ack failed: %v", err)
|
m.opts.Logger.Error(m.opts.Context, "ack failed: "+err.Error())
|
||||||
m.opts.Meter.Counter(semconv.SubscribeMessageTotal, "endpoint", t, "status", "failure").Inc()
|
m.opts.Meter.Counter(semconv.SubscribeMessageTotal, "endpoint", t, "status", "failure").Inc()
|
||||||
} else {
|
} else {
|
||||||
m.opts.Meter.Counter(semconv.SubscribeMessageTotal, "endpoint", t, "status", "success").Inc()
|
m.opts.Meter.Counter(semconv.SubscribeMessageTotal, "endpoint", t, "status", "success").Inc()
|
||||||
|
@@ -133,7 +133,7 @@ var (
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if err := fn(ctx, c); err != nil {
|
if err := fn(ctx, c); err != nil {
|
||||||
c.Options().Logger.Errorf(ctx, "%s BeforeLoad err: %v", c.String(), err)
|
c.Options().Logger.Error(ctx, c.String()+" BeforeLoad error "+err.Error())
|
||||||
if !c.Options().AllowFail {
|
if !c.Options().AllowFail {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -148,7 +148,7 @@ var (
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if err := fn(ctx, c); err != nil {
|
if err := fn(ctx, c); err != nil {
|
||||||
c.Options().Logger.Errorf(ctx, "%s AfterLoad err: %v", c.String(), err)
|
c.Options().Logger.Error(ctx, c.String()+" AfterLoad error "+err.Error())
|
||||||
if !c.Options().AllowFail {
|
if !c.Options().AllowFail {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -163,7 +163,7 @@ var (
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if err := fn(ctx, c); err != nil {
|
if err := fn(ctx, c); err != nil {
|
||||||
c.Options().Logger.Errorf(ctx, "%s BeforeSave err: %v", c.String(), err)
|
c.Options().Logger.Error(ctx, c.String()+" BeforeSave error "+err.Error())
|
||||||
if !c.Options().AllowFail {
|
if !c.Options().AllowFail {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -178,7 +178,7 @@ var (
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if err := fn(ctx, c); err != nil {
|
if err := fn(ctx, c); err != nil {
|
||||||
c.Options().Logger.Errorf(ctx, "%s AfterSave err: %v", c.String(), err)
|
c.Options().Logger.Error(ctx, c.String()+" AfterSave error "+err.Error())
|
||||||
if !c.Options().AllowFail {
|
if !c.Options().AllowFail {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -193,7 +193,7 @@ var (
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if err := fn(ctx, c); err != nil {
|
if err := fn(ctx, c); err != nil {
|
||||||
c.Options().Logger.Errorf(ctx, "%s BeforeInit err: %v", c.String(), err)
|
c.Options().Logger.Error(ctx, c.String()+" BeforeInit error "+err.Error())
|
||||||
if !c.Options().AllowFail {
|
if !c.Options().AllowFail {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -208,7 +208,7 @@ var (
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if err := fn(ctx, c); err != nil {
|
if err := fn(ctx, c); err != nil {
|
||||||
c.Options().Logger.Errorf(ctx, "%s AfterInit err: %v", c.String(), err)
|
c.Options().Logger.Error(ctx, c.String()+" AfterInit error "+err.Error())
|
||||||
if !c.Options().AllowFail {
|
if !c.Options().AllowFail {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@@ -7,8 +7,8 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/google/uuid"
|
|
||||||
"dario.cat/mergo"
|
"dario.cat/mergo"
|
||||||
|
"github.com/google/uuid"
|
||||||
"go.unistack.org/micro/v4/options"
|
"go.unistack.org/micro/v4/options"
|
||||||
mid "go.unistack.org/micro/v4/util/id"
|
mid "go.unistack.org/micro/v4/util/id"
|
||||||
rutil "go.unistack.org/micro/v4/util/reflect"
|
rutil "go.unistack.org/micro/v4/util/reflect"
|
||||||
@@ -40,6 +40,10 @@ func (c *defaultConfig) Init(opts ...options.Option) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *defaultConfig) Load(ctx context.Context, opts ...options.Option) error {
|
func (c *defaultConfig) Load(ctx context.Context, opts ...options.Option) error {
|
||||||
|
if c.opts.SkipLoad != nil && c.opts.SkipLoad(ctx, c) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
if err := DefaultBeforeLoad(ctx, c); err != nil && !c.opts.AllowFail {
|
if err := DefaultBeforeLoad(ctx, c); err != nil && !c.opts.AllowFail {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -292,7 +296,11 @@ func fillValues(valueOf reflect.Value, tname string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *defaultConfig) Save(ctx context.Context, opts ...options.Option) error {
|
func (c *defaultConfig) Save(ctx context.Context, _ ...options.Option) error {
|
||||||
|
if c.opts.SkipSave != nil && c.opts.SkipSave(ctx, c) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
if err := DefaultBeforeSave(ctx, c); err != nil {
|
if err := DefaultBeforeSave(ctx, c); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@@ -43,6 +43,10 @@ type Options struct {
|
|||||||
AfterInit []func(context.Context, Config) error
|
AfterInit []func(context.Context, Config) error
|
||||||
// AllowFail flag to allow fail in config source
|
// AllowFail flag to allow fail in config source
|
||||||
AllowFail bool
|
AllowFail bool
|
||||||
|
// SkipLoad runs only if condition returns true
|
||||||
|
SkipLoad func(context.Context, Config) bool
|
||||||
|
// SkipSave runs only if condition returns true
|
||||||
|
SkipSave func(context.Context, Config) bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewOptions new options struct with filed values
|
// NewOptions new options struct with filed values
|
||||||
|
157
database/dsn.go
Normal file
157
database/dsn.go
Normal file
@@ -0,0 +1,157 @@
|
|||||||
|
package database
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/tls"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrInvalidDSNAddr = errors.New("invalid dsn addr")
|
||||||
|
ErrInvalidDSNUnescaped = errors.New("dsn must be escaped")
|
||||||
|
ErrInvalidDSNNoSlash = errors.New("dsn must contains slash")
|
||||||
|
)
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
TLSConfig *tls.Config
|
||||||
|
Username string
|
||||||
|
Password string
|
||||||
|
Scheme string
|
||||||
|
Host string
|
||||||
|
Port string
|
||||||
|
Database string
|
||||||
|
Params []string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cfg *Config) FormatDSN() string {
|
||||||
|
var s strings.Builder
|
||||||
|
|
||||||
|
if len(cfg.Scheme) > 0 {
|
||||||
|
s.WriteString(cfg.Scheme + "://")
|
||||||
|
}
|
||||||
|
// [username[:password]@]
|
||||||
|
if len(cfg.Username) > 0 {
|
||||||
|
s.WriteString(cfg.Username)
|
||||||
|
if len(cfg.Password) > 0 {
|
||||||
|
s.WriteByte(':')
|
||||||
|
s.WriteString(url.PathEscape(cfg.Password))
|
||||||
|
}
|
||||||
|
s.WriteByte('@')
|
||||||
|
}
|
||||||
|
|
||||||
|
// [host:port]
|
||||||
|
if len(cfg.Host) > 0 {
|
||||||
|
s.WriteString(cfg.Host)
|
||||||
|
if len(cfg.Port) > 0 {
|
||||||
|
s.WriteByte(':')
|
||||||
|
s.WriteString(cfg.Port)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// /dbname
|
||||||
|
s.WriteByte('/')
|
||||||
|
s.WriteString(url.PathEscape(cfg.Database))
|
||||||
|
|
||||||
|
for i := 0; i < len(cfg.Params); i += 2 {
|
||||||
|
if i == 0 {
|
||||||
|
s.WriteString("?")
|
||||||
|
} else {
|
||||||
|
s.WriteString("&")
|
||||||
|
}
|
||||||
|
s.WriteString(cfg.Params[i])
|
||||||
|
s.WriteString("=")
|
||||||
|
s.WriteString(cfg.Params[i+1])
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParseDSN(dsn string) (*Config, error) {
|
||||||
|
cfg := &Config{}
|
||||||
|
|
||||||
|
// [user[:password]@][net[(addr)]]/dbname[?param1=value1¶mN=valueN]
|
||||||
|
// Find last '/' that goes before dbname
|
||||||
|
foundSlash := false
|
||||||
|
for i := len(dsn) - 1; i >= 0; i-- {
|
||||||
|
if dsn[i] == '/' {
|
||||||
|
foundSlash = true
|
||||||
|
var j, k int
|
||||||
|
|
||||||
|
// left part is empty if i <= 0
|
||||||
|
if i > 0 {
|
||||||
|
// Find the first ':' in dsn
|
||||||
|
for j = i; j >= 0; j-- {
|
||||||
|
if dsn[j] == ':' {
|
||||||
|
cfg.Scheme = dsn[0:j]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// [username[:password]@][host]
|
||||||
|
// Find the last '@' in dsn[:i]
|
||||||
|
for j = i; j >= 0; j-- {
|
||||||
|
if dsn[j] == '@' {
|
||||||
|
// username[:password]
|
||||||
|
// Find the second ':' in dsn[:j]
|
||||||
|
for k = 0; k < j; k++ {
|
||||||
|
if dsn[k] == ':' {
|
||||||
|
if cfg.Scheme == dsn[:k] {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
var err error
|
||||||
|
cfg.Password, err = url.PathUnescape(dsn[k+1 : j])
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cfg.Username = dsn[len(cfg.Scheme)+3 : k]
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for k = j + 1; k < i; k++ {
|
||||||
|
if dsn[k] == ':' {
|
||||||
|
cfg.Host = dsn[j+1 : k]
|
||||||
|
cfg.Port = dsn[k+1 : i]
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// dbname[?param1=value1&...¶mN=valueN]
|
||||||
|
// Find the first '?' in dsn[i+1:]
|
||||||
|
for j = i + 1; j < len(dsn); j++ {
|
||||||
|
if dsn[j] == '?' {
|
||||||
|
parts := strings.Split(dsn[j+1:], "&")
|
||||||
|
cfg.Params = make([]string, 0, len(parts)*2)
|
||||||
|
for _, p := range parts {
|
||||||
|
k, v, found := strings.Cut(p, "=")
|
||||||
|
if !found {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
cfg.Params = append(cfg.Params, k, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var err error
|
||||||
|
dbname := dsn[i+1 : j]
|
||||||
|
if cfg.Database, err = url.PathUnescape(dbname); err != nil {
|
||||||
|
return nil, fmt.Errorf("invalid dbname %q: %w", dbname, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !foundSlash && len(dsn) > 0 {
|
||||||
|
return nil, ErrInvalidDSNNoSlash
|
||||||
|
}
|
||||||
|
|
||||||
|
return cfg, nil
|
||||||
|
}
|
31
database/dsn_test.go
Normal file
31
database/dsn_test.go
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
package database
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/url"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestParseDSN(t *testing.T) {
|
||||||
|
cfg, err := ParseDSN("postgres://username:p@ssword#@host:12345/dbname?key1=val2&key2=val2")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if cfg.Password != "p@ssword#" {
|
||||||
|
t.Fatalf("parsing error")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFormatDSN(t *testing.T) {
|
||||||
|
src := "postgres://username:p@ssword#@host:12345/dbname?key1=val2&key2=val2"
|
||||||
|
cfg, err := ParseDSN(src)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
dst, err := url.PathUnescape(cfg.FormatDSN())
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if src != dst {
|
||||||
|
t.Fatalf("\n%s\n%s", src, dst)
|
||||||
|
}
|
||||||
|
}
|
@@ -190,7 +190,7 @@ func (w *microWorkflow) Execute(ctx context.Context, req *Message, opts ...optio
|
|||||||
steps, err := w.getSteps(options.Start, options.Reverse)
|
steps, err := w.getSteps(options.Start, options.Reverse)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if werr := workflowStore.Write(w.opts.Context, "status", &codec.Frame{Data: []byte(StatusPending.String())}); werr != nil {
|
if werr := workflowStore.Write(w.opts.Context, "status", &codec.Frame{Data: []byte(StatusPending.String())}); werr != nil {
|
||||||
w.opts.Logger.Errorf(w.opts.Context, "store error: %v", werr)
|
w.opts.Logger.Error(w.opts.Context, "store write error", "error", werr.Error())
|
||||||
}
|
}
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
@@ -214,7 +214,7 @@ func (w *microWorkflow) Execute(ctx context.Context, req *Message, opts ...optio
|
|||||||
done := make(chan struct{})
|
done := make(chan struct{})
|
||||||
|
|
||||||
if werr := workflowStore.Write(w.opts.Context, "status", &codec.Frame{Data: []byte(StatusRunning.String())}); werr != nil {
|
if werr := workflowStore.Write(w.opts.Context, "status", &codec.Frame{Data: []byte(StatusRunning.String())}); werr != nil {
|
||||||
w.opts.Logger.Errorf(w.opts.Context, "store error: %v", werr)
|
w.opts.Logger.Error(w.opts.Context, "store write error", "error", werr.Error())
|
||||||
return eid, werr
|
return eid, werr
|
||||||
}
|
}
|
||||||
for idx := range steps {
|
for idx := range steps {
|
||||||
@@ -239,7 +239,7 @@ func (w *microWorkflow) Execute(ctx context.Context, req *Message, opts ...optio
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
if w.opts.Logger.V(logger.TraceLevel) {
|
if w.opts.Logger.V(logger.TraceLevel) {
|
||||||
w.opts.Logger.Tracef(nctx, "will be executed %v", steps[idx][nidx])
|
w.opts.Logger.Trace(nctx, fmt.Sprintf("step will be executed %v", steps[idx][nidx]))
|
||||||
}
|
}
|
||||||
cstep := steps[idx][nidx]
|
cstep := steps[idx][nidx]
|
||||||
// nolint: nestif
|
// nolint: nestif
|
||||||
@@ -259,21 +259,21 @@ func (w *microWorkflow) Execute(ctx context.Context, req *Message, opts ...optio
|
|||||||
if serr != nil {
|
if serr != nil {
|
||||||
step.SetStatus(StatusFailure)
|
step.SetStatus(StatusFailure)
|
||||||
if werr := stepStore.Write(ctx, step.ID()+w.opts.Store.Options().Separator+"rsp", serr); werr != nil && w.opts.Logger.V(logger.ErrorLevel) {
|
if werr := stepStore.Write(ctx, step.ID()+w.opts.Store.Options().Separator+"rsp", serr); werr != nil && w.opts.Logger.V(logger.ErrorLevel) {
|
||||||
w.opts.Logger.Errorf(ctx, "store write error: %v", werr)
|
w.opts.Logger.Error(ctx, "store write error", "error", werr.Error())
|
||||||
}
|
}
|
||||||
if werr := stepStore.Write(ctx, step.ID()+w.opts.Store.Options().Separator+"status", &codec.Frame{Data: []byte(StatusFailure.String())}); werr != nil && w.opts.Logger.V(logger.ErrorLevel) {
|
if werr := stepStore.Write(ctx, step.ID()+w.opts.Store.Options().Separator+"status", &codec.Frame{Data: []byte(StatusFailure.String())}); werr != nil && w.opts.Logger.V(logger.ErrorLevel) {
|
||||||
w.opts.Logger.Errorf(ctx, "store write error: %v", werr)
|
w.opts.Logger.Error(ctx, "store write error", "error", werr.Error())
|
||||||
}
|
}
|
||||||
cherr <- serr
|
cherr <- serr
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if werr := stepStore.Write(ctx, step.ID()+w.opts.Store.Options().Separator+"rsp", rsp); werr != nil {
|
if werr := stepStore.Write(ctx, step.ID()+w.opts.Store.Options().Separator+"rsp", rsp); werr != nil {
|
||||||
w.opts.Logger.Errorf(ctx, "store write error: %v", werr)
|
w.opts.Logger.Error(ctx, "store write error", "error", werr.Error())
|
||||||
cherr <- werr
|
cherr <- werr
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if werr := stepStore.Write(ctx, step.ID()+w.opts.Store.Options().Separator+"status", &codec.Frame{Data: []byte(StatusSuccess.String())}); werr != nil {
|
if werr := stepStore.Write(ctx, step.ID()+w.opts.Store.Options().Separator+"status", &codec.Frame{Data: []byte(StatusSuccess.String())}); werr != nil {
|
||||||
w.opts.Logger.Errorf(ctx, "store write error: %v", werr)
|
w.opts.Logger.Error(ctx, "store write error", "error", werr.Error())
|
||||||
cherr <- werr
|
cherr <- werr
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -292,16 +292,16 @@ func (w *microWorkflow) Execute(ctx context.Context, req *Message, opts ...optio
|
|||||||
if serr != nil {
|
if serr != nil {
|
||||||
cstep.SetStatus(StatusFailure)
|
cstep.SetStatus(StatusFailure)
|
||||||
if werr := stepStore.Write(ctx, cstep.ID()+w.opts.Store.Options().Separator+"rsp", serr); werr != nil && w.opts.Logger.V(logger.ErrorLevel) {
|
if werr := stepStore.Write(ctx, cstep.ID()+w.opts.Store.Options().Separator+"rsp", serr); werr != nil && w.opts.Logger.V(logger.ErrorLevel) {
|
||||||
w.opts.Logger.Errorf(ctx, "store write error: %v", werr)
|
w.opts.Logger.Error(ctx, "store write error", "error", werr.Error())
|
||||||
}
|
}
|
||||||
if werr := stepStore.Write(ctx, cstep.ID()+w.opts.Store.Options().Separator+"status", &codec.Frame{Data: []byte(StatusFailure.String())}); werr != nil && w.opts.Logger.V(logger.ErrorLevel) {
|
if werr := stepStore.Write(ctx, cstep.ID()+w.opts.Store.Options().Separator+"status", &codec.Frame{Data: []byte(StatusFailure.String())}); werr != nil && w.opts.Logger.V(logger.ErrorLevel) {
|
||||||
w.opts.Logger.Errorf(ctx, "store write error: %v", werr)
|
w.opts.Logger.Error(ctx, "store write error", "error", werr.Error())
|
||||||
}
|
}
|
||||||
cherr <- serr
|
cherr <- serr
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if werr := stepStore.Write(ctx, cstep.ID()+w.opts.Store.Options().Separator+"rsp", rsp); werr != nil {
|
if werr := stepStore.Write(ctx, cstep.ID()+w.opts.Store.Options().Separator+"rsp", rsp); werr != nil {
|
||||||
w.opts.Logger.Errorf(ctx, "store write error: %v", werr)
|
w.opts.Logger.Error(ctx, "store write error", "error", werr.Error())
|
||||||
cherr <- werr
|
cherr <- werr
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -319,7 +319,7 @@ func (w *microWorkflow) Execute(ctx context.Context, req *Message, opts ...optio
|
|||||||
return eid, nil
|
return eid, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.Tracef(ctx, "wait for finish or error")
|
w.opts.Logger.Trace(ctx, "wait for finish or error")
|
||||||
select {
|
select {
|
||||||
case <-nctx.Done():
|
case <-nctx.Done():
|
||||||
err = nctx.Err()
|
err = nctx.Err()
|
||||||
@@ -335,15 +335,15 @@ func (w *microWorkflow) Execute(ctx context.Context, req *Message, opts ...optio
|
|||||||
switch {
|
switch {
|
||||||
case nctx.Err() != nil:
|
case nctx.Err() != nil:
|
||||||
if werr := workflowStore.Write(w.opts.Context, "status", &codec.Frame{Data: []byte(StatusAborted.String())}); werr != nil {
|
if werr := workflowStore.Write(w.opts.Context, "status", &codec.Frame{Data: []byte(StatusAborted.String())}); werr != nil {
|
||||||
w.opts.Logger.Errorf(w.opts.Context, "store error: %v", werr)
|
w.opts.Logger.Error(w.opts.Context, "store write error", "error", werr.Error())
|
||||||
}
|
}
|
||||||
case err == nil:
|
case err == nil:
|
||||||
if werr := workflowStore.Write(w.opts.Context, "status", &codec.Frame{Data: []byte(StatusSuccess.String())}); werr != nil {
|
if werr := workflowStore.Write(w.opts.Context, "status", &codec.Frame{Data: []byte(StatusSuccess.String())}); werr != nil {
|
||||||
w.opts.Logger.Errorf(w.opts.Context, "store error: %v", werr)
|
w.opts.Logger.Error(w.opts.Context, "store write error", "error", werr.Error())
|
||||||
}
|
}
|
||||||
case err != nil:
|
case err != nil:
|
||||||
if werr := workflowStore.Write(w.opts.Context, "status", &codec.Frame{Data: []byte(StatusFailure.String())}); werr != nil {
|
if werr := workflowStore.Write(w.opts.Context, "status", &codec.Frame{Data: []byte(StatusFailure.String())}); werr != nil {
|
||||||
w.opts.Logger.Errorf(w.opts.Context, "store error: %v", werr)
|
w.opts.Logger.Error(w.opts.Context, "store write error", "error", werr.Error())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -17,7 +17,7 @@ func TestFSMStart(t *testing.T) {
|
|||||||
|
|
||||||
wrapper := func(next StateFunc) StateFunc {
|
wrapper := func(next StateFunc) StateFunc {
|
||||||
return func(sctx context.Context, s State, opts ...StateOption) (State, error) {
|
return func(sctx context.Context, s State, opts ...StateOption) (State, error) {
|
||||||
sctx = logger.NewContext(sctx, logger.Fields("state", s.Name()))
|
sctx = logger.NewContext(sctx, logger.Attrs("state", s.Name()))
|
||||||
return next(sctx, s, opts...)
|
return next(sctx, s, opts...)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -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
|
|
||||||
}
|
|
116
logger/logger.go
116
logger/logger.go
@@ -1,16 +1,19 @@
|
|||||||
// Package logger provides a log interface
|
// Package logger provides a log interface
|
||||||
package logger // import "go.unistack.org/micro/v4/logger"
|
package logger
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"os"
|
|
||||||
|
|
||||||
"go.unistack.org/micro/v4/options"
|
"go.unistack.org/micro/v4/options"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
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()
|
||||||
// DefaultLevel used by logger
|
// DefaultLevel used by logger
|
||||||
DefaultLevel = InfoLevel
|
DefaultLevel = InfoLevel
|
||||||
// DefaultCallerSkipCount used by logger
|
// DefaultCallerSkipCount used by logger
|
||||||
@@ -29,101 +32,54 @@ type Logger interface {
|
|||||||
Level(level Level)
|
Level(level Level)
|
||||||
// The Logger options
|
// The Logger options
|
||||||
Options() Options
|
Options() Options
|
||||||
// Fields set fields to always be logged with keyval pairs
|
// Attrs set attrs to always be logged with keyval pairs
|
||||||
Fields(fields ...interface{}) Logger
|
Attrs(attrs ...interface{}) Logger
|
||||||
// Info level message
|
// Info level message
|
||||||
Info(ctx context.Context, args ...interface{})
|
Info(ctx context.Context, msg string, attrs ...interface{})
|
||||||
// Trace level message
|
|
||||||
Trace(ctx context.Context, args ...interface{})
|
|
||||||
// Debug level message
|
|
||||||
Debug(ctx context.Context, args ...interface{})
|
|
||||||
// Warn level message
|
|
||||||
Warn(ctx context.Context, args ...interface{})
|
|
||||||
// Error level message
|
|
||||||
Error(ctx context.Context, args ...interface{})
|
|
||||||
// Fatal level message
|
|
||||||
Fatal(ctx context.Context, args ...interface{})
|
|
||||||
// Infof level message
|
|
||||||
Infof(ctx context.Context, msg string, args ...interface{})
|
|
||||||
// Tracef level message
|
// Tracef level message
|
||||||
Tracef(ctx context.Context, msg string, args ...interface{})
|
Trace(ctx context.Context, msg string, attrs ...interface{})
|
||||||
// Debug level message
|
// Debug level message
|
||||||
Debugf(ctx context.Context, msg string, args ...interface{})
|
Debug(ctx context.Context, msg string, attrs ...interface{})
|
||||||
// Warn level message
|
// Warn level message
|
||||||
Warnf(ctx context.Context, msg string, args ...interface{})
|
Warn(ctx context.Context, msg string, attrs ...interface{})
|
||||||
// Error level message
|
// Error level message
|
||||||
Errorf(ctx context.Context, msg string, args ...interface{})
|
Error(ctx context.Context, msg string, attrs ...interface{})
|
||||||
// Fatal level message
|
// Fatal level message
|
||||||
Fatalf(ctx context.Context, msg string, args ...interface{})
|
Fatal(ctx context.Context, msg string, attrs ...interface{})
|
||||||
// Log logs message with needed level
|
// Log logs message with needed level
|
||||||
Log(ctx context.Context, level Level, args ...interface{})
|
Log(ctx context.Context, level Level, msg string, attrs ...interface{})
|
||||||
// Logf logs message with needed level
|
|
||||||
Logf(ctx context.Context, level Level, msg string, args ...interface{})
|
|
||||||
// String returns the name of logger
|
// String returns the name of logger
|
||||||
String() string
|
String() string
|
||||||
}
|
}
|
||||||
|
|
||||||
// Field contains keyval pair
|
// Info writes formatted msg to default logger on info level
|
||||||
type Field interface{}
|
func Info(ctx context.Context, msg string, attrs ...interface{}) {
|
||||||
|
DefaultLogger.Info(ctx, msg, attrs...)
|
||||||
// Info writes msg to default logger on info level
|
|
||||||
func Info(ctx context.Context, args ...interface{}) {
|
|
||||||
DefaultLogger.Info(ctx, args...)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Error writes msg to default logger on error level
|
// Error writes formatted msg to default logger on error level
|
||||||
func Error(ctx context.Context, args ...interface{}) {
|
func Error(ctx context.Context, msg string, attrs ...interface{}) {
|
||||||
DefaultLogger.Error(ctx, args...)
|
DefaultLogger.Error(ctx, msg, attrs...)
|
||||||
}
|
|
||||||
|
|
||||||
// Debug writes msg to default logger on debug level
|
|
||||||
func Debug(ctx context.Context, args ...interface{}) {
|
|
||||||
DefaultLogger.Debug(ctx, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Warn writes msg to default logger on warn level
|
|
||||||
func Warn(ctx context.Context, args ...interface{}) {
|
|
||||||
DefaultLogger.Warn(ctx, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Trace writes msg to default logger on trace level
|
|
||||||
func Trace(ctx context.Context, args ...interface{}) {
|
|
||||||
DefaultLogger.Trace(ctx, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fatal writes msg to default logger on fatal level
|
|
||||||
func Fatal(ctx context.Context, args ...interface{}) {
|
|
||||||
DefaultLogger.Fatal(ctx, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Infof writes formatted msg to default logger on info level
|
|
||||||
func Infof(ctx context.Context, msg string, args ...interface{}) {
|
|
||||||
DefaultLogger.Infof(ctx, msg, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Errorf writes formatted msg to default logger on error level
|
|
||||||
func Errorf(ctx context.Context, msg string, args ...interface{}) {
|
|
||||||
DefaultLogger.Errorf(ctx, msg, args...)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Debugf writes formatted msg to default logger on debug level
|
// Debugf writes formatted msg to default logger on debug level
|
||||||
func Debugf(ctx context.Context, msg string, args ...interface{}) {
|
func Debugf(ctx context.Context, msg string, attrs ...interface{}) {
|
||||||
DefaultLogger.Debugf(ctx, msg, args...)
|
DefaultLogger.Debug(ctx, msg, attrs...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Warnf writes formatted msg to default logger on warn level
|
// Warn writes formatted msg to default logger on warn level
|
||||||
func Warnf(ctx context.Context, msg string, args ...interface{}) {
|
func Warn(ctx context.Context, msg string, attrs ...interface{}) {
|
||||||
DefaultLogger.Warnf(ctx, msg, args...)
|
DefaultLogger.Warn(ctx, msg, attrs...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tracef writes formatted msg to default logger on trace level
|
// Trace writes formatted msg to default logger on trace level
|
||||||
func Tracef(ctx context.Context, msg string, args ...interface{}) {
|
func Trace(ctx context.Context, msg string, attrs ...interface{}) {
|
||||||
DefaultLogger.Tracef(ctx, msg, args...)
|
DefaultLogger.Trace(ctx, msg, attrs...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fatalf writes formatted msg to default logger on fatal level
|
// Fatal writes formatted msg to default logger on fatal level
|
||||||
func Fatalf(ctx context.Context, msg string, args ...interface{}) {
|
func Fatal(ctx context.Context, msg string, attrs ...interface{}) {
|
||||||
DefaultLogger.Fatalf(ctx, msg, args...)
|
DefaultLogger.Fatal(ctx, msg, attrs...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// V returns true if passed level enabled in default logger
|
// V returns true if passed level enabled in default logger
|
||||||
@@ -131,12 +87,12 @@ func V(level Level) bool {
|
|||||||
return DefaultLogger.V(level)
|
return DefaultLogger.V(level)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Init initialize logger
|
// Init initialize default logger
|
||||||
func Init(opts ...options.Option) error {
|
func Init(opts ...options.Option) error {
|
||||||
return DefaultLogger.Init(opts...)
|
return DefaultLogger.Init(opts...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fields create logger with specific fields
|
// Attrs create default logger with specific attrs
|
||||||
func Fields(fields ...interface{}) Logger {
|
func Attrs(attrs ...interface{}) Logger {
|
||||||
return DefaultLogger.Fields(fields...)
|
return DefaultLogger.Attrs(attrs...)
|
||||||
}
|
}
|
||||||
|
71
logger/noop.go
Normal file
71
logger/noop.go
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
package logger
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"go.unistack.org/micro/v4/options"
|
||||||
|
)
|
||||||
|
|
||||||
|
type noopLogger struct {
|
||||||
|
opts Options
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewLogger(opts ...options.Option) Logger {
|
||||||
|
options := NewOptions(opts...)
|
||||||
|
return &noopLogger{opts: options}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *noopLogger) V(lvl Level) bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *noopLogger) Level(lvl Level) {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *noopLogger) Init(opts ...options.Option) error {
|
||||||
|
for _, o := range opts {
|
||||||
|
o(&l.opts)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *noopLogger) Clone(opts ...options.Option) Logger {
|
||||||
|
nl := &noopLogger{opts: l.opts}
|
||||||
|
for _, o := range opts {
|
||||||
|
o(&nl.opts)
|
||||||
|
}
|
||||||
|
return nl
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *noopLogger) Attrs(attrs ...interface{}) Logger {
|
||||||
|
return l
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *noopLogger) Options() Options {
|
||||||
|
return l.opts
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *noopLogger) String() string {
|
||||||
|
return "noop"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *noopLogger) Log(ctx context.Context, lvl Level, msg string, attrs ...interface{}) {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *noopLogger) Info(ctx context.Context, msg string, attrs ...interface{}) {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *noopLogger) Debug(ctx context.Context, msg string, attrs ...interface{}) {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *noopLogger) Error(ctx context.Context, msg string, attrs ...interface{}) {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *noopLogger) Trace(ctx context.Context, msg string, attrs ...interface{}) {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *noopLogger) Warn(ctx context.Context, msg string, attrs ...interface{}) {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *noopLogger) Fatal(ctx context.Context, msg string, attrs ...interface{}) {
|
||||||
|
}
|
@@ -3,9 +3,12 @@ package logger
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"io"
|
"io"
|
||||||
|
"log/slog"
|
||||||
"os"
|
"os"
|
||||||
|
"reflect"
|
||||||
|
|
||||||
"go.unistack.org/micro/v4/options"
|
"go.unistack.org/micro/v4/options"
|
||||||
|
rutil "go.unistack.org/micro/v4/util/reflect"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Options holds logger options
|
// Options holds logger options
|
||||||
@@ -14,35 +17,66 @@ type Options struct {
|
|||||||
Out io.Writer
|
Out io.Writer
|
||||||
// Context holds exernal options
|
// Context holds exernal options
|
||||||
Context context.Context
|
Context context.Context
|
||||||
// Fields holds additional metadata
|
// Attrs holds additional attributes
|
||||||
Fields []interface{}
|
Attrs []interface{}
|
||||||
// Name holds the logger name
|
// Name holds the logger name
|
||||||
Name string
|
Name string
|
||||||
// The logging level the logger should log
|
// The logging level the logger should log
|
||||||
Level Level
|
Level Level
|
||||||
// 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
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewOptions creates new options struct
|
// NewOptions creates new options struct
|
||||||
func NewOptions(opts ...options.Option) Options {
|
func NewOptions(opts ...options.Option) Options {
|
||||||
options := Options{
|
options := Options{
|
||||||
Level: DefaultLevel,
|
Level: DefaultLevel,
|
||||||
Fields: make([]interface{}, 0, 6),
|
Attrs: 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
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithFields set default fields for the logger
|
// WithContextAttrFuncs appends default funcs for the context arrts filler
|
||||||
func WithFields(fields ...interface{}) options.Option {
|
func WithContextAttrFuncs(fncs ...ContextAttrFunc) options.Option {
|
||||||
return func(src interface{}) error {
|
return func(src interface{}) error {
|
||||||
return options.Set(src, fields, ".Fields")
|
v, err := options.Get(src, ".ContextAttrFuncs")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
} else if rutil.IsZero(v) {
|
||||||
|
v = reflect.MakeSlice(reflect.TypeOf(v), 0, len(fncs)).Interface()
|
||||||
|
}
|
||||||
|
cv := reflect.ValueOf(v)
|
||||||
|
for _, l := range fncs {
|
||||||
|
cv = reflect.Append(cv, reflect.ValueOf(l))
|
||||||
|
}
|
||||||
|
return options.Set(src, cv.Interface(), ".ContextAttrFuncs")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithAttrs set default fields for the logger
|
||||||
|
func WithAttrs(attrs ...interface{}) options.Option {
|
||||||
|
return func(src interface{}) error {
|
||||||
|
return options.Set(src, attrs, ".Attrs")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -66,3 +100,79 @@ func WithCallerSkipCount(c int) options.Option {
|
|||||||
return options.Set(src, c, ".CallerSkipCount")
|
return options.Set(src, c, ".CallerSkipCount")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func WithZapKeys() options.Option {
|
||||||
|
return func(src interface{}) error {
|
||||||
|
var err error
|
||||||
|
if err = options.Set(src, "@timestamp", ".TimeKey"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err = options.Set(src, "level", ".LevelKey"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err = options.Set(src, "msg", ".MessageKey"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err = options.Set(src, "caller", ".SourceKey"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func WithZerologKeys() options.Option {
|
||||||
|
return func(src interface{}) error {
|
||||||
|
var err error
|
||||||
|
if err = options.Set(src, "time", ".TimeKey"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err = options.Set(src, "level", ".LevelKey"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err = options.Set(src, "message", ".MessageKey"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err = options.Set(src, "caller", ".SourceKey"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func WithSlogKeys() options.Option {
|
||||||
|
return func(src interface{}) error {
|
||||||
|
var err error
|
||||||
|
if err = options.Set(src, slog.TimeKey, ".TimeKey"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err = options.Set(src, slog.LevelKey, ".LevelKey"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err = options.Set(src, slog.MessageKey, ".MessageKey"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err = options.Set(src, slog.SourceKey, ".SourceKey"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func WithMicroKeys() options.Option {
|
||||||
|
return func(src interface{}) error {
|
||||||
|
var err error
|
||||||
|
if err = options.Set(src, "timestamp", ".TimeKey"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err = options.Set(src, "level", ".LevelKey"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err = options.Set(src, "msg", ".MessageKey"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err = options.Set(src, "caller", ".SourceKey"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
307
logger/slog/slog.go
Normal file
307
logger/slog/slog.go
Normal file
@@ -0,0 +1,307 @@
|
|||||||
|
package slog
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"log/slog"
|
||||||
|
"os"
|
||||||
|
"runtime"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"go.unistack.org/micro/v4/logger"
|
||||||
|
"go.unistack.org/micro/v4/options"
|
||||||
|
"go.unistack.org/micro/v4/tracer"
|
||||||
|
)
|
||||||
|
|
||||||
|
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")
|
||||||
|
)
|
||||||
|
|
||||||
|
func (s *slogLogger) renameAttr(_ []string, a slog.Attr) slog.Attr {
|
||||||
|
switch a.Key {
|
||||||
|
case slog.SourceKey:
|
||||||
|
source := a.Value.Any().(*slog.Source)
|
||||||
|
a.Value = slog.StringValue(source.File + ":" + strconv.Itoa(source.Line))
|
||||||
|
a.Key = s.opts.SourceKey
|
||||||
|
case slog.TimeKey:
|
||||||
|
a.Key = s.opts.TimeKey
|
||||||
|
case slog.MessageKey:
|
||||||
|
a.Key = s.opts.MessageKey
|
||||||
|
case slog.LevelKey:
|
||||||
|
level := a.Value.Any().(slog.Level)
|
||||||
|
lvl := slogToLoggerLevel(level)
|
||||||
|
a.Key = s.opts.LevelKey
|
||||||
|
switch {
|
||||||
|
case lvl < logger.DebugLevel:
|
||||||
|
a.Value = traceValue
|
||||||
|
case lvl < logger.InfoLevel:
|
||||||
|
a.Value = debugValue
|
||||||
|
case lvl < logger.WarnLevel:
|
||||||
|
a.Value = infoValue
|
||||||
|
case lvl < logger.ErrorLevel:
|
||||||
|
a.Value = warnValue
|
||||||
|
case lvl < logger.FatalLevel:
|
||||||
|
a.Value = errorValue
|
||||||
|
case lvl >= logger.FatalLevel:
|
||||||
|
a.Value = fatalValue
|
||||||
|
default:
|
||||||
|
a.Value = infoValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
|
||||||
|
type slogLogger struct {
|
||||||
|
slog *slog.Logger
|
||||||
|
leveler *slog.LevelVar
|
||||||
|
opts logger.Options
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *slogLogger) Clone(opts ...options.Option) logger.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: s.renameAttr,
|
||||||
|
Level: l.leveler,
|
||||||
|
AddSource: true,
|
||||||
|
}
|
||||||
|
l.leveler.Set(loggerToSlogLevel(l.opts.Level))
|
||||||
|
handler := slog.NewJSONHandler(options.Out, handleOpt)
|
||||||
|
l.slog = slog.New(handler).With(options.Attrs...)
|
||||||
|
|
||||||
|
return l
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *slogLogger) V(level logger.Level) bool {
|
||||||
|
return s.opts.Level.Enabled(level)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *slogLogger) Level(level logger.Level) {
|
||||||
|
s.leveler.Set(loggerToSlogLevel(level))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *slogLogger) Options() logger.Options {
|
||||||
|
return s.opts
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *slogLogger) Attrs(attrs ...interface{}) logger.Logger {
|
||||||
|
nl := &slogLogger{opts: s.opts}
|
||||||
|
nl.leveler = new(slog.LevelVar)
|
||||||
|
nl.leveler.Set(s.leveler.Level())
|
||||||
|
|
||||||
|
handleOpt := &slog.HandlerOptions{
|
||||||
|
ReplaceAttr: nl.renameAttr,
|
||||||
|
Level: s.leveler,
|
||||||
|
AddSource: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
handler := slog.NewJSONHandler(s.opts.Out, handleOpt)
|
||||||
|
nl.slog = slog.New(handler).With(attrs...)
|
||||||
|
|
||||||
|
return nl
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *slogLogger) Init(opts ...options.Option) error {
|
||||||
|
if len(s.opts.ContextAttrFuncs) == 0 {
|
||||||
|
s.opts.ContextAttrFuncs = logger.DefaultContextAttrFuncs
|
||||||
|
}
|
||||||
|
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: s.renameAttr,
|
||||||
|
Level: s.leveler,
|
||||||
|
AddSource: true,
|
||||||
|
}
|
||||||
|
s.leveler.Set(loggerToSlogLevel(s.opts.Level))
|
||||||
|
handler := slog.NewJSONHandler(s.opts.Out, handleOpt)
|
||||||
|
s.slog = slog.New(handler).With(s.opts.Attrs...)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *slogLogger) Log(ctx context.Context, lvl logger.Level, msg string, attrs ...interface{}) {
|
||||||
|
if !s.V(lvl) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var pcs [1]uintptr
|
||||||
|
runtime.Callers(s.opts.CallerSkipCount, pcs[:]) // skip [Callers, Infof]
|
||||||
|
r := slog.NewRecord(time.Now(), loggerToSlogLevel(lvl), msg, pcs[0])
|
||||||
|
for _, fn := range s.opts.ContextAttrFuncs {
|
||||||
|
attrs = append(attrs, fn(ctx)...)
|
||||||
|
}
|
||||||
|
r.Add(attrs...)
|
||||||
|
_ = s.slog.Handler().Handle(ctx, r)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *slogLogger) Info(ctx context.Context, msg string, attrs ...interface{}) {
|
||||||
|
if !s.V(logger.InfoLevel) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var pcs [1]uintptr
|
||||||
|
runtime.Callers(s.opts.CallerSkipCount, pcs[:]) // skip [Callers, Infof]
|
||||||
|
r := slog.NewRecord(time.Now(), slog.LevelInfo, msg, pcs[0])
|
||||||
|
for _, fn := range s.opts.ContextAttrFuncs {
|
||||||
|
attrs = append(attrs, fn(ctx)...)
|
||||||
|
}
|
||||||
|
r.Add(attrs...)
|
||||||
|
_ = s.slog.Handler().Handle(ctx, r)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *slogLogger) Debug(ctx context.Context, msg string, attrs ...interface{}) {
|
||||||
|
if !s.V(logger.DebugLevel) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var pcs [1]uintptr
|
||||||
|
runtime.Callers(s.opts.CallerSkipCount, pcs[:]) // skip [Callers, Infof]
|
||||||
|
r := slog.NewRecord(time.Now(), slog.LevelDebug, msg, pcs[0])
|
||||||
|
for _, fn := range s.opts.ContextAttrFuncs {
|
||||||
|
attrs = append(attrs, fn(ctx)...)
|
||||||
|
}
|
||||||
|
r.Add(attrs...)
|
||||||
|
_ = s.slog.Handler().Handle(ctx, r)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *slogLogger) Trace(ctx context.Context, msg string, attrs ...interface{}) {
|
||||||
|
if !s.V(logger.TraceLevel) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var pcs [1]uintptr
|
||||||
|
runtime.Callers(s.opts.CallerSkipCount, pcs[:]) // skip [Callers, Infof]
|
||||||
|
r := slog.NewRecord(time.Now(), slog.LevelDebug-1, msg, pcs[0])
|
||||||
|
for _, fn := range s.opts.ContextAttrFuncs {
|
||||||
|
attrs = append(attrs, fn(ctx)...)
|
||||||
|
}
|
||||||
|
r.Add(attrs...)
|
||||||
|
_ = s.slog.Handler().Handle(ctx, r)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *slogLogger) Error(ctx context.Context, msg string, attrs ...interface{}) {
|
||||||
|
if !s.V(logger.ErrorLevel) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var pcs [1]uintptr
|
||||||
|
runtime.Callers(s.opts.CallerSkipCount, pcs[:]) // skip [Callers, Infof]
|
||||||
|
r := slog.NewRecord(time.Now(), slog.LevelError, msg, pcs[0])
|
||||||
|
for _, fn := range s.opts.ContextAttrFuncs {
|
||||||
|
attrs = append(attrs, fn(ctx)...)
|
||||||
|
}
|
||||||
|
r.Add(attrs...)
|
||||||
|
r.Attrs(func(a slog.Attr) bool {
|
||||||
|
if a.Key == "error" {
|
||||||
|
if span, ok := tracer.SpanFromContext(ctx); ok {
|
||||||
|
span.SetStatus(tracer.SpanStatusError, a.Value.String())
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
_ = s.slog.Handler().Handle(ctx, r)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *slogLogger) Fatal(ctx context.Context, msg string, attrs ...interface{}) {
|
||||||
|
if !s.V(logger.FatalLevel) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var pcs [1]uintptr
|
||||||
|
runtime.Callers(s.opts.CallerSkipCount, pcs[:]) // skip [Callers, Infof]
|
||||||
|
r := slog.NewRecord(time.Now(), slog.LevelError+1, msg, pcs[0])
|
||||||
|
for _, fn := range s.opts.ContextAttrFuncs {
|
||||||
|
attrs = append(attrs, fn(ctx)...)
|
||||||
|
}
|
||||||
|
r.Add(attrs...)
|
||||||
|
_ = s.slog.Handler().Handle(ctx, r)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *slogLogger) Warn(ctx context.Context, msg string, attrs ...interface{}) {
|
||||||
|
if !s.V(logger.WarnLevel) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var pcs [1]uintptr
|
||||||
|
runtime.Callers(s.opts.CallerSkipCount, pcs[:]) // skip [Callers, Infof]
|
||||||
|
r := slog.NewRecord(time.Now(), slog.LevelWarn, msg, pcs[0])
|
||||||
|
for _, fn := range s.opts.ContextAttrFuncs {
|
||||||
|
attrs = append(attrs, fn(ctx)...)
|
||||||
|
}
|
||||||
|
r.Add(attrs...)
|
||||||
|
_ = s.slog.Handler().Handle(ctx, r)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *slogLogger) String() string {
|
||||||
|
return "slog"
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewLogger(opts ...options.Option) logger.Logger {
|
||||||
|
l := &slogLogger{
|
||||||
|
opts: logger.NewOptions(opts...),
|
||||||
|
}
|
||||||
|
return l
|
||||||
|
}
|
||||||
|
|
||||||
|
func loggerToSlogLevel(level logger.Level) slog.Level {
|
||||||
|
switch level {
|
||||||
|
case logger.DebugLevel:
|
||||||
|
return slog.LevelDebug
|
||||||
|
case logger.WarnLevel:
|
||||||
|
return slog.LevelWarn
|
||||||
|
case logger.ErrorLevel:
|
||||||
|
return slog.LevelError
|
||||||
|
case logger.TraceLevel:
|
||||||
|
return slog.LevelDebug - 1
|
||||||
|
case logger.FatalLevel:
|
||||||
|
return slog.LevelError + 1
|
||||||
|
default:
|
||||||
|
return slog.LevelInfo
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func slogToLoggerLevel(level slog.Level) logger.Level {
|
||||||
|
switch level {
|
||||||
|
case slog.LevelDebug:
|
||||||
|
return logger.DebugLevel
|
||||||
|
case slog.LevelWarn:
|
||||||
|
return logger.WarnLevel
|
||||||
|
case slog.LevelError:
|
||||||
|
return logger.ErrorLevel
|
||||||
|
case slog.LevelDebug - 1:
|
||||||
|
return logger.TraceLevel
|
||||||
|
case slog.LevelError + 1:
|
||||||
|
return logger.FatalLevel
|
||||||
|
default:
|
||||||
|
return logger.InfoLevel
|
||||||
|
}
|
||||||
|
}
|
@@ -1,21 +1,23 @@
|
|||||||
package logger
|
package slog
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"log"
|
"log"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"go.unistack.org/micro/v4/logger"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestContext(t *testing.T) {
|
func TestContext(t *testing.T) {
|
||||||
ctx := context.TODO()
|
ctx := context.TODO()
|
||||||
buf := bytes.NewBuffer(nil)
|
buf := bytes.NewBuffer(nil)
|
||||||
l := NewLogger(WithLevel(TraceLevel), WithOutput(buf))
|
l := NewLogger(logger.WithLevel(logger.TraceLevel), logger.WithOutput(buf))
|
||||||
if err := l.Init(); err != nil {
|
if err := l.Init(); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
nl, ok := FromContext(NewContext(ctx, l.Fields("key", "val")))
|
nl, ok := logger.FromContext(logger.NewContext(ctx, l.Attrs("key", "val")))
|
||||||
if !ok {
|
if !ok {
|
||||||
t.Fatal("context without logger")
|
t.Fatal("context without logger")
|
||||||
}
|
}
|
||||||
@@ -25,15 +27,15 @@ func TestContext(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestFields(t *testing.T) {
|
func TestAttrs(t *testing.T) {
|
||||||
ctx := context.TODO()
|
ctx := context.TODO()
|
||||||
buf := bytes.NewBuffer(nil)
|
buf := bytes.NewBuffer(nil)
|
||||||
l := NewLogger(WithLevel(TraceLevel), WithOutput(buf))
|
l := NewLogger(logger.WithLevel(logger.TraceLevel), logger.WithOutput(buf))
|
||||||
if err := l.Init(); err != nil {
|
if err := l.Init(); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
nl := l.Fields("key", "val")
|
nl := l.Attrs("key", "val")
|
||||||
|
|
||||||
nl.Info(ctx, "message")
|
nl.Info(ctx, "message")
|
||||||
if !bytes.Contains(buf.Bytes(), []byte(`"key":"val"`)) {
|
if !bytes.Contains(buf.Bytes(), []byte(`"key":"val"`)) {
|
||||||
@@ -45,15 +47,15 @@ func TestFromContextWithFields(t *testing.T) {
|
|||||||
ctx := context.TODO()
|
ctx := context.TODO()
|
||||||
buf := bytes.NewBuffer(nil)
|
buf := bytes.NewBuffer(nil)
|
||||||
var ok bool
|
var ok bool
|
||||||
l := NewLogger(WithLevel(TraceLevel), WithOutput(buf))
|
l := NewLogger(logger.WithLevel(logger.TraceLevel), logger.WithOutput(buf))
|
||||||
if err := l.Init(); err != nil {
|
if err := l.Init(); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
nl := l.Fields("key", "val")
|
nl := l.Attrs("key", "val")
|
||||||
|
|
||||||
ctx = NewContext(ctx, nl)
|
ctx = logger.NewContext(ctx, nl)
|
||||||
|
|
||||||
l, ok = FromContext(ctx)
|
l, ok = logger.FromContext(ctx)
|
||||||
if !ok {
|
if !ok {
|
||||||
t.Fatalf("context does not have logger")
|
t.Fatalf("context does not have logger")
|
||||||
}
|
}
|
||||||
@@ -67,11 +69,11 @@ func TestFromContextWithFields(t *testing.T) {
|
|||||||
func TestClone(t *testing.T) {
|
func TestClone(t *testing.T) {
|
||||||
ctx := context.TODO()
|
ctx := context.TODO()
|
||||||
buf := bytes.NewBuffer(nil)
|
buf := bytes.NewBuffer(nil)
|
||||||
l := NewLogger(WithLevel(TraceLevel), WithOutput(buf))
|
l := NewLogger(logger.WithLevel(logger.TraceLevel), logger.WithOutput(buf))
|
||||||
if err := l.Init(); err != nil {
|
if err := l.Init(); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
nl := l.Clone(WithLevel(ErrorLevel))
|
nl := l.Clone(logger.WithLevel(logger.ErrorLevel))
|
||||||
if err := nl.Init(); err != nil {
|
if err := nl.Init(); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -87,27 +89,27 @@ func TestClone(t *testing.T) {
|
|||||||
|
|
||||||
func TestRedirectStdLogger(t *testing.T) {
|
func TestRedirectStdLogger(t *testing.T) {
|
||||||
buf := bytes.NewBuffer(nil)
|
buf := bytes.NewBuffer(nil)
|
||||||
l := NewLogger(WithLevel(TraceLevel), WithOutput(buf))
|
l := NewLogger(logger.WithLevel(logger.ErrorLevel), logger.WithOutput(buf))
|
||||||
if err := l.Init(); err != nil {
|
if err := l.Init(); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
fn := RedirectStdLogger(l, ErrorLevel)
|
fn := logger.RedirectStdLogger(l, logger.ErrorLevel)
|
||||||
defer fn()
|
defer fn()
|
||||||
log.Print("test")
|
log.Print("test")
|
||||||
if !bytes.Contains(buf.Bytes(), []byte(`"level":"error","msg":"test","timestamp"`)) {
|
if !(bytes.Contains(buf.Bytes(), []byte(`"level":"error"`)) && bytes.Contains(buf.Bytes(), []byte(`"msg":"test"`))) {
|
||||||
t.Fatalf("logger error, buf %s", buf.Bytes())
|
t.Fatalf("logger error, buf %s", buf.Bytes())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestStdLogger(t *testing.T) {
|
func TestStdLogger(t *testing.T) {
|
||||||
buf := bytes.NewBuffer(nil)
|
buf := bytes.NewBuffer(nil)
|
||||||
l := NewLogger(WithLevel(TraceLevel), WithOutput(buf))
|
l := NewLogger(logger.WithLevel(logger.TraceLevel), logger.WithOutput(buf))
|
||||||
if err := l.Init(); err != nil {
|
if err := l.Init(); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
lg := NewStdLogger(l, ErrorLevel)
|
lg := logger.NewStdLogger(l, logger.ErrorLevel)
|
||||||
lg.Print("test")
|
lg.Print("test")
|
||||||
if !bytes.Contains(buf.Bytes(), []byte(`"level":"error","msg":"test","timestamp"`)) {
|
if !(bytes.Contains(buf.Bytes(), []byte(`"level":"error"`)) && bytes.Contains(buf.Bytes(), []byte(`"msg":"test"`))) {
|
||||||
t.Fatalf("logger error, buf %s", buf.Bytes())
|
t.Fatalf("logger error, buf %s", buf.Bytes())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -115,24 +117,25 @@ func TestStdLogger(t *testing.T) {
|
|||||||
func TestLogger(t *testing.T) {
|
func TestLogger(t *testing.T) {
|
||||||
ctx := context.TODO()
|
ctx := context.TODO()
|
||||||
buf := bytes.NewBuffer(nil)
|
buf := bytes.NewBuffer(nil)
|
||||||
l := NewLogger(WithLevel(TraceLevel), WithOutput(buf))
|
l := NewLogger(logger.WithLevel(logger.TraceLevel), logger.WithOutput(buf))
|
||||||
if err := l.Init(); err != nil {
|
if err := l.Init(); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
l.Trace(ctx, "trace_msg1")
|
l.Trace(ctx, "trace_msg1")
|
||||||
l.Warn(ctx, "warn_msg1")
|
l.Warn(ctx, "warn_msg1")
|
||||||
l.Fields("error", "test").Info(ctx, "error message")
|
l.Attrs("error", "test").Info(ctx, "error message")
|
||||||
l.Warn(ctx, "first", " ", "second")
|
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(`"level":"trace"`)) && bytes.Contains(buf.Bytes(), []byte(`"msg":"trace_msg1"`))) {
|
||||||
|
t.Fatalf("logger tracer, buf %s", buf.Bytes())
|
||||||
}
|
}
|
||||||
if !bytes.Contains(buf.Bytes(), []byte(`"warn","msg":"warn_msg1"`)) {
|
if !(bytes.Contains(buf.Bytes(), []byte(`"level":"warn"`)) && bytes.Contains(buf.Bytes(), []byte(`"msg":"warn_msg1"`))) {
|
||||||
t.Fatalf("logger error, buf %s", buf.Bytes())
|
t.Fatalf("logger warn, buf %s", buf.Bytes())
|
||||||
}
|
}
|
||||||
if !bytes.Contains(buf.Bytes(), []byte(`"error":"test","level":"info","msg":"error message"`)) {
|
if !(bytes.Contains(buf.Bytes(), []byte(`"level":"info"`)) && bytes.Contains(buf.Bytes(), []byte(`"msg":"error message","error":"test"`))) {
|
||||||
t.Fatalf("logger error, buf %s", buf.Bytes())
|
t.Fatalf("logger info, buf %s", buf.Bytes())
|
||||||
}
|
}
|
||||||
if !bytes.Contains(buf.Bytes(), []byte(`"level":"warn","msg":"first second"`)) {
|
if !(bytes.Contains(buf.Bytes(), []byte(`"level":"warn"`)) && bytes.Contains(buf.Bytes(), []byte(`"msg":"first second"`))) {
|
||||||
t.Fatalf("logger error, buf %s", buf.Bytes())
|
t.Fatalf("logger warn, buf %s", buf.Bytes())
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -12,7 +12,7 @@ type stdLogger struct {
|
|||||||
|
|
||||||
// NewStdLogger returns new *log.Logger baked by logger.Logger implementation
|
// NewStdLogger returns new *log.Logger baked by logger.Logger implementation
|
||||||
func NewStdLogger(l Logger, level Level) *log.Logger {
|
func NewStdLogger(l Logger, level Level) *log.Logger {
|
||||||
return log.New(&stdLogger{l: l, level: level}, "" /* prefix */, 0 /* flags */)
|
return log.New(&stdLogger{l: l.Clone(WithCallerSkipCount(l.Options().CallerSkipCount + 1)), level: level}, "" /* prefix */, 0 /* flags */)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sl *stdLogger) Write(p []byte) (int, error) {
|
func (sl *stdLogger) Write(p []byte) (int, error) {
|
||||||
|
@@ -1,298 +0,0 @@
|
|||||||
// Package wrapper provides wrapper for Logger
|
|
||||||
package wrapper
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"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 []options.Option, err error) []string {
|
|
||||||
labels := []string{"service", req.Service(), "endpoint", req.Endpoint()}
|
|
||||||
if err != nil {
|
|
||||||
labels = append(labels, "error", err.Error())
|
|
||||||
}
|
|
||||||
return labels
|
|
||||||
}
|
|
||||||
|
|
||||||
// DefaultClientStreamObserver called by wrapper in client Stream
|
|
||||||
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())
|
|
||||||
}
|
|
||||||
return labels
|
|
||||||
}
|
|
||||||
|
|
||||||
// DefaultServerHandlerObserver called by wrapper in server Handler
|
|
||||||
DefaultServerHandlerObserver = func(ctx context.Context, req server.Request, rsp interface{}, err error) []string {
|
|
||||||
labels := []string{"service", req.Service(), "endpoint", req.Endpoint()}
|
|
||||||
if err != nil {
|
|
||||||
labels = append(labels, "error", err.Error())
|
|
||||||
}
|
|
||||||
return labels
|
|
||||||
}
|
|
||||||
|
|
||||||
// DefaultClientCallFuncObserver called by wrapper in client CallFunc
|
|
||||||
DefaultClientCallFuncObserver = func(ctx context.Context, addr string, req client.Request, rsp interface{}, opts client.CallOptions, err error) []string {
|
|
||||||
labels := []string{"service", req.Service(), "endpoint", req.Endpoint()}
|
|
||||||
if err != nil {
|
|
||||||
labels = append(labels, "error", err.Error())
|
|
||||||
}
|
|
||||||
return labels
|
|
||||||
}
|
|
||||||
|
|
||||||
// DefaultSkipEndpoints wrapper not called for this endpoints
|
|
||||||
DefaultSkipEndpoints = []string{"Meter.Metrics", "Health.Live", "Health.Ready", "Health.Version"}
|
|
||||||
)
|
|
||||||
|
|
||||||
type lWrapper struct {
|
|
||||||
client.Client
|
|
||||||
serverHandler server.HandlerFunc
|
|
||||||
clientCallFunc client.CallFunc
|
|
||||||
opts Options
|
|
||||||
}
|
|
||||||
|
|
||||||
type (
|
|
||||||
// ClientCallObserver func signature
|
|
||||||
ClientCallObserver func(context.Context, client.Request, interface{}, []options.Option, error) []string
|
|
||||||
// ClientStreamObserver func signature
|
|
||||||
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
|
|
||||||
ServerHandlerObserver func(context.Context, server.Request, interface{}, error) []string
|
|
||||||
)
|
|
||||||
|
|
||||||
// Options struct for wrapper
|
|
||||||
type Options struct {
|
|
||||||
// Logger that used for log
|
|
||||||
Logger logger.Logger
|
|
||||||
// ServerHandlerObservers funcs
|
|
||||||
ServerHandlerObservers []ServerHandlerObserver
|
|
||||||
// ClientCallObservers funcs
|
|
||||||
ClientCallObservers []ClientCallObserver
|
|
||||||
// ClientStreamObservers funcs
|
|
||||||
ClientStreamObservers []ClientStreamObserver
|
|
||||||
// ClientCallFuncObservers funcs
|
|
||||||
ClientCallFuncObservers []ClientCallFuncObserver
|
|
||||||
// SkipEndpoints
|
|
||||||
SkipEndpoints []string
|
|
||||||
// Level for logger
|
|
||||||
Level logger.Level
|
|
||||||
// Enabled flag
|
|
||||||
Enabled bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// Option func signature
|
|
||||||
type Option func(*Options)
|
|
||||||
|
|
||||||
// NewOptions creates Options from Option slice
|
|
||||||
func NewOptions(opts ...Option) Options {
|
|
||||||
options := Options{
|
|
||||||
Logger: logger.DefaultLogger,
|
|
||||||
Level: logger.TraceLevel,
|
|
||||||
ClientCallObservers: []ClientCallObserver{DefaultClientCallObserver},
|
|
||||||
ClientStreamObservers: []ClientStreamObserver{DefaultClientStreamObserver},
|
|
||||||
ClientCallFuncObservers: []ClientCallFuncObserver{DefaultClientCallFuncObserver},
|
|
||||||
ServerHandlerObservers: []ServerHandlerObserver{DefaultServerHandlerObserver},
|
|
||||||
SkipEndpoints: DefaultSkipEndpoints,
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, o := range opts {
|
|
||||||
o(&options)
|
|
||||||
}
|
|
||||||
|
|
||||||
return options
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithEnabled enable/diable flag
|
|
||||||
func WithEnabled(b bool) Option {
|
|
||||||
return func(o *Options) {
|
|
||||||
o.Enabled = b
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithLevel log level
|
|
||||||
func WithLevel(l logger.Level) Option {
|
|
||||||
return func(o *Options) {
|
|
||||||
o.Level = l
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithLogger logger
|
|
||||||
func WithLogger(l logger.Logger) Option {
|
|
||||||
return func(o *Options) {
|
|
||||||
o.Logger = l
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithClientCallObservers funcs
|
|
||||||
func WithClientCallObservers(ob ...ClientCallObserver) Option {
|
|
||||||
return func(o *Options) {
|
|
||||||
o.ClientCallObservers = ob
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithClientStreamObservers funcs
|
|
||||||
func WithClientStreamObservers(ob ...ClientStreamObserver) Option {
|
|
||||||
return func(o *Options) {
|
|
||||||
o.ClientStreamObservers = ob
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithClientCallFuncObservers funcs
|
|
||||||
func WithClientCallFuncObservers(ob ...ClientCallFuncObserver) Option {
|
|
||||||
return func(o *Options) {
|
|
||||||
o.ClientCallFuncObservers = ob
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithServerHandlerObservers funcs
|
|
||||||
func WithServerHandlerObservers(ob ...ServerHandlerObserver) Option {
|
|
||||||
return func(o *Options) {
|
|
||||||
o.ServerHandlerObservers = ob
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// SkipEndpoins
|
|
||||||
func SkipEndpoints(eps ...string) Option {
|
|
||||||
return func(o *Options) {
|
|
||||||
o.SkipEndpoints = append(o.SkipEndpoints, eps...)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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())
|
|
||||||
for _, ep := range l.opts.SkipEndpoints {
|
|
||||||
if ep == endpoint {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !l.opts.Enabled {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
var labels []string
|
|
||||||
for _, o := range l.opts.ClientCallObservers {
|
|
||||||
labels = append(labels, o(ctx, req, rsp, opts, err)...)
|
|
||||||
}
|
|
||||||
l.opts.Logger.Fields(labels).Log(ctx, l.opts.Level)
|
|
||||||
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
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())
|
|
||||||
for _, ep := range l.opts.SkipEndpoints {
|
|
||||||
if ep == endpoint {
|
|
||||||
return stream, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !l.opts.Enabled {
|
|
||||||
return stream, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var labels []string
|
|
||||||
for _, o := range l.opts.ClientStreamObservers {
|
|
||||||
labels = append(labels, o(ctx, req, opts, stream, err)...)
|
|
||||||
}
|
|
||||||
l.opts.Logger.Fields(labels).Log(ctx, l.opts.Level)
|
|
||||||
|
|
||||||
return stream, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *lWrapper) ServerHandler(ctx context.Context, req server.Request, rsp interface{}) error {
|
|
||||||
err := l.serverHandler(ctx, req, rsp)
|
|
||||||
|
|
||||||
endpoint := req.Endpoint()
|
|
||||||
for _, ep := range l.opts.SkipEndpoints {
|
|
||||||
if ep == endpoint {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !l.opts.Enabled {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
var labels []string
|
|
||||||
for _, o := range l.opts.ServerHandlerObservers {
|
|
||||||
labels = append(labels, o(ctx, req, rsp, err)...)
|
|
||||||
}
|
|
||||||
l.opts.Logger.Fields(labels).Log(ctx, l.opts.Level)
|
|
||||||
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewClientWrapper accepts an open options and returns a Client Wrapper
|
|
||||||
func NewClientWrapper(opts ...Option) client.Wrapper {
|
|
||||||
return func(c client.Client) client.Client {
|
|
||||||
options := NewOptions()
|
|
||||||
for _, o := range opts {
|
|
||||||
o(&options)
|
|
||||||
}
|
|
||||||
return &lWrapper{opts: options, Client: c}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewClientCallWrapper accepts an options and returns a Call Wrapper
|
|
||||||
func NewClientCallWrapper(opts ...Option) client.CallWrapper {
|
|
||||||
return func(h client.CallFunc) client.CallFunc {
|
|
||||||
options := NewOptions()
|
|
||||||
for _, o := range opts {
|
|
||||||
o(&options)
|
|
||||||
}
|
|
||||||
|
|
||||||
l := &lWrapper{opts: options, clientCallFunc: h}
|
|
||||||
return l.ClientCallFunc
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *lWrapper) ClientCallFunc(ctx context.Context, addr string, req client.Request, rsp interface{}, opts client.CallOptions) error {
|
|
||||||
err := l.clientCallFunc(ctx, addr, req, rsp, opts)
|
|
||||||
|
|
||||||
endpoint := fmt.Sprintf("%s.%s", req.Service(), req.Endpoint())
|
|
||||||
for _, ep := range l.opts.SkipEndpoints {
|
|
||||||
if ep == endpoint {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !l.opts.Enabled {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
var labels []string
|
|
||||||
for _, o := range l.opts.ClientCallFuncObservers {
|
|
||||||
labels = append(labels, o(ctx, addr, req, rsp, opts, err)...)
|
|
||||||
}
|
|
||||||
l.opts.Logger.Fields(labels).Log(ctx, l.opts.Level)
|
|
||||||
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewServerHandlerWrapper accepts an options and returns a Handler Wrapper
|
|
||||||
func NewServerHandlerWrapper(opts ...Option) server.HandlerWrapper {
|
|
||||||
return func(h server.HandlerFunc) server.HandlerFunc {
|
|
||||||
options := NewOptions()
|
|
||||||
for _, o := range opts {
|
|
||||||
o(&options)
|
|
||||||
}
|
|
||||||
|
|
||||||
l := &lWrapper{opts: options, serverHandler: h}
|
|
||||||
return l.ServerHandler
|
|
||||||
}
|
|
||||||
}
|
|
@@ -63,7 +63,7 @@ func (m *memory) ttlPrune() {
|
|||||||
for id, n := range record.Nodes {
|
for id, n := range record.Nodes {
|
||||||
if n.TTL != 0 && time.Since(n.LastSeen) > n.TTL {
|
if n.TTL != 0 && time.Since(n.LastSeen) > n.TTL {
|
||||||
if m.opts.Logger.V(logger.DebugLevel) {
|
if m.opts.Logger.V(logger.DebugLevel) {
|
||||||
m.opts.Logger.Debugf(m.opts.Context, "Register TTL expired for node %s of service %s", n.ID, service)
|
m.opts.Logger.Debug(m.opts.Context, "RegisterTTL expired for node "+n.ID+" of service "+service)
|
||||||
}
|
}
|
||||||
delete(m.records[domain][service][version].Nodes, id)
|
delete(m.records[domain][service][version].Nodes, id)
|
||||||
}
|
}
|
||||||
@@ -150,7 +150,7 @@ func (m *memory) Register(ctx context.Context, s *Service, opts ...RegisterOptio
|
|||||||
if _, ok := srvs[s.Name][s.Version]; !ok {
|
if _, ok := srvs[s.Name][s.Version]; !ok {
|
||||||
srvs[s.Name][s.Version] = r
|
srvs[s.Name][s.Version] = r
|
||||||
if m.opts.Logger.V(logger.DebugLevel) {
|
if m.opts.Logger.V(logger.DebugLevel) {
|
||||||
m.opts.Logger.Debugf(m.opts.Context, "Register added new service: %s, version: %s", s.Name, s.Version)
|
m.opts.Logger.Debug(m.opts.Context, "register added new service: "+s.Name+", version "+s.Version)
|
||||||
}
|
}
|
||||||
m.records[options.Domain] = srvs
|
m.records[options.Domain] = srvs
|
||||||
go m.sendEvent(&Result{Action: "create", Service: s})
|
go m.sendEvent(&Result{Action: "create", Service: s})
|
||||||
@@ -190,14 +190,14 @@ func (m *memory) Register(ctx context.Context, s *Service, opts ...RegisterOptio
|
|||||||
|
|
||||||
if addedNodes {
|
if addedNodes {
|
||||||
if m.opts.Logger.V(logger.DebugLevel) {
|
if m.opts.Logger.V(logger.DebugLevel) {
|
||||||
m.opts.Logger.Debugf(m.opts.Context, "Register added new node to service: %s, version: %s", s.Name, s.Version)
|
m.opts.Logger.Debug(m.opts.Context, "register added new node to service: "+s.Name+", version "+s.Version)
|
||||||
}
|
}
|
||||||
go m.sendEvent(&Result{Action: "update", Service: s})
|
go m.sendEvent(&Result{Action: "update", Service: s})
|
||||||
} else {
|
} else {
|
||||||
// refresh TTL and timestamp
|
// refresh TTL and timestamp
|
||||||
for _, n := range s.Nodes {
|
for _, n := range s.Nodes {
|
||||||
if m.opts.Logger.V(logger.DebugLevel) {
|
if m.opts.Logger.V(logger.DebugLevel) {
|
||||||
m.opts.Logger.Debugf(m.opts.Context, "Updated registration for service: %s, version: %s", s.Name, s.Version)
|
m.opts.Logger.Debug(m.opts.Context, "updated registration for service: "+s.Name+", version "+s.Version)
|
||||||
}
|
}
|
||||||
srvs[s.Name][s.Version].Nodes[n.ID].TTL = options.TTL
|
srvs[s.Name][s.Version].Nodes[n.ID].TTL = options.TTL
|
||||||
srvs[s.Name][s.Version].Nodes[n.ID].LastSeen = time.Now()
|
srvs[s.Name][s.Version].Nodes[n.ID].LastSeen = time.Now()
|
||||||
@@ -242,7 +242,7 @@ func (m *memory) Deregister(ctx context.Context, s *Service, opts ...DeregisterO
|
|||||||
for _, n := range s.Nodes {
|
for _, n := range s.Nodes {
|
||||||
if _, ok := version.Nodes[n.ID]; ok {
|
if _, ok := version.Nodes[n.ID]; ok {
|
||||||
if m.opts.Logger.V(logger.DebugLevel) {
|
if m.opts.Logger.V(logger.DebugLevel) {
|
||||||
m.opts.Logger.Debugf(m.opts.Context, "Register removed node from service: %s, version: %s", s.Name, s.Version)
|
m.opts.Logger.Debug(m.opts.Context, "register removed node from service: "+s.Name+", version "+s.Version)
|
||||||
}
|
}
|
||||||
delete(version.Nodes, n.ID)
|
delete(version.Nodes, n.ID)
|
||||||
}
|
}
|
||||||
@@ -263,7 +263,7 @@ func (m *memory) Deregister(ctx context.Context, s *Service, opts ...DeregisterO
|
|||||||
go m.sendEvent(&Result{Action: "delete", Service: s})
|
go m.sendEvent(&Result{Action: "delete", Service: s})
|
||||||
|
|
||||||
if m.opts.Logger.V(logger.DebugLevel) {
|
if m.opts.Logger.V(logger.DebugLevel) {
|
||||||
m.opts.Logger.Debugf(m.opts.Context, "Register removed service: %s", s.Name)
|
m.opts.Logger.Debug(m.opts.Context, "register removed service: "+s.Name)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -272,7 +272,7 @@ func (m *memory) Deregister(ctx context.Context, s *Service, opts ...DeregisterO
|
|||||||
delete(m.records[options.Domain][s.Name], s.Version)
|
delete(m.records[options.Domain][s.Name], s.Version)
|
||||||
go m.sendEvent(&Result{Action: "delete", Service: s})
|
go m.sendEvent(&Result{Action: "delete", Service: s})
|
||||||
if m.opts.Logger.V(logger.DebugLevel) {
|
if m.opts.Logger.V(logger.DebugLevel) {
|
||||||
m.opts.Logger.Debugf(m.opts.Context, "Register removed service: %s, version: %s", s.Name, s.Version)
|
m.opts.Logger.Debug(m.opts.Context, "register removed service: "+s.Name+", version "+s.Version)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@@ -467,7 +467,7 @@ func serviceToRecord(s *Service, ttl time.Duration) *record {
|
|||||||
}
|
}
|
||||||
|
|
||||||
endpoints := make([]*Endpoint, len(s.Endpoints))
|
endpoints := make([]*Endpoint, len(s.Endpoints))
|
||||||
for i, e := range s.Endpoints {
|
for i, e := range s.Endpoints { // TODO: vtolstov use copy
|
||||||
endpoints[i] = e
|
endpoints[i] = e
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -122,7 +122,7 @@ func (n *noopServer) Register() error {
|
|||||||
|
|
||||||
if !registered {
|
if !registered {
|
||||||
if config.Logger.V(logger.InfoLevel) {
|
if config.Logger.V(logger.InfoLevel) {
|
||||||
config.Logger.Infof(n.opts.Context, "register [%s] Registering node: %s", config.Register.String(), service.Nodes[0].ID)
|
config.Logger.Info(n.opts.Context, "register ["+config.Register.String()+"] Registering node: "+service.Nodes[0].ID)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -160,7 +160,7 @@ func (n *noopServer) Deregister() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if config.Logger.V(logger.InfoLevel) {
|
if config.Logger.V(logger.InfoLevel) {
|
||||||
config.Logger.Infof(n.opts.Context, "deregistering node: %s", service.Nodes[0].ID)
|
config.Logger.Info(n.opts.Context, "deregistering node: "+service.Nodes[0].ID)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := DefaultDeregisterFunc(service, config); err != nil {
|
if err := DefaultDeregisterFunc(service, config); err != nil {
|
||||||
@@ -204,7 +204,7 @@ func (n *noopServer) Start() error {
|
|||||||
config.Address = addr
|
config.Address = addr
|
||||||
|
|
||||||
if config.Logger.V(logger.InfoLevel) {
|
if config.Logger.V(logger.InfoLevel) {
|
||||||
config.Logger.Infof(n.opts.Context, "server [noop] Listening on %s", config.Address)
|
config.Logger.Info(n.opts.Context, "server [noop] Listening on "+config.Address)
|
||||||
}
|
}
|
||||||
|
|
||||||
n.Lock()
|
n.Lock()
|
||||||
@@ -217,13 +217,13 @@ func (n *noopServer) Start() error {
|
|||||||
// nolint: nestif
|
// nolint: nestif
|
||||||
if err := config.RegisterCheck(config.Context); err != nil {
|
if err := config.RegisterCheck(config.Context); err != nil {
|
||||||
if config.Logger.V(logger.ErrorLevel) {
|
if config.Logger.V(logger.ErrorLevel) {
|
||||||
config.Logger.Errorf(n.opts.Context, "server %s-%s register check error: %s", config.Name, config.ID, err)
|
config.Logger.Error(n.opts.Context, "server "+config.Name+"-"+config.ID+" register check error: "+err.Error())
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// announce self to the world
|
// announce self to the world
|
||||||
if err := n.Register(); err != nil {
|
if err := n.Register(); err != nil {
|
||||||
if config.Logger.V(logger.ErrorLevel) {
|
if config.Logger.V(logger.ErrorLevel) {
|
||||||
config.Logger.Errorf(n.opts.Context, "server register error: %v", err)
|
config.Logger.Error(n.opts.Context, "server register error: "+err.Error())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -252,23 +252,23 @@ func (n *noopServer) Start() error {
|
|||||||
// nolint: nestif
|
// nolint: nestif
|
||||||
if rerr != nil && registered {
|
if rerr != nil && registered {
|
||||||
if config.Logger.V(logger.ErrorLevel) {
|
if config.Logger.V(logger.ErrorLevel) {
|
||||||
config.Logger.Errorf(n.opts.Context, "server %s-%s register check error: %s, deregister it", config.Name, config.ID, rerr)
|
config.Logger.Error(n.opts.Context, "server "+config.Name+"-"+config.ID+" register check error: ", rerr.Error())
|
||||||
}
|
}
|
||||||
// deregister self in case of error
|
// deregister self in case of error
|
||||||
if err := n.Deregister(); err != nil {
|
if err := n.Deregister(); err != nil {
|
||||||
if config.Logger.V(logger.ErrorLevel) {
|
if config.Logger.V(logger.ErrorLevel) {
|
||||||
config.Logger.Errorf(n.opts.Context, "server %s-%s deregister error: %s", config.Name, config.ID, err)
|
config.Logger.Error(n.opts.Context, "server "+config.Name+"-"+config.ID+" deregister error: ", err.Error())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if rerr != nil && !registered {
|
} else if rerr != nil && !registered {
|
||||||
if config.Logger.V(logger.ErrorLevel) {
|
if config.Logger.V(logger.ErrorLevel) {
|
||||||
config.Logger.Errorf(n.opts.Context, "server %s-%s register check error: %s", config.Name, config.ID, rerr)
|
config.Logger.Error(n.opts.Context, "server "+config.Name+"-"+config.ID+" register check error: ", rerr.Error())
|
||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if err := n.Register(); err != nil {
|
if err := n.Register(); err != nil {
|
||||||
if config.Logger.V(logger.ErrorLevel) {
|
if config.Logger.V(logger.ErrorLevel) {
|
||||||
config.Logger.Errorf(n.opts.Context, "server %s-%s register error: %s", config.Name, config.ID, err)
|
config.Logger.Error(n.opts.Context, "server "+config.Name+"-"+config.ID+" register error: ", err.Error())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// wait for exit
|
// wait for exit
|
||||||
@@ -280,7 +280,7 @@ func (n *noopServer) Start() error {
|
|||||||
// deregister self
|
// deregister self
|
||||||
if err := n.Deregister(); err != nil {
|
if err := n.Deregister(); err != nil {
|
||||||
if config.Logger.V(logger.ErrorLevel) {
|
if config.Logger.V(logger.ErrorLevel) {
|
||||||
config.Logger.Errorf(n.opts.Context, "server deregister error: ", err)
|
config.Logger.Error(n.opts.Context, "server deregister error: "+err.Error())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
78
service.go
78
service.go
@@ -258,7 +258,7 @@ func (s *service) Start() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if config.Loggers[0].V(logger.InfoLevel) {
|
if config.Loggers[0].V(logger.InfoLevel) {
|
||||||
config.Loggers[0].Infof(s.opts.Context, "starting [service] %s version %s", s.Options().Name, s.Options().Version)
|
config.Loggers[0].Info(s.opts.Context, "starting [service] "+s.Options().Name+" version "+s.Options().Version)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(s.opts.Servers) == 0 {
|
if len(s.opts.Servers) == 0 {
|
||||||
@@ -304,7 +304,7 @@ func (s *service) Stop() error {
|
|||||||
s.RUnlock()
|
s.RUnlock()
|
||||||
|
|
||||||
if config.Loggers[0].V(logger.InfoLevel) {
|
if config.Loggers[0].V(logger.InfoLevel) {
|
||||||
config.Loggers[0].Infof(s.opts.Context, "stoppping [service] %s", s.Name())
|
config.Loggers[0].Info(s.opts.Context, "stoppping [service] "+s.Name())
|
||||||
}
|
}
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
@@ -372,19 +372,71 @@ func (s *service) Run() error {
|
|||||||
return s.Stop()
|
return s.Stop()
|
||||||
}
|
}
|
||||||
|
|
||||||
type nameIface interface {
|
|
||||||
Name() string
|
|
||||||
}
|
|
||||||
|
|
||||||
func getNameIndex(n string, ifaces interface{}) int {
|
func getNameIndex(n string, ifaces interface{}) int {
|
||||||
values, ok := ifaces.([]interface{})
|
switch values := ifaces.(type) {
|
||||||
if !ok {
|
case []router.Router:
|
||||||
return 0
|
for idx, iface := range values {
|
||||||
}
|
if iface.Name() == n {
|
||||||
for idx, iface := range values {
|
return idx
|
||||||
if ifc, ok := iface.(nameIface); ok && ifc.Name() == n {
|
}
|
||||||
return idx
|
|
||||||
}
|
}
|
||||||
|
case []register.Register:
|
||||||
|
for idx, iface := range values {
|
||||||
|
if iface.Name() == n {
|
||||||
|
return idx
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case []store.Store:
|
||||||
|
for idx, iface := range values {
|
||||||
|
if iface.Name() == n {
|
||||||
|
return idx
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case []tracer.Tracer:
|
||||||
|
for idx, iface := range values {
|
||||||
|
if iface.Name() == n {
|
||||||
|
return idx
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case []server.Server:
|
||||||
|
for idx, iface := range values {
|
||||||
|
if iface.Name() == n {
|
||||||
|
return idx
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case []config.Config:
|
||||||
|
for idx, iface := range values {
|
||||||
|
if iface.Name() == n {
|
||||||
|
return idx
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case []meter.Meter:
|
||||||
|
for idx, iface := range values {
|
||||||
|
if iface.Name() == n {
|
||||||
|
return idx
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case []broker.Broker:
|
||||||
|
for idx, iface := range values {
|
||||||
|
if iface.Name() == n {
|
||||||
|
return idx
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case []client.Client:
|
||||||
|
for idx, iface := range values {
|
||||||
|
if iface.Name() == n {
|
||||||
|
return idx
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
case []logger.Logger:
|
||||||
|
for idx, iface := range values {
|
||||||
|
if iface.Name() == n {
|
||||||
|
return idx
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
@@ -17,20 +17,18 @@ import (
|
|||||||
"go.unistack.org/micro/v4/tracer"
|
"go.unistack.org/micro/v4/tracer"
|
||||||
)
|
)
|
||||||
|
|
||||||
type testItem struct {
|
func TestClient(t *testing.T) {
|
||||||
name string
|
c1 := client.NewClient(options.Name("test1"))
|
||||||
}
|
c2 := client.NewClient(options.Name("test2"))
|
||||||
|
|
||||||
func (ti *testItem) Name() string {
|
svc := NewService(Client(c1, c2))
|
||||||
return ti.name
|
if err := svc.Init(); err != nil {
|
||||||
}
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
func TestGetNameIndex(t *testing.T) {
|
x1 := svc.Client("test2")
|
||||||
item1 := &testItem{name: "first"}
|
if x1.Name() != "test2" {
|
||||||
item2 := &testItem{name: "second"}
|
t.Fatal("invalid client")
|
||||||
items := []interface{}{item1, item2}
|
|
||||||
if idx := getNameIndex("second", items); idx != 1 {
|
|
||||||
t.Fatalf("getNameIndex func error, item not found")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -4,6 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
|
|
||||||
"go.unistack.org/micro/v4/options"
|
"go.unistack.org/micro/v4/options"
|
||||||
|
"go.unistack.org/micro/v4/util/id"
|
||||||
)
|
)
|
||||||
|
|
||||||
var _ Tracer = (*noopTracer)(nil)
|
var _ Tracer = (*noopTracer)(nil)
|
||||||
@@ -26,6 +27,8 @@ func (t *noopTracer) Start(ctx context.Context, name string, opts ...options.Opt
|
|||||||
labels: options.Labels,
|
labels: options.Labels,
|
||||||
kind: options.Kind,
|
kind: options.Kind,
|
||||||
}
|
}
|
||||||
|
span.spanID.s, _ = id.New()
|
||||||
|
span.traceID.s, _ = id.New()
|
||||||
if span.ctx == nil {
|
if span.ctx == nil {
|
||||||
span.ctx = context.Background()
|
span.ctx = context.Background()
|
||||||
}
|
}
|
||||||
@@ -53,11 +56,21 @@ type noopEvent struct {
|
|||||||
labels []interface{}
|
labels []interface{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type noopStringer struct {
|
||||||
|
s string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s noopStringer) String() string {
|
||||||
|
return s.s
|
||||||
|
}
|
||||||
|
|
||||||
type noopSpan struct {
|
type noopSpan struct {
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
tracer Tracer
|
tracer Tracer
|
||||||
name string
|
name string
|
||||||
statusMsg string
|
statusMsg string
|
||||||
|
traceID noopStringer
|
||||||
|
spanID noopStringer
|
||||||
events []*noopEvent
|
events []*noopEvent
|
||||||
labels []interface{}
|
labels []interface{}
|
||||||
logs []interface{}
|
logs []interface{}
|
||||||
@@ -97,6 +110,14 @@ func (s *noopSpan) Kind() SpanKind {
|
|||||||
return s.kind
|
return s.kind
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *noopSpan) TraceID() string {
|
||||||
|
return s.traceID.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *noopSpan) SpanID() string {
|
||||||
|
return s.spanID.String()
|
||||||
|
}
|
||||||
|
|
||||||
func (s *noopSpan) Status() (SpanStatus, string) {
|
func (s *noopSpan) Status() (SpanStatus, string) {
|
||||||
return s.status, s.statusMsg
|
return s.status, s.statusMsg
|
||||||
}
|
}
|
||||||
|
@@ -2,9 +2,11 @@ package tracer
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"reflect"
|
||||||
|
|
||||||
"go.unistack.org/micro/v4/logger"
|
"go.unistack.org/micro/v4/logger"
|
||||||
"go.unistack.org/micro/v4/options"
|
"go.unistack.org/micro/v4/options"
|
||||||
|
rutil "go.unistack.org/micro/v4/util/reflect"
|
||||||
)
|
)
|
||||||
|
|
||||||
type SpanStatus int
|
type SpanStatus int
|
||||||
@@ -93,6 +95,43 @@ type EventOptions struct {
|
|||||||
Labels []interface{}
|
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 {
|
func WithSpanKind(k SpanKind) options.Option {
|
||||||
return func(src interface{}) error {
|
return func(src interface{}) error {
|
||||||
return options.Set(src, k, ".Kind")
|
return options.Set(src, k, ".Kind")
|
||||||
|
@@ -6,6 +6,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"sort"
|
"sort"
|
||||||
|
|
||||||
|
"go.unistack.org/micro/v4/logger"
|
||||||
"go.unistack.org/micro/v4/options"
|
"go.unistack.org/micro/v4/options"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -45,6 +46,20 @@ type Span interface {
|
|||||||
AddLogs(kv ...interface{})
|
AddLogs(kv ...interface{})
|
||||||
// Kind returns span kind
|
// Kind returns span kind
|
||||||
Kind() SpanKind
|
Kind() SpanKind
|
||||||
|
// TraceID returns trace id
|
||||||
|
TraceID() string
|
||||||
|
// SpanID returns span id
|
||||||
|
SpanID() string
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
logger.DefaultContextAttrFuncs = append(logger.DefaultContextAttrFuncs, func(ctx context.Context) []interface{} {
|
||||||
|
span, ok := SpanFromContext(ctx)
|
||||||
|
if !ok || span == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return []interface{}{"trace", span.TraceID(), "span", span.SpanID()}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// sort labels alphabeticaly by label name
|
// sort labels alphabeticaly by label name
|
||||||
|
28
tracer/tracer_test.go
Normal file
28
tracer/tracer_test.go
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
package tracer_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"go.unistack.org/micro/v4/logger"
|
||||||
|
"go.unistack.org/micro/v4/logger/slog"
|
||||||
|
"go.unistack.org/micro/v4/tracer"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestLoggerWithTracer(t *testing.T) {
|
||||||
|
ctx := context.TODO()
|
||||||
|
buf := bytes.NewBuffer(nil)
|
||||||
|
logger.DefaultLogger = slog.NewLogger(logger.WithOutput(buf))
|
||||||
|
|
||||||
|
if err := logger.Init(); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
var span tracer.Span
|
||||||
|
ctx, span = tracer.DefaultTracer.Start(ctx, "test")
|
||||||
|
logger.Info(ctx, "msg")
|
||||||
|
if !strings.Contains(buf.String(), span.TraceID()) {
|
||||||
|
t.Fatalf("log does not contains tracer id")
|
||||||
|
}
|
||||||
|
}
|
@@ -13,7 +13,7 @@ import (
|
|||||||
"go.unistack.org/micro/v4/tracer"
|
"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{} {
|
func ExtractDefaultLabels(md metadata.Metadata) []interface{} {
|
||||||
labels := make([]interface{}, 0, len(DefaultHeadersExctract))
|
labels := make([]interface{}, 0, len(DefaultHeadersExctract))
|
||||||
@@ -27,6 +27,7 @@ func ExtractDefaultLabels(md metadata.Metadata) []interface{} {
|
|||||||
|
|
||||||
var (
|
var (
|
||||||
DefaultClientCallObserver = func(ctx context.Context, req client.Request, rsp interface{}, opts []options.Option, 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{}
|
var labels []interface{}
|
||||||
if md, ok := metadata.FromOutgoingContext(ctx); ok {
|
if md, ok := metadata.FromOutgoingContext(ctx); ok {
|
||||||
labels = append(labels, ExtractDefaultLabels(md)...)
|
labels = append(labels, ExtractDefaultLabels(md)...)
|
||||||
|
@@ -71,7 +71,7 @@ func New(opts ...Option) (string, error) {
|
|||||||
func Must(opts ...Option) string {
|
func Must(opts ...Option) string {
|
||||||
id, err := New(opts...)
|
id, err := New(opts...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Fatal(context.TODO(), err)
|
logger.Fatal(context.TODO(), err.Error())
|
||||||
}
|
}
|
||||||
return id
|
return id
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user