Compare commits
27 Commits
Author | SHA1 | Date | |
---|---|---|---|
88c7439c01 | |||
f4d0237785 | |||
0f343dad0b | |||
7c29afba0b | |||
8159b9d233 | |||
45cdac5c29 | |||
98db0dc8bc | |||
453d2232bd | |||
9b387312da | |||
84024f7713 | |||
5a554f9f0c | |||
9c33cbc8e2 | |||
848fe1c0d4 | |||
6cbf23fec5 | |||
7462b0b53c | |||
cb743cee3f | |||
d18952951c | |||
|
f6b7f1b4bc | ||
|
33fa702104 | ||
4debc392d1 | |||
7137d99102 | |||
f76b3171d9 | |||
db3fc42204 | |||
f59023d741 | |||
6f17fd891a | |||
fd93308e8e | |||
2aaa0a74d8 |
2
.github/workflows/build.yml
vendored
2
.github/workflows/build.yml
vendored
@@ -34,7 +34,7 @@ jobs:
|
|||||||
- name: checkout
|
- name: checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
- name: lint
|
- name: lint
|
||||||
uses: golangci/golangci-lint-action@v3.3.1
|
uses: golangci/golangci-lint-action@v3.4.0
|
||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
with:
|
with:
|
||||||
# Required: the version of golangci-lint is required and must be specified without patch version: we always use the latest patch version.
|
# Required: the version of golangci-lint is required and must be specified without patch version: we always use the latest patch version.
|
||||||
|
2
.github/workflows/dependabot-automerge.yml
vendored
2
.github/workflows/dependabot-automerge.yml
vendored
@@ -15,7 +15,7 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: metadata
|
- name: metadata
|
||||||
id: metadata
|
id: metadata
|
||||||
uses: dependabot/fetch-metadata@v1.3.5
|
uses: dependabot/fetch-metadata@v1.3.6
|
||||||
with:
|
with:
|
||||||
github-token: "${{ secrets.TOKEN }}"
|
github-token: "${{ secrets.TOKEN }}"
|
||||||
- name: merge
|
- name: merge
|
||||||
|
2
.github/workflows/pr.yml
vendored
2
.github/workflows/pr.yml
vendored
@@ -34,7 +34,7 @@ jobs:
|
|||||||
- name: checkout
|
- name: checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
- name: lint
|
- name: lint
|
||||||
uses: golangci/golangci-lint-action@v3.3.1
|
uses: golangci/golangci-lint-action@v3.4.0
|
||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
with:
|
with:
|
||||||
# Required: the version of golangci-lint is required and must be specified without patch version: we always use the latest patch version.
|
# Required: the version of golangci-lint is required and must be specified without patch version: we always use the latest patch version.
|
||||||
|
@@ -5,9 +5,11 @@ import (
|
|||||||
"reflect"
|
"reflect"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/imdario/mergo"
|
"github.com/imdario/mergo"
|
||||||
rutil "go.unistack.org/micro/v3/util/reflect"
|
rutil "go.unistack.org/micro/v3/util/reflect"
|
||||||
|
mtime "go.unistack.org/micro/v3/util/time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type defaultConfig struct {
|
type defaultConfig struct {
|
||||||
@@ -75,6 +77,7 @@ func fillValue(value reflect.Value, val string) error {
|
|||||||
if !rutil.IsEmpty(value) {
|
if !rutil.IsEmpty(value) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
switch value.Kind() {
|
switch value.Kind() {
|
||||||
case reflect.Map:
|
case reflect.Map:
|
||||||
t := value.Type()
|
t := value.Type()
|
||||||
@@ -151,11 +154,26 @@ func fillValue(value reflect.Value, val string) error {
|
|||||||
}
|
}
|
||||||
value.Set(reflect.ValueOf(int32(v)))
|
value.Set(reflect.ValueOf(int32(v)))
|
||||||
case reflect.Int64:
|
case reflect.Int64:
|
||||||
v, err := strconv.ParseInt(val, 10, 64)
|
switch {
|
||||||
if err != nil {
|
case value.Type().String() == "time.Duration" && value.Type().PkgPath() == "time":
|
||||||
return err
|
v, err := time.ParseDuration(val)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
value.Set(reflect.ValueOf(v))
|
||||||
|
case value.Type().String() == "time.Duration" && value.Type().PkgPath() == "go.unistack.org/micro/v3/util/time":
|
||||||
|
v, err := mtime.ParseDuration(val)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
value.SetInt(int64(v))
|
||||||
|
default:
|
||||||
|
v, err := strconv.ParseInt(val, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
value.Set(reflect.ValueOf(v))
|
||||||
}
|
}
|
||||||
value.Set(reflect.ValueOf(v))
|
|
||||||
case reflect.Uint:
|
case reflect.Uint:
|
||||||
v, err := strconv.ParseUint(val, 10, 0)
|
v, err := strconv.ParseUint(val, 10, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@@ -4,15 +4,19 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
"go.unistack.org/micro/v3/config"
|
"go.unistack.org/micro/v3/config"
|
||||||
|
mtime "go.unistack.org/micro/v3/util/time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type cfg struct {
|
type cfg struct {
|
||||||
StringValue string `default:"string_value"`
|
StringValue string `default:"string_value"`
|
||||||
IgnoreValue string `json:"-"`
|
IgnoreValue string `json:"-"`
|
||||||
StructValue *cfgStructValue
|
StructValue *cfgStructValue
|
||||||
IntValue int `default:"99"`
|
IntValue int `default:"99"`
|
||||||
|
DurationValue time.Duration `default:"10s"`
|
||||||
|
MDurationValue mtime.Duration `default:"10s"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type cfgStructValue struct {
|
type cfgStructValue struct {
|
||||||
|
126
fsm/default.go
Normal file
126
fsm/default.go
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
package fsm
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
type state struct {
|
||||||
|
body interface{}
|
||||||
|
name string
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ State = &state{}
|
||||||
|
|
||||||
|
func (s *state) Name() string {
|
||||||
|
return s.name
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *state) Body() interface{} {
|
||||||
|
return s.body
|
||||||
|
}
|
||||||
|
|
||||||
|
// fsm is a finite state machine
|
||||||
|
type fsm struct {
|
||||||
|
statesMap map[string]StateFunc
|
||||||
|
current string
|
||||||
|
statesOrder []string
|
||||||
|
opts Options
|
||||||
|
mu sync.Mutex
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewFSM creates a new finite state machine having the specified initial state
|
||||||
|
// with specified options
|
||||||
|
func NewFSM(opts ...Option) *fsm {
|
||||||
|
return &fsm{
|
||||||
|
statesMap: map[string]StateFunc{},
|
||||||
|
opts: NewOptions(opts...),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Current returns the current state
|
||||||
|
func (f *fsm) Current() string {
|
||||||
|
f.mu.Lock()
|
||||||
|
s := f.current
|
||||||
|
f.mu.Unlock()
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// Current returns the current state
|
||||||
|
func (f *fsm) Reset() {
|
||||||
|
f.mu.Lock()
|
||||||
|
f.current = f.opts.Initial
|
||||||
|
f.mu.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
// State adds state to fsm
|
||||||
|
func (f *fsm) State(state string, fn StateFunc) {
|
||||||
|
f.mu.Lock()
|
||||||
|
f.statesMap[state] = fn
|
||||||
|
f.statesOrder = append(f.statesOrder, state)
|
||||||
|
f.mu.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start runs state machine with provided data
|
||||||
|
func (f *fsm) Start(ctx context.Context, args interface{}, opts ...Option) (interface{}, error) {
|
||||||
|
var err error
|
||||||
|
|
||||||
|
f.mu.Lock()
|
||||||
|
options := f.opts
|
||||||
|
|
||||||
|
for _, opt := range opts {
|
||||||
|
opt(&options)
|
||||||
|
}
|
||||||
|
|
||||||
|
sopts := []StateOption{StateDryRun(options.DryRun)}
|
||||||
|
|
||||||
|
cstate := options.Initial
|
||||||
|
states := make(map[string]StateFunc, len(f.statesMap))
|
||||||
|
for k, v := range f.statesMap {
|
||||||
|
states[k] = v
|
||||||
|
}
|
||||||
|
f.current = cstate
|
||||||
|
f.mu.Unlock()
|
||||||
|
|
||||||
|
var s State
|
||||||
|
s = &state{name: cstate, body: args}
|
||||||
|
nstate := s.Name()
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return nil, ctx.Err()
|
||||||
|
default:
|
||||||
|
fn, ok := states[nstate]
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf(`state "%s" %w`, nstate, ErrInvalidState)
|
||||||
|
}
|
||||||
|
f.mu.Lock()
|
||||||
|
f.current = nstate
|
||||||
|
f.mu.Unlock()
|
||||||
|
|
||||||
|
// wrap the handler func
|
||||||
|
for i := len(options.Wrappers); i > 0; i-- {
|
||||||
|
fn = options.Wrappers[i-1](fn)
|
||||||
|
}
|
||||||
|
|
||||||
|
s, err = fn(ctx, s, sopts...)
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case err != nil:
|
||||||
|
return s.Body(), err
|
||||||
|
case s.Name() == StateEnd:
|
||||||
|
return s.Body(), nil
|
||||||
|
case s.Name() == "":
|
||||||
|
for idx := range f.statesOrder {
|
||||||
|
if f.statesOrder[idx] == nstate && len(f.statesOrder) > idx+1 {
|
||||||
|
nstate = f.statesOrder[idx+1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
nstate = s.Name()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
174
fsm/fsm.go
174
fsm/fsm.go
@@ -3,8 +3,6 @@ package fsm // import "go.unistack.org/micro/v3/fsm"
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
|
||||||
"sync"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -12,170 +10,20 @@ var (
|
|||||||
StateEnd = "end"
|
StateEnd = "end"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Options struct holding fsm options
|
type State interface {
|
||||||
type Options struct {
|
Name() string
|
||||||
// DryRun mode
|
Body() interface{}
|
||||||
DryRun bool
|
|
||||||
// Initial state
|
|
||||||
Initial string
|
|
||||||
// HooksBefore func slice runs in order before state
|
|
||||||
HooksBefore []HookBeforeFunc
|
|
||||||
// HooksAfter func slice runs in order after state
|
|
||||||
HooksAfter []HookAfterFunc
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// HookBeforeFunc func signature
|
// StateWrapper wraps the StateFunc and returns the equivalent
|
||||||
type HookBeforeFunc func(ctx context.Context, state string, args interface{})
|
type StateWrapper func(StateFunc) StateFunc
|
||||||
|
|
||||||
// HookAfterFunc func signature
|
|
||||||
type HookAfterFunc func(ctx context.Context, state string, args interface{})
|
|
||||||
|
|
||||||
// Option func signature
|
|
||||||
type Option func(*Options)
|
|
||||||
|
|
||||||
// StateOptions holds state options
|
|
||||||
type StateOptions struct {
|
|
||||||
DryRun bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// StateDryRun says that state executes in dry run mode
|
|
||||||
func StateDryRun(b bool) StateOption {
|
|
||||||
return func(o *StateOptions) {
|
|
||||||
o.DryRun = b
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// StateOption func signature
|
|
||||||
type StateOption func(*StateOptions)
|
|
||||||
|
|
||||||
// InitialState sets init state for state machine
|
|
||||||
func InitialState(initial string) Option {
|
|
||||||
return func(o *Options) {
|
|
||||||
o.Initial = initial
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// HookBefore provides hook func slice
|
|
||||||
func HookBefore(fns ...HookBeforeFunc) Option {
|
|
||||||
return func(o *Options) {
|
|
||||||
o.HooksBefore = fns
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// HookAfter provides hook func slice
|
|
||||||
func HookAfter(fns ...HookAfterFunc) Option {
|
|
||||||
return func(o *Options) {
|
|
||||||
o.HooksAfter = fns
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// StateFunc called on state transition and return next step and error
|
// StateFunc called on state transition and return next step and error
|
||||||
type StateFunc func(ctx context.Context, args interface{}, opts ...StateOption) (string, interface{}, error)
|
type StateFunc func(ctx context.Context, state State, opts ...StateOption) (State, error)
|
||||||
|
|
||||||
// FSM is a finite state machine
|
type FSM interface {
|
||||||
type FSM struct {
|
Start(context.Context, interface{}, ...Option) (interface{}, error)
|
||||||
mu sync.Mutex
|
Current() string
|
||||||
statesMap map[string]StateFunc
|
Reset()
|
||||||
statesOrder []string
|
State(string, StateFunc)
|
||||||
opts *Options
|
|
||||||
current string
|
|
||||||
}
|
|
||||||
|
|
||||||
// New creates a new finite state machine having the specified initial state
|
|
||||||
// with specified options
|
|
||||||
func New(opts ...Option) *FSM {
|
|
||||||
options := &Options{}
|
|
||||||
|
|
||||||
for _, opt := range opts {
|
|
||||||
opt(options)
|
|
||||||
}
|
|
||||||
|
|
||||||
return &FSM{
|
|
||||||
statesMap: map[string]StateFunc{},
|
|
||||||
opts: options,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Current returns the current state
|
|
||||||
func (f *FSM) Current() string {
|
|
||||||
f.mu.Lock()
|
|
||||||
defer f.mu.Unlock()
|
|
||||||
return f.current
|
|
||||||
}
|
|
||||||
|
|
||||||
// Current returns the current state
|
|
||||||
func (f *FSM) Reset() {
|
|
||||||
f.mu.Lock()
|
|
||||||
f.current = f.opts.Initial
|
|
||||||
f.mu.Unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
// State adds state to fsm
|
|
||||||
func (f *FSM) State(state string, fn StateFunc) {
|
|
||||||
f.mu.Lock()
|
|
||||||
f.statesMap[state] = fn
|
|
||||||
f.statesOrder = append(f.statesOrder, state)
|
|
||||||
f.mu.Unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Init initialize fsm and check states
|
|
||||||
|
|
||||||
// Start runs state machine with provided data
|
|
||||||
func (f *FSM) Start(ctx context.Context, args interface{}, opts ...Option) (interface{}, error) {
|
|
||||||
var err error
|
|
||||||
var ok bool
|
|
||||||
var fn StateFunc
|
|
||||||
var nstate string
|
|
||||||
|
|
||||||
f.mu.Lock()
|
|
||||||
options := f.opts
|
|
||||||
|
|
||||||
for _, opt := range opts {
|
|
||||||
opt(options)
|
|
||||||
}
|
|
||||||
|
|
||||||
sopts := []StateOption{StateDryRun(options.DryRun)}
|
|
||||||
|
|
||||||
cstate := options.Initial
|
|
||||||
states := make(map[string]StateFunc, len(f.statesMap))
|
|
||||||
for k, v := range f.statesMap {
|
|
||||||
states[k] = v
|
|
||||||
}
|
|
||||||
f.current = cstate
|
|
||||||
f.mu.Unlock()
|
|
||||||
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case <-ctx.Done():
|
|
||||||
return nil, ctx.Err()
|
|
||||||
default:
|
|
||||||
fn, ok = states[cstate]
|
|
||||||
if !ok {
|
|
||||||
return nil, fmt.Errorf(`state "%s" %w`, cstate, ErrInvalidState)
|
|
||||||
}
|
|
||||||
f.mu.Lock()
|
|
||||||
f.current = cstate
|
|
||||||
f.mu.Unlock()
|
|
||||||
for _, fn := range options.HooksBefore {
|
|
||||||
fn(ctx, cstate, args)
|
|
||||||
}
|
|
||||||
nstate, args, err = fn(ctx, args, sopts...)
|
|
||||||
for _, fn := range options.HooksAfter {
|
|
||||||
fn(ctx, cstate, args)
|
|
||||||
}
|
|
||||||
switch {
|
|
||||||
case err != nil:
|
|
||||||
return args, err
|
|
||||||
case nstate == StateEnd:
|
|
||||||
return args, nil
|
|
||||||
case nstate == "":
|
|
||||||
for idx := range f.statesOrder {
|
|
||||||
if f.statesOrder[idx] == cstate && len(f.statesOrder) > idx+1 {
|
|
||||||
nstate = f.statesOrder[idx+1]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
cstate = nstate
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@@ -1,63 +1,72 @@
|
|||||||
package fsm
|
package fsm
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"go.unistack.org/micro/v3/logger"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestFSMStart(t *testing.T) {
|
func TestFSMStart(t *testing.T) {
|
||||||
ctx := context.TODO()
|
ctx := context.TODO()
|
||||||
buf := bytes.NewBuffer(nil)
|
|
||||||
pfb := func(_ context.Context, state string, _ interface{}) {
|
if err := logger.DefaultLogger.Init(); err != nil {
|
||||||
fmt.Fprintf(buf, "before state %s\n", state)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
pfa := func(_ context.Context, state string, _ interface{}) {
|
|
||||||
fmt.Fprintf(buf, "after state %s\n", state)
|
wrapper := func(next StateFunc) StateFunc {
|
||||||
|
return func(sctx context.Context, s State, opts ...StateOption) (State, error) {
|
||||||
|
sctx = logger.NewContext(sctx, logger.Fields("state", s.Name()))
|
||||||
|
return next(sctx, s, opts...)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
f := New(InitialState("1"), HookBefore(pfb), HookAfter(pfa))
|
|
||||||
f1 := func(_ context.Context, req interface{}, _ ...StateOption) (string, interface{}, error) {
|
f := NewFSM(InitialState("1"), WrapState(wrapper))
|
||||||
args := req.(map[string]interface{})
|
f1 := func(sctx context.Context, s State, opts ...StateOption) (State, error) {
|
||||||
|
_, ok := logger.FromContext(sctx)
|
||||||
|
if !ok {
|
||||||
|
t.Fatal("f1 context does not have logger")
|
||||||
|
}
|
||||||
|
args := s.Body().(map[string]interface{})
|
||||||
if v, ok := args["request"].(string); !ok || v == "" {
|
if v, ok := args["request"].(string); !ok || v == "" {
|
||||||
return "", nil, fmt.Errorf("empty request")
|
return nil, fmt.Errorf("empty request")
|
||||||
}
|
}
|
||||||
return "2", map[string]interface{}{"response": "test2"}, nil
|
return &state{name: "", body: map[string]interface{}{"response": "state1"}}, nil
|
||||||
}
|
}
|
||||||
f2 := func(_ context.Context, req interface{}, _ ...StateOption) (string, interface{}, error) {
|
f2 := func(sctx context.Context, s State, opts ...StateOption) (State, error) {
|
||||||
args := req.(map[string]interface{})
|
_, ok := logger.FromContext(sctx)
|
||||||
if v, ok := args["response"].(string); !ok || v == "" {
|
if !ok {
|
||||||
return "", nil, fmt.Errorf("empty response")
|
t.Fatal("f2 context does not have logger")
|
||||||
}
|
}
|
||||||
return "", map[string]interface{}{"response": "test"}, nil
|
args := s.Body().(map[string]interface{})
|
||||||
|
if v, ok := args["response"].(string); !ok || v == "" {
|
||||||
|
return nil, fmt.Errorf("empty response")
|
||||||
|
}
|
||||||
|
return &state{name: "", body: map[string]interface{}{"response": "state2"}}, nil
|
||||||
}
|
}
|
||||||
f3 := func(_ context.Context, req interface{}, _ ...StateOption) (string, interface{}, error) {
|
f3 := func(sctx context.Context, s State, opts ...StateOption) (State, error) {
|
||||||
args := req.(map[string]interface{})
|
_, ok := logger.FromContext(sctx)
|
||||||
if v, ok := args["response"].(string); !ok || v == "" {
|
if !ok {
|
||||||
return "", nil, fmt.Errorf("empty response")
|
t.Fatal("f3 context does not have logger")
|
||||||
}
|
}
|
||||||
return StateEnd, map[string]interface{}{"response": "test_last"}, nil
|
args := s.Body().(map[string]interface{})
|
||||||
|
if v, ok := args["response"].(string); !ok || v == "" {
|
||||||
|
return nil, fmt.Errorf("empty response")
|
||||||
|
}
|
||||||
|
return &state{name: StateEnd, body: map[string]interface{}{"response": "state3"}}, nil
|
||||||
}
|
}
|
||||||
f.State("1", f1)
|
f.State("1", f1)
|
||||||
f.State("2", f2)
|
f.State("2", f2)
|
||||||
f.State("3", f3)
|
f.State("3", f3)
|
||||||
rsp, err := f.Start(ctx, map[string]interface{}{"request": "test1"})
|
rsp, err := f.Start(ctx, map[string]interface{}{"request": "state"})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
args := rsp.(map[string]interface{})
|
args := rsp.(map[string]interface{})
|
||||||
if v, ok := args["response"].(string); !ok || v == "" {
|
if v, ok := args["response"].(string); !ok || v == "" {
|
||||||
t.Fatalf("nil rsp: %#+v", args)
|
t.Fatalf("nil rsp: %#+v", args)
|
||||||
} else if v != "test_last" {
|
} else if v != "state3" {
|
||||||
t.Fatalf("invalid rsp %#+v", args)
|
t.Fatalf("invalid rsp %#+v", args)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !bytes.Contains(buf.Bytes(), []byte(`before state 1`)) ||
|
|
||||||
!bytes.Contains(buf.Bytes(), []byte(`before state 2`)) ||
|
|
||||||
!bytes.Contains(buf.Bytes(), []byte(`after state 1`)) ||
|
|
||||||
!bytes.Contains(buf.Bytes(), []byte(`after state 2`)) ||
|
|
||||||
!bytes.Contains(buf.Bytes(), []byte(`after state 3`)) ||
|
|
||||||
!bytes.Contains(buf.Bytes(), []byte(`after state 3`)) {
|
|
||||||
t.Fatalf("fsm not works properly or hooks error, buf: %s", buf.Bytes())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
52
fsm/options.go
Normal file
52
fsm/options.go
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
package fsm
|
||||||
|
|
||||||
|
// Options struct holding fsm options
|
||||||
|
type Options struct {
|
||||||
|
// Initial state
|
||||||
|
Initial string
|
||||||
|
// Wrappers runs before state
|
||||||
|
Wrappers []StateWrapper
|
||||||
|
// DryRun mode
|
||||||
|
DryRun bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// Option func signature
|
||||||
|
type Option func(*Options)
|
||||||
|
|
||||||
|
// StateOptions holds state options
|
||||||
|
type StateOptions struct {
|
||||||
|
DryRun bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// StateDryRun says that state executes in dry run mode
|
||||||
|
func StateDryRun(b bool) StateOption {
|
||||||
|
return func(o *StateOptions) {
|
||||||
|
o.DryRun = b
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// StateOption func signature
|
||||||
|
type StateOption func(*StateOptions)
|
||||||
|
|
||||||
|
// InitialState sets init state for state machine
|
||||||
|
func InitialState(initial string) Option {
|
||||||
|
return func(o *Options) {
|
||||||
|
o.Initial = initial
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WrapState adds a state Wrapper to a list of options passed into the fsm
|
||||||
|
func WrapState(w StateWrapper) Option {
|
||||||
|
return func(o *Options) {
|
||||||
|
o.Wrappers = append(o.Wrappers, w)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewOptions returns new Options struct filled by passed Option
|
||||||
|
func NewOptions(opts ...Option) Options {
|
||||||
|
options := Options{}
|
||||||
|
for _, o := range opts {
|
||||||
|
o(&options)
|
||||||
|
}
|
||||||
|
return options
|
||||||
|
}
|
@@ -12,10 +12,8 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type defaultLogger struct {
|
type defaultLogger struct {
|
||||||
enc *json.Encoder
|
enc *json.Encoder
|
||||||
logFunc LogFunc
|
opts Options
|
||||||
logfFunc LogfFunc
|
|
||||||
opts Options
|
|
||||||
sync.RWMutex
|
sync.RWMutex
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -27,10 +25,6 @@ func (l *defaultLogger) Init(opts ...Option) error {
|
|||||||
}
|
}
|
||||||
l.enc = json.NewEncoder(l.opts.Out)
|
l.enc = json.NewEncoder(l.opts.Out)
|
||||||
// wrap the Log func
|
// wrap the Log func
|
||||||
for i := len(l.opts.Wrappers); i > 0; i-- {
|
|
||||||
l.logFunc = l.opts.Wrappers[i-1].Log(l.logFunc)
|
|
||||||
l.logfFunc = l.opts.Wrappers[i-1].Logf(l.logfFunc)
|
|
||||||
}
|
|
||||||
l.Unlock()
|
l.Unlock()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -47,17 +41,10 @@ func (l *defaultLogger) Clone(opts ...Option) Logger {
|
|||||||
o(&oldopts)
|
o(&oldopts)
|
||||||
}
|
}
|
||||||
|
|
||||||
oldopts.Wrappers = newopts.Wrappers
|
|
||||||
l.Lock()
|
l.Lock()
|
||||||
cl := &defaultLogger{opts: oldopts, logFunc: l.logFunc, logfFunc: l.logfFunc, enc: json.NewEncoder(l.opts.Out)}
|
cl := &defaultLogger{opts: oldopts, enc: json.NewEncoder(l.opts.Out)}
|
||||||
l.Unlock()
|
l.Unlock()
|
||||||
|
|
||||||
// wrap the Log func
|
|
||||||
for i := len(newopts.Wrappers); i > 0; i-- {
|
|
||||||
cl.logFunc = newopts.Wrappers[i-1].Log(cl.logFunc)
|
|
||||||
cl.logfFunc = newopts.Wrappers[i-1].Logf(cl.logfFunc)
|
|
||||||
}
|
|
||||||
|
|
||||||
return cl
|
return cl
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -75,15 +62,17 @@ func (l *defaultLogger) Level(level Level) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (l *defaultLogger) Fields(fields ...interface{}) Logger {
|
func (l *defaultLogger) Fields(fields ...interface{}) Logger {
|
||||||
|
l.RLock()
|
||||||
nl := &defaultLogger{opts: l.opts, enc: l.enc}
|
nl := &defaultLogger{opts: l.opts, enc: l.enc}
|
||||||
if len(fields) == 0 {
|
if len(fields) == 0 {
|
||||||
|
l.RUnlock()
|
||||||
return nl
|
return nl
|
||||||
} else if len(fields)%2 != 0 {
|
} else if len(fields)%2 != 0 {
|
||||||
fields = fields[:len(fields)-1]
|
fields = fields[:len(fields)-1]
|
||||||
}
|
}
|
||||||
nl.logFunc = l.logFunc
|
nl.opts.Fields = copyFields(l.opts.Fields)
|
||||||
nl.logfFunc = l.logfFunc
|
|
||||||
nl.opts.Fields = append(nl.opts.Fields, fields...)
|
nl.opts.Fields = append(nl.opts.Fields, fields...)
|
||||||
|
l.RUnlock()
|
||||||
return nl
|
return nl
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -143,27 +132,27 @@ func (l *defaultLogger) Fatal(ctx context.Context, args ...interface{}) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (l *defaultLogger) Infof(ctx context.Context, msg string, args ...interface{}) {
|
func (l *defaultLogger) Infof(ctx context.Context, msg string, args ...interface{}) {
|
||||||
l.logfFunc(ctx, InfoLevel, msg, args...)
|
l.Logf(ctx, InfoLevel, msg, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *defaultLogger) Errorf(ctx context.Context, msg string, args ...interface{}) {
|
func (l *defaultLogger) Errorf(ctx context.Context, msg string, args ...interface{}) {
|
||||||
l.logfFunc(ctx, ErrorLevel, msg, args...)
|
l.Logf(ctx, ErrorLevel, msg, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *defaultLogger) Debugf(ctx context.Context, msg string, args ...interface{}) {
|
func (l *defaultLogger) Debugf(ctx context.Context, msg string, args ...interface{}) {
|
||||||
l.logfFunc(ctx, DebugLevel, msg, args...)
|
l.Logf(ctx, DebugLevel, msg, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *defaultLogger) Warnf(ctx context.Context, msg string, args ...interface{}) {
|
func (l *defaultLogger) Warnf(ctx context.Context, msg string, args ...interface{}) {
|
||||||
l.logfFunc(ctx, WarnLevel, msg, args...)
|
l.Logf(ctx, WarnLevel, msg, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *defaultLogger) Tracef(ctx context.Context, msg string, args ...interface{}) {
|
func (l *defaultLogger) Tracef(ctx context.Context, msg string, args ...interface{}) {
|
||||||
l.logfFunc(ctx, TraceLevel, msg, args...)
|
l.Logf(ctx, TraceLevel, msg, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *defaultLogger) Fatalf(ctx context.Context, msg string, args ...interface{}) {
|
func (l *defaultLogger) Fatalf(ctx context.Context, msg string, args ...interface{}) {
|
||||||
l.logfFunc(ctx, FatalLevel, msg, args...)
|
l.Logf(ctx, FatalLevel, msg, args...)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -236,8 +225,6 @@ func NewLogger(opts ...Option) Logger {
|
|||||||
l := &defaultLogger{
|
l := &defaultLogger{
|
||||||
opts: NewOptions(opts...),
|
opts: NewOptions(opts...),
|
||||||
}
|
}
|
||||||
l.logFunc = l.Log
|
|
||||||
l.logfFunc = l.Logf
|
|
||||||
l.enc = json.NewEncoder(l.opts.Out)
|
l.enc = json.NewEncoder(l.opts.Out)
|
||||||
return l
|
return l
|
||||||
}
|
}
|
||||||
|
@@ -32,7 +32,33 @@ func TestFields(t *testing.T) {
|
|||||||
if err := l.Init(); err != nil {
|
if err := l.Init(); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
l.Fields("key", "val").Info(ctx, "message")
|
|
||||||
|
nl := l.Fields("key", "val")
|
||||||
|
|
||||||
|
nl.Info(ctx, "message")
|
||||||
|
if !bytes.Contains(buf.Bytes(), []byte(`"key":"val"`)) {
|
||||||
|
t.Fatalf("logger fields not works, buf contains: %s", buf.Bytes())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFromContextWithFields(t *testing.T) {
|
||||||
|
ctx := context.TODO()
|
||||||
|
buf := bytes.NewBuffer(nil)
|
||||||
|
var ok bool
|
||||||
|
l := NewLogger(WithLevel(TraceLevel), WithOutput(buf))
|
||||||
|
if err := l.Init(); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
nl := l.Fields("key", "val")
|
||||||
|
|
||||||
|
ctx = NewContext(ctx, nl)
|
||||||
|
|
||||||
|
l, ok = FromContext(ctx)
|
||||||
|
if !ok {
|
||||||
|
t.Fatalf("context does not have logger")
|
||||||
|
}
|
||||||
|
|
||||||
|
l.Info(ctx, "message")
|
||||||
if !bytes.Contains(buf.Bytes(), []byte(`"key":"val"`)) {
|
if !bytes.Contains(buf.Bytes(), []byte(`"key":"val"`)) {
|
||||||
t.Fatalf("logger fields not works, buf contains: %s", buf.Bytes())
|
t.Fatalf("logger fields not works, buf contains: %s", buf.Bytes())
|
||||||
}
|
}
|
||||||
@@ -110,39 +136,3 @@ func TestLogger(t *testing.T) {
|
|||||||
t.Fatalf("logger error, buf %s", buf.Bytes())
|
t.Fatalf("logger error, buf %s", buf.Bytes())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestLoggerWrapper(t *testing.T) {
|
|
||||||
ctx := context.TODO()
|
|
||||||
buf := bytes.NewBuffer(nil)
|
|
||||||
l := NewLogger(WithLevel(TraceLevel), WithOutput(buf))
|
|
||||||
if err := l.Init(WrapLogger(NewOmitWrapper())); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
type secret struct {
|
|
||||||
Name string
|
|
||||||
Passw string `logger:"omit"`
|
|
||||||
}
|
|
||||||
s := &secret{Name: "name", Passw: "secret"}
|
|
||||||
l.Errorf(ctx, "test %#+v", s)
|
|
||||||
if !bytes.Contains(buf.Bytes(), []byte(`logger.secret{Name:\"name\", Passw:\"\"}"`)) {
|
|
||||||
t.Fatalf("omit not works, struct: %v, output: %s", s, buf.Bytes())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestOmitLoggerWrapper(t *testing.T) {
|
|
||||||
ctx := context.TODO()
|
|
||||||
buf := bytes.NewBuffer(nil)
|
|
||||||
l := NewOmitLogger(NewLogger(WithLevel(TraceLevel), WithOutput(buf)))
|
|
||||||
if err := l.Init(); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
type secret struct {
|
|
||||||
Name string
|
|
||||||
Passw string `logger:"omit"`
|
|
||||||
}
|
|
||||||
s := &secret{Name: "name", Passw: "secret"}
|
|
||||||
l.Errorf(ctx, "test %#+v", s)
|
|
||||||
if !bytes.Contains(buf.Bytes(), []byte(`logger.secret{Name:\"name\", Passw:\"\"}"`)) {
|
|
||||||
t.Fatalf("omit not works, struct: %v, output: %s", s, buf.Bytes())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@@ -19,8 +19,6 @@ type Options struct {
|
|||||||
Fields []interface{}
|
Fields []interface{}
|
||||||
// Name holds the logger name
|
// Name holds the logger name
|
||||||
Name string
|
Name string
|
||||||
// Wrappers logger wrapper that called before actual Log/Logf function
|
|
||||||
Wrappers []Wrapper
|
|
||||||
// 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
|
||||||
@@ -83,10 +81,3 @@ func WithName(n string) Option {
|
|||||||
o.Name = n
|
o.Name = n
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// WrapLogger adds a logger Wrapper to a list of options passed into the logger
|
|
||||||
func WrapLogger(w Wrapper) Option {
|
|
||||||
return func(o *Options) {
|
|
||||||
o.Wrappers = append(o.Wrappers, w)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@@ -46,13 +46,21 @@ var (
|
|||||||
closeMapBytes = []byte("}")
|
closeMapBytes = []byte("}")
|
||||||
)
|
)
|
||||||
|
|
||||||
type unwrap struct {
|
type protoMessage interface {
|
||||||
val interface{}
|
Reset()
|
||||||
s fmt.State
|
ProtoMessage()
|
||||||
pointers map[uintptr]int
|
}
|
||||||
opts *Options
|
|
||||||
depth int
|
type Wrapper struct {
|
||||||
ignoreNextType bool
|
val interface{}
|
||||||
|
s fmt.State
|
||||||
|
pointers map[uintptr]int
|
||||||
|
opts *Options
|
||||||
|
depth int
|
||||||
|
ignoreNextType bool
|
||||||
|
takeMap map[int]bool
|
||||||
|
protoWrapperType bool
|
||||||
|
sqlWrapperType bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// Options struct
|
// Options struct
|
||||||
@@ -106,14 +114,14 @@ func Tagged(b bool) Option {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func Unwrap(val interface{}, opts ...Option) *unwrap {
|
func Unwrap(val interface{}, opts ...Option) *Wrapper {
|
||||||
options := NewOptions(opts...)
|
options := NewOptions(opts...)
|
||||||
return &unwrap{val: val, opts: &options, pointers: make(map[uintptr]int)}
|
return &Wrapper{val: val, opts: &options, pointers: make(map[uintptr]int), takeMap: make(map[int]bool)}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *unwrap) unpackValue(v reflect.Value) reflect.Value {
|
func (w *Wrapper) unpackValue(v reflect.Value) reflect.Value {
|
||||||
if v.Kind() == reflect.Interface {
|
if v.Kind() == reflect.Interface {
|
||||||
f.ignoreNextType = false
|
w.ignoreNextType = false
|
||||||
if !v.IsNil() {
|
if !v.IsNil() {
|
||||||
v = v.Elem()
|
v = v.Elem()
|
||||||
}
|
}
|
||||||
@@ -122,19 +130,19 @@ func (f *unwrap) unpackValue(v reflect.Value) reflect.Value {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// formatPtr handles formatting of pointers by indirecting them as necessary.
|
// formatPtr handles formatting of pointers by indirecting them as necessary.
|
||||||
func (f *unwrap) formatPtr(v reflect.Value) {
|
func (w *Wrapper) formatPtr(v reflect.Value) {
|
||||||
// Display nil if top level pointer is nil.
|
// Display nil if top level pointer is nil.
|
||||||
showTypes := f.s.Flag('#')
|
showTypes := w.s.Flag('#')
|
||||||
if v.IsNil() && (!showTypes || f.ignoreNextType) {
|
if v.IsNil() && (!showTypes || w.ignoreNextType) {
|
||||||
_, _ = f.s.Write(nilAngleBytes)
|
_, _ = w.s.Write(nilAngleBytes)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove pointers at or below the current depth from map used to detect
|
// Remove pointers at or below the current depth from map used to detect
|
||||||
// circular refs.
|
// circular refs.
|
||||||
for k, depth := range f.pointers {
|
for k, depth := range w.pointers {
|
||||||
if depth >= f.depth {
|
if depth >= w.depth {
|
||||||
delete(f.pointers, k)
|
delete(w.pointers, k)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -156,12 +164,12 @@ func (f *unwrap) formatPtr(v reflect.Value) {
|
|||||||
indirects++
|
indirects++
|
||||||
addr := ve.Pointer()
|
addr := ve.Pointer()
|
||||||
pointerChain = append(pointerChain, addr)
|
pointerChain = append(pointerChain, addr)
|
||||||
if pd, ok := f.pointers[addr]; ok && pd < f.depth {
|
if pd, ok := w.pointers[addr]; ok && pd < w.depth {
|
||||||
cycleFound = true
|
cycleFound = true
|
||||||
indirects--
|
indirects--
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
f.pointers[addr] = f.depth
|
w.pointers[addr] = w.depth
|
||||||
|
|
||||||
ve = ve.Elem()
|
ve = ve.Elem()
|
||||||
if ve.Kind() == reflect.Interface {
|
if ve.Kind() == reflect.Interface {
|
||||||
@@ -174,49 +182,49 @@ func (f *unwrap) formatPtr(v reflect.Value) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Display type or indirection level depending on flags.
|
// Display type or indirection level depending on flags.
|
||||||
if showTypes && !f.ignoreNextType {
|
if showTypes && !w.ignoreNextType {
|
||||||
if f.depth > 0 {
|
if w.depth > 0 {
|
||||||
_, _ = f.s.Write(openParenBytes)
|
_, _ = w.s.Write(openParenBytes)
|
||||||
}
|
}
|
||||||
if f.depth > 0 {
|
if w.depth > 0 {
|
||||||
_, _ = f.s.Write(bytes.Repeat(asteriskBytes, indirects))
|
_, _ = w.s.Write(bytes.Repeat(asteriskBytes, indirects))
|
||||||
} else {
|
} else {
|
||||||
_, _ = f.s.Write(bytes.Repeat(ampBytes, indirects))
|
_, _ = w.s.Write(bytes.Repeat(ampBytes, indirects))
|
||||||
}
|
}
|
||||||
_, _ = f.s.Write([]byte(ve.Type().String()))
|
_, _ = w.s.Write([]byte(ve.Type().String()))
|
||||||
if f.depth > 0 {
|
if w.depth > 0 {
|
||||||
_, _ = f.s.Write(closeParenBytes)
|
_, _ = w.s.Write(closeParenBytes)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if nilFound || cycleFound {
|
if nilFound || cycleFound {
|
||||||
indirects += strings.Count(ve.Type().String(), "*")
|
indirects += strings.Count(ve.Type().String(), "*")
|
||||||
}
|
}
|
||||||
_, _ = f.s.Write(openAngleBytes)
|
_, _ = w.s.Write(openAngleBytes)
|
||||||
_, _ = f.s.Write([]byte(strings.Repeat("*", indirects)))
|
_, _ = w.s.Write([]byte(strings.Repeat("*", indirects)))
|
||||||
_, _ = f.s.Write(closeAngleBytes)
|
_, _ = w.s.Write(closeAngleBytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Display pointer information depending on flags.
|
// Display pointer information depending on flags.
|
||||||
if f.s.Flag('+') && (len(pointerChain) > 0) {
|
if w.s.Flag('+') && (len(pointerChain) > 0) {
|
||||||
_, _ = f.s.Write(openParenBytes)
|
_, _ = w.s.Write(openParenBytes)
|
||||||
for i, addr := range pointerChain {
|
for i, addr := range pointerChain {
|
||||||
if i > 0 {
|
if i > 0 {
|
||||||
_, _ = f.s.Write(pointerChainBytes)
|
_, _ = w.s.Write(pointerChainBytes)
|
||||||
}
|
}
|
||||||
getHexPtr(f.s, addr)
|
getHexPtr(w.s, addr)
|
||||||
}
|
}
|
||||||
_, _ = f.s.Write(closeParenBytes)
|
_, _ = w.s.Write(closeParenBytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Display dereferenced value.
|
// Display dereferenced value.
|
||||||
switch {
|
switch {
|
||||||
case nilFound:
|
case nilFound:
|
||||||
_, _ = f.s.Write(nilAngleBytes)
|
_, _ = w.s.Write(nilAngleBytes)
|
||||||
case cycleFound:
|
case cycleFound:
|
||||||
_, _ = f.s.Write(circularShortBytes)
|
_, _ = w.s.Write(circularShortBytes)
|
||||||
default:
|
default:
|
||||||
f.ignoreNextType = true
|
w.ignoreNextType = true
|
||||||
f.format(ve)
|
w.format(ve)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -224,54 +232,73 @@ func (f *unwrap) formatPtr(v reflect.Value) {
|
|||||||
// uses the passed reflect value to figure out what kind of object we are
|
// uses the passed reflect value to figure out what kind of object we are
|
||||||
// dealing with and formats it appropriately. It is a recursive function,
|
// dealing with and formats it appropriately. It is a recursive function,
|
||||||
// however circular data structures are detected and handled properly.
|
// however circular data structures are detected and handled properly.
|
||||||
func (f *unwrap) format(v reflect.Value) {
|
func (w *Wrapper) format(v reflect.Value) {
|
||||||
if f.opts.Codec != nil {
|
if w.opts.Codec != nil {
|
||||||
buf, err := f.opts.Codec.Marshal(v.Interface())
|
buf, err := w.opts.Codec.Marshal(v.Interface())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
_, _ = f.s.Write(invalidAngleBytes)
|
_, _ = w.s.Write(invalidAngleBytes)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
_, _ = f.s.Write(buf)
|
_, _ = w.s.Write(buf)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle invalid reflect values immediately.
|
// Handle invalid reflect values immediately.
|
||||||
kind := v.Kind()
|
kind := v.Kind()
|
||||||
if kind == reflect.Invalid {
|
if kind == reflect.Invalid {
|
||||||
_, _ = f.s.Write(invalidAngleBytes)
|
_, _ = w.s.Write(invalidAngleBytes)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle pointers specially.
|
// Handle pointers specially.
|
||||||
if kind == reflect.Ptr {
|
switch kind {
|
||||||
f.formatPtr(v)
|
case reflect.Ptr:
|
||||||
|
if !v.IsZero() {
|
||||||
|
if strings.HasPrefix(reflect.Indirect(v).Type().String(), "wrapperspb.") {
|
||||||
|
w.protoWrapperType = true
|
||||||
|
} else if strings.HasPrefix(reflect.Indirect(v).Type().String(), "sql.Null") {
|
||||||
|
w.sqlWrapperType = true
|
||||||
|
} else if v.CanInterface() {
|
||||||
|
if _, ok := v.Interface().(protoMessage); ok {
|
||||||
|
w.protoWrapperType = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
w.formatPtr(v)
|
||||||
return
|
return
|
||||||
|
case reflect.Struct:
|
||||||
|
if !v.IsZero() {
|
||||||
|
if strings.HasPrefix(reflect.Indirect(v).Type().String(), "sql.Null") {
|
||||||
|
w.sqlWrapperType = true
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// get type information unless already handled elsewhere.
|
// get type information unless already handled elsewhere.
|
||||||
if !f.ignoreNextType && f.s.Flag('#') {
|
if !w.ignoreNextType && w.s.Flag('#') {
|
||||||
if v.Type().Kind() != reflect.Map &&
|
if v.Type().Kind() != reflect.Map &&
|
||||||
v.Type().Kind() != reflect.String &&
|
v.Type().Kind() != reflect.String &&
|
||||||
v.Type().Kind() != reflect.Array &&
|
v.Type().Kind() != reflect.Array &&
|
||||||
v.Type().Kind() != reflect.Slice {
|
v.Type().Kind() != reflect.Slice {
|
||||||
_, _ = f.s.Write(openParenBytes)
|
_, _ = w.s.Write(openParenBytes)
|
||||||
}
|
}
|
||||||
if v.Kind() != reflect.String {
|
if v.Kind() != reflect.String {
|
||||||
_, _ = f.s.Write([]byte(v.Type().String()))
|
_, _ = w.s.Write([]byte(v.Type().String()))
|
||||||
}
|
}
|
||||||
if v.Type().Kind() != reflect.Map &&
|
if v.Type().Kind() != reflect.Map &&
|
||||||
v.Type().Kind() != reflect.String &&
|
v.Type().Kind() != reflect.String &&
|
||||||
v.Type().Kind() != reflect.Array &&
|
v.Type().Kind() != reflect.Array &&
|
||||||
v.Type().Kind() != reflect.Slice {
|
v.Type().Kind() != reflect.Slice {
|
||||||
_, _ = f.s.Write(closeParenBytes)
|
_, _ = w.s.Write(closeParenBytes)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
f.ignoreNextType = false
|
w.ignoreNextType = false
|
||||||
|
|
||||||
// Call Stringer/error interfaces if they exist and the handle methods
|
// Call Stringer/error interfaces if they exist and the handle methods
|
||||||
// flag is enabled.
|
// flag is enabled.
|
||||||
if f.opts.Methods {
|
if w.opts.Methods {
|
||||||
if (kind != reflect.Invalid) && (kind != reflect.Interface) {
|
if (kind != reflect.Invalid) && (kind != reflect.Interface) {
|
||||||
if handled := handleMethods(f.opts, f.s, v); handled {
|
if handled := handleMethods(w.opts, w.s, v); handled {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -279,48 +306,48 @@ func (f *unwrap) format(v reflect.Value) {
|
|||||||
|
|
||||||
switch kind {
|
switch kind {
|
||||||
case reflect.Invalid:
|
case reflect.Invalid:
|
||||||
_, _ = f.s.Write(invalidAngleBytes)
|
_, _ = w.s.Write(invalidAngleBytes)
|
||||||
case reflect.Bool:
|
case reflect.Bool:
|
||||||
getBool(f.s, v.Bool())
|
getBool(w.s, v.Bool())
|
||||||
case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int:
|
case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int:
|
||||||
getInt(f.s, v.Int(), 10)
|
getInt(w.s, v.Int(), 10)
|
||||||
case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint:
|
case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint:
|
||||||
getUint(f.s, v.Uint(), 10)
|
getUint(w.s, v.Uint(), 10)
|
||||||
case reflect.Float32:
|
case reflect.Float32:
|
||||||
getFloat(f.s, v.Float(), 32)
|
getFloat(w.s, v.Float(), 32)
|
||||||
case reflect.Float64:
|
case reflect.Float64:
|
||||||
getFloat(f.s, v.Float(), 64)
|
getFloat(w.s, v.Float(), 64)
|
||||||
case reflect.Complex64:
|
case reflect.Complex64:
|
||||||
getComplex(f.s, v.Complex(), 32)
|
getComplex(w.s, v.Complex(), 32)
|
||||||
case reflect.Complex128:
|
case reflect.Complex128:
|
||||||
getComplex(f.s, v.Complex(), 64)
|
getComplex(w.s, v.Complex(), 64)
|
||||||
case reflect.Slice:
|
case reflect.Slice:
|
||||||
if v.IsNil() {
|
if v.IsNil() {
|
||||||
_, _ = f.s.Write(nilAngleBytes)
|
_, _ = w.s.Write(nilAngleBytes)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
fallthrough
|
fallthrough
|
||||||
case reflect.Array:
|
case reflect.Array:
|
||||||
_, _ = f.s.Write(openBraceBytes)
|
_, _ = w.s.Write(openBraceBytes)
|
||||||
f.depth++
|
w.depth++
|
||||||
numEntries := v.Len()
|
numEntries := v.Len()
|
||||||
for i := 0; i < numEntries; i++ {
|
for i := 0; i < numEntries; i++ {
|
||||||
if i > 0 {
|
if i > 0 {
|
||||||
_, _ = f.s.Write(commaBytes)
|
_, _ = w.s.Write(commaBytes)
|
||||||
_, _ = f.s.Write(spaceBytes)
|
_, _ = w.s.Write(spaceBytes)
|
||||||
}
|
}
|
||||||
f.ignoreNextType = true
|
w.ignoreNextType = true
|
||||||
f.format(f.unpackValue(v.Index(i)))
|
w.format(w.unpackValue(v.Index(i)))
|
||||||
}
|
}
|
||||||
f.depth--
|
w.depth--
|
||||||
_, _ = f.s.Write(closeBraceBytes)
|
_, _ = w.s.Write(closeBraceBytes)
|
||||||
case reflect.String:
|
case reflect.String:
|
||||||
_, _ = f.s.Write([]byte(`"` + v.String() + `"`))
|
_, _ = w.s.Write([]byte(`"` + v.String() + `"`))
|
||||||
case reflect.Interface:
|
case reflect.Interface:
|
||||||
// The only time we should get here is for nil interfaces due to
|
// The only time we should get here is for nil interfaces due to
|
||||||
// unpackValue calls.
|
// unpackValue calls.
|
||||||
if v.IsNil() {
|
if v.IsNil() {
|
||||||
_, _ = f.s.Write(nilAngleBytes)
|
_, _ = w.s.Write(nilAngleBytes)
|
||||||
}
|
}
|
||||||
case reflect.Ptr:
|
case reflect.Ptr:
|
||||||
// Do nothing. We should never get here since pointers have already
|
// Do nothing. We should never get here since pointers have already
|
||||||
@@ -328,34 +355,54 @@ func (f *unwrap) format(v reflect.Value) {
|
|||||||
case reflect.Map:
|
case reflect.Map:
|
||||||
// nil maps should be indicated as different than empty maps
|
// nil maps should be indicated as different than empty maps
|
||||||
if v.IsNil() {
|
if v.IsNil() {
|
||||||
_, _ = f.s.Write(nilAngleBytes)
|
_, _ = w.s.Write(nilAngleBytes)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
_, _ = f.s.Write(openMapBytes)
|
_, _ = w.s.Write(openMapBytes)
|
||||||
f.depth++
|
w.depth++
|
||||||
keys := v.MapKeys()
|
keys := v.MapKeys()
|
||||||
for i, key := range keys {
|
for i, key := range keys {
|
||||||
if i > 0 {
|
if i > 0 {
|
||||||
_, _ = f.s.Write(spaceBytes)
|
_, _ = w.s.Write(spaceBytes)
|
||||||
}
|
}
|
||||||
f.ignoreNextType = true
|
w.ignoreNextType = true
|
||||||
f.format(f.unpackValue(key))
|
w.format(w.unpackValue(key))
|
||||||
_, _ = f.s.Write(colonBytes)
|
_, _ = w.s.Write(colonBytes)
|
||||||
f.ignoreNextType = true
|
w.ignoreNextType = true
|
||||||
f.format(f.unpackValue(v.MapIndex(key)))
|
w.format(w.unpackValue(v.MapIndex(key)))
|
||||||
}
|
}
|
||||||
f.depth--
|
w.depth--
|
||||||
_, _ = f.s.Write(closeMapBytes)
|
_, _ = w.s.Write(closeMapBytes)
|
||||||
case reflect.Struct:
|
case reflect.Struct:
|
||||||
|
|
||||||
numFields := v.NumField()
|
numFields := v.NumField()
|
||||||
numWritten := 0
|
numWritten := 0
|
||||||
_, _ = f.s.Write(openBraceBytes)
|
_, _ = w.s.Write(openBraceBytes)
|
||||||
f.depth++
|
w.depth++
|
||||||
|
|
||||||
vt := v.Type()
|
vt := v.Type()
|
||||||
prevSkip := false
|
prevSkip := false
|
||||||
|
|
||||||
for i := 0; i < numFields; i++ {
|
for i := 0; i < numFields; i++ {
|
||||||
|
switch vt.Field(i).Type.PkgPath() {
|
||||||
|
case "google.golang.org/protobuf/internal/impl", "google.golang.org/protobuf/internal/pragma":
|
||||||
|
w.protoWrapperType = true
|
||||||
|
prevSkip = true
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if w.protoWrapperType && !vt.Field(i).IsExported() {
|
||||||
|
prevSkip = true
|
||||||
|
continue
|
||||||
|
} else if w.sqlWrapperType && vt.Field(i).Name == "Valid" {
|
||||||
|
prevSkip = true
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if _, ok := vt.Field(i).Tag.Lookup("protobuf"); ok && !w.protoWrapperType {
|
||||||
|
w.protoWrapperType = true
|
||||||
|
}
|
||||||
sv, ok := vt.Field(i).Tag.Lookup("logger")
|
sv, ok := vt.Field(i).Tag.Lookup("logger")
|
||||||
if ok {
|
switch {
|
||||||
|
case ok:
|
||||||
switch sv {
|
switch sv {
|
||||||
case "omit":
|
case "omit":
|
||||||
prevSkip = true
|
prevSkip = true
|
||||||
@@ -363,57 +410,68 @@ func (f *unwrap) format(v reflect.Value) {
|
|||||||
case "take":
|
case "take":
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
} else if f.opts.Tagged {
|
case !ok && w.opts.Tagged:
|
||||||
prevSkip = true
|
// skip top level untagged
|
||||||
continue
|
if w.depth == 1 {
|
||||||
}
|
prevSkip = true
|
||||||
if i > 0 && !prevSkip {
|
continue
|
||||||
_, _ = f.s.Write(commaBytes)
|
}
|
||||||
_, _ = f.s.Write(spaceBytes)
|
if tv, ok := w.takeMap[w.depth]; ok && !tv {
|
||||||
|
prevSkip = true
|
||||||
|
continue
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if prevSkip {
|
if prevSkip {
|
||||||
prevSkip = false
|
prevSkip = false
|
||||||
}
|
}
|
||||||
vtf := vt.Field(i)
|
|
||||||
if f.s.Flag('+') || f.s.Flag('#') {
|
if numWritten > 0 {
|
||||||
_, _ = f.s.Write([]byte(vtf.Name))
|
_, _ = w.s.Write(commaBytes)
|
||||||
_, _ = f.s.Write(colonBytes)
|
_, _ = w.s.Write(spaceBytes)
|
||||||
}
|
}
|
||||||
f.format(f.unpackValue(v.Field(i)))
|
|
||||||
|
vt := vt.Field(i)
|
||||||
|
if w.s.Flag('+') || w.s.Flag('#') {
|
||||||
|
_, _ = w.s.Write([]byte(vt.Name))
|
||||||
|
_, _ = w.s.Write(colonBytes)
|
||||||
|
}
|
||||||
|
w.format(w.unpackValue(v.Field(i)))
|
||||||
numWritten++
|
numWritten++
|
||||||
}
|
}
|
||||||
f.depth--
|
w.depth--
|
||||||
if numWritten == 0 && f.depth < 0 {
|
|
||||||
_, _ = f.s.Write(filteredBytes)
|
if numWritten == 0 && w.depth < 0 {
|
||||||
|
_, _ = w.s.Write(filteredBytes)
|
||||||
}
|
}
|
||||||
_, _ = f.s.Write(closeBraceBytes)
|
_, _ = w.s.Write(closeBraceBytes)
|
||||||
case reflect.Uintptr:
|
case reflect.Uintptr:
|
||||||
getHexPtr(f.s, uintptr(v.Uint()))
|
getHexPtr(w.s, uintptr(v.Uint()))
|
||||||
case reflect.UnsafePointer, reflect.Chan, reflect.Func:
|
case reflect.UnsafePointer, reflect.Chan, reflect.Func:
|
||||||
getHexPtr(f.s, v.Pointer())
|
getHexPtr(w.s, v.Pointer())
|
||||||
// There were not any other types at the time this code was written, but
|
// There were not any other types at the time this code was written, but
|
||||||
// fall back to letting the default fmt package handle it if any get added.
|
// fall back to letting the default fmt package handle it if any get added.
|
||||||
default:
|
default:
|
||||||
format := f.buildDefaultFormat()
|
format := w.buildDefaultFormat()
|
||||||
if v.CanInterface() {
|
if v.CanInterface() {
|
||||||
_, _ = fmt.Fprintf(f.s, format, v.Interface())
|
_, _ = fmt.Fprintf(w.s, format, v.Interface())
|
||||||
} else {
|
} else {
|
||||||
_, _ = fmt.Fprintf(f.s, format, v.String())
|
_, _ = fmt.Fprintf(w.s, format, v.String())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *unwrap) Format(s fmt.State, verb rune) {
|
func (w *Wrapper) Format(s fmt.State, verb rune) {
|
||||||
f.s = s
|
w.s = s
|
||||||
|
|
||||||
// Use standard formatting for verbs that are not v.
|
// Use standard formatting for verbs that are not v.
|
||||||
if verb != 'v' {
|
if verb != 'v' {
|
||||||
format := f.constructOrigFormat(verb)
|
format := w.constructOrigFormat(verb)
|
||||||
_, _ = fmt.Fprintf(s, format, f.val)
|
_, _ = fmt.Fprintf(s, format, w.val)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if f.val == nil {
|
if w.val == nil {
|
||||||
if s.Flag('#') {
|
if s.Flag('#') {
|
||||||
_, _ = s.Write(interfaceBytes)
|
_, _ = s.Write(interfaceBytes)
|
||||||
}
|
}
|
||||||
@@ -421,7 +479,11 @@ func (f *unwrap) Format(s fmt.State, verb rune) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
f.format(reflect.ValueOf(f.val))
|
if w.opts.Tagged {
|
||||||
|
w.buildTakeMap(reflect.ValueOf(w.val), 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
w.format(reflect.ValueOf(w.val))
|
||||||
}
|
}
|
||||||
|
|
||||||
// handle special methods like error.Error() or fmt.Stringer interface
|
// handle special methods like error.Error() or fmt.Stringer interface
|
||||||
@@ -537,11 +599,11 @@ func catchPanic(w io.Writer, _ reflect.Value) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *unwrap) buildDefaultFormat() (format string) {
|
func (w *Wrapper) buildDefaultFormat() (format string) {
|
||||||
buf := bytes.NewBuffer(percentBytes)
|
buf := bytes.NewBuffer(percentBytes)
|
||||||
|
|
||||||
for _, flag := range sf {
|
for _, flag := range sf {
|
||||||
if f.s.Flag(int(flag)) {
|
if w.s.Flag(int(flag)) {
|
||||||
_, _ = buf.WriteRune(flag)
|
_, _ = buf.WriteRune(flag)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -552,26 +614,68 @@ func (f *unwrap) buildDefaultFormat() (format string) {
|
|||||||
return format
|
return format
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *unwrap) constructOrigFormat(verb rune) (format string) {
|
func (w *Wrapper) constructOrigFormat(verb rune) string {
|
||||||
buf := bytes.NewBuffer(percentBytes)
|
buf := bytes.NewBuffer(percentBytes)
|
||||||
|
|
||||||
for _, flag := range sf {
|
for _, flag := range sf {
|
||||||
if f.s.Flag(int(flag)) {
|
if w.s.Flag(int(flag)) {
|
||||||
_, _ = buf.WriteRune(flag)
|
_, _ = buf.WriteRune(flag)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if width, ok := f.s.Width(); ok {
|
if width, ok := w.s.Width(); ok {
|
||||||
_, _ = buf.WriteString(strconv.Itoa(width))
|
_, _ = buf.WriteString(strconv.Itoa(width))
|
||||||
}
|
}
|
||||||
|
|
||||||
if precision, ok := f.s.Precision(); ok {
|
if precision, ok := w.s.Precision(); ok {
|
||||||
_, _ = buf.Write(precisionBytes)
|
_, _ = buf.Write(precisionBytes)
|
||||||
_, _ = buf.WriteString(strconv.Itoa(precision))
|
_, _ = buf.WriteString(strconv.Itoa(precision))
|
||||||
}
|
}
|
||||||
|
|
||||||
_, _ = buf.WriteRune(verb)
|
_, _ = buf.WriteRune(verb)
|
||||||
|
|
||||||
format = buf.String()
|
return buf.String()
|
||||||
return format
|
}
|
||||||
|
|
||||||
|
func (w *Wrapper) buildTakeMap(v reflect.Value, depth int) {
|
||||||
|
if !v.IsValid() || v.IsZero() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
switch v.Kind() {
|
||||||
|
case reflect.Slice, reflect.Array:
|
||||||
|
for i := 0; i < v.Len(); i++ {
|
||||||
|
w.buildTakeMap(v.Index(i), depth+1)
|
||||||
|
}
|
||||||
|
w.takeMap[depth] = true
|
||||||
|
return
|
||||||
|
case reflect.Struct:
|
||||||
|
break
|
||||||
|
case reflect.Ptr:
|
||||||
|
v = v.Elem()
|
||||||
|
if v.Kind() != reflect.Struct {
|
||||||
|
w.takeMap[depth] = true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
w.takeMap[depth] = true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
vt := v.Type()
|
||||||
|
|
||||||
|
for i := 0; i < v.NumField(); i++ {
|
||||||
|
sv, ok := vt.Field(i).Tag.Lookup("logger")
|
||||||
|
if ok && sv == "take" {
|
||||||
|
w.takeMap[depth] = false
|
||||||
|
}
|
||||||
|
if v.Kind() == reflect.Struct ||
|
||||||
|
(v.Kind() == reflect.Ptr && v.Elem().Kind() == reflect.Struct) {
|
||||||
|
w.buildTakeMap(v.Field(i), depth+1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := w.takeMap[depth]; !ok {
|
||||||
|
w.takeMap[depth] = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -86,12 +86,12 @@ func TestTaggedNested(t *testing.T) {
|
|||||||
unk string
|
unk string
|
||||||
}
|
}
|
||||||
type str struct {
|
type str struct {
|
||||||
val *val `logger:"take"`
|
|
||||||
key string `logger:"omit"`
|
key string `logger:"omit"`
|
||||||
|
val *val `logger:"take"`
|
||||||
}
|
}
|
||||||
|
|
||||||
var iface interface{}
|
var iface interface{}
|
||||||
v := &str{key: "omit", val: &val{key: "test", val: "omit", unk: "unk"}}
|
v := &str{val: &val{key: "test", unk: "unk"}}
|
||||||
iface = v
|
iface = v
|
||||||
buf := fmt.Sprintf("%#v", Unwrap(iface, Tagged(true)))
|
buf := fmt.Sprintf("%#v", Unwrap(iface, Tagged(true)))
|
||||||
if strings.Compare(buf, `&unwrap.str{val:(*unwrap.val){key:"test"}}`) != 0 {
|
if strings.Compare(buf, `&unwrap.str{val:(*unwrap.val){key:"test"}}`) != 0 {
|
||||||
|
@@ -1,166 +0,0 @@
|
|||||||
package logger // import "go.unistack.org/micro/v3/logger/wrapper"
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"reflect"
|
|
||||||
|
|
||||||
rutil "go.unistack.org/micro/v3/util/reflect"
|
|
||||||
)
|
|
||||||
|
|
||||||
// LogFunc function used for Log method
|
|
||||||
type LogFunc func(ctx context.Context, level Level, args ...interface{})
|
|
||||||
|
|
||||||
// LogfFunc function used for Logf method
|
|
||||||
type LogfFunc func(ctx context.Context, level Level, msg string, args ...interface{})
|
|
||||||
|
|
||||||
type Wrapper interface {
|
|
||||||
// Log logs message with needed level
|
|
||||||
Log(LogFunc) LogFunc
|
|
||||||
// Logf logs message with needed level
|
|
||||||
Logf(LogfFunc) LogfFunc
|
|
||||||
}
|
|
||||||
|
|
||||||
var _ Logger = &omitLogger{}
|
|
||||||
|
|
||||||
type omitLogger struct {
|
|
||||||
l Logger
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewOmitLogger(l Logger) Logger {
|
|
||||||
return &omitLogger{l: l}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *omitLogger) Init(opts ...Option) error {
|
|
||||||
return w.l.Init(append(opts, WrapLogger(NewOmitWrapper()))...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *omitLogger) V(level Level) bool {
|
|
||||||
return w.l.V(level)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *omitLogger) Level(level Level) {
|
|
||||||
w.l.Level(level)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *omitLogger) Clone(opts ...Option) Logger {
|
|
||||||
return w.l.Clone(opts...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *omitLogger) Options() Options {
|
|
||||||
return w.l.Options()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *omitLogger) Fields(fields ...interface{}) Logger {
|
|
||||||
return w.l.Fields(fields...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *omitLogger) Info(ctx context.Context, args ...interface{}) {
|
|
||||||
w.l.Info(ctx, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *omitLogger) Trace(ctx context.Context, args ...interface{}) {
|
|
||||||
w.l.Trace(ctx, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *omitLogger) Debug(ctx context.Context, args ...interface{}) {
|
|
||||||
w.l.Debug(ctx, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *omitLogger) Warn(ctx context.Context, args ...interface{}) {
|
|
||||||
w.l.Warn(ctx, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *omitLogger) Error(ctx context.Context, args ...interface{}) {
|
|
||||||
w.l.Error(ctx, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *omitLogger) Fatal(ctx context.Context, args ...interface{}) {
|
|
||||||
w.l.Fatal(ctx, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *omitLogger) Infof(ctx context.Context, msg string, args ...interface{}) {
|
|
||||||
w.l.Infof(ctx, msg, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *omitLogger) Tracef(ctx context.Context, msg string, args ...interface{}) {
|
|
||||||
w.l.Tracef(ctx, msg, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *omitLogger) Debugf(ctx context.Context, msg string, args ...interface{}) {
|
|
||||||
w.l.Debugf(ctx, msg, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *omitLogger) Warnf(ctx context.Context, msg string, args ...interface{}) {
|
|
||||||
w.l.Warnf(ctx, msg, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *omitLogger) Errorf(ctx context.Context, msg string, args ...interface{}) {
|
|
||||||
w.l.Errorf(ctx, msg, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *omitLogger) Fatalf(ctx context.Context, msg string, args ...interface{}) {
|
|
||||||
w.l.Fatalf(ctx, msg, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *omitLogger) Log(ctx context.Context, level Level, args ...interface{}) {
|
|
||||||
w.l.Log(ctx, level, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *omitLogger) Logf(ctx context.Context, level Level, msg string, args ...interface{}) {
|
|
||||||
w.l.Logf(ctx, level, msg, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *omitLogger) String() string {
|
|
||||||
return w.l.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
type omitWrapper struct{}
|
|
||||||
|
|
||||||
func NewOmitWrapper() Wrapper {
|
|
||||||
return &omitWrapper{}
|
|
||||||
}
|
|
||||||
|
|
||||||
func getArgs(args []interface{}) []interface{} {
|
|
||||||
nargs := make([]interface{}, 0, len(args))
|
|
||||||
var err error
|
|
||||||
for _, arg := range args {
|
|
||||||
val := reflect.ValueOf(arg)
|
|
||||||
if val.Kind() == reflect.Ptr {
|
|
||||||
val = val.Elem()
|
|
||||||
}
|
|
||||||
narg := arg
|
|
||||||
if val.Kind() != reflect.Struct {
|
|
||||||
nargs = append(nargs, narg)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if narg, err = rutil.Zero(arg); err != nil {
|
|
||||||
nargs = append(nargs, narg)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
rutil.CopyDefaults(narg, arg)
|
|
||||||
if flds, ferr := rutil.StructFields(narg); ferr == nil {
|
|
||||||
for _, fld := range flds {
|
|
||||||
if tv, ok := fld.Field.Tag.Lookup("logger"); ok && tv == "omit" {
|
|
||||||
fld.Value.Set(reflect.Zero(fld.Value.Type()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
nargs = append(nargs, narg)
|
|
||||||
}
|
|
||||||
return nargs
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *omitWrapper) Log(fn LogFunc) LogFunc {
|
|
||||||
return func(ctx context.Context, level Level, args ...interface{}) {
|
|
||||||
fn(ctx, level, getArgs(args)...)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *omitWrapper) Logf(fn LogfFunc) LogfFunc {
|
|
||||||
return func(ctx context.Context, level Level, msg string, args ...interface{}) {
|
|
||||||
fn(ctx, level, msg, getArgs(args)...)
|
|
||||||
}
|
|
||||||
}
|
|
@@ -124,7 +124,7 @@ func AppendOutgoingContext(ctx context.Context, kv ...string) context.Context {
|
|||||||
return NewOutgoingContext(ctx, md)
|
return NewOutgoingContext(ctx, md)
|
||||||
}
|
}
|
||||||
for k, v := range md {
|
for k, v := range md {
|
||||||
omd.Set(k, v)
|
omd[k] = v
|
||||||
}
|
}
|
||||||
return NewOutgoingContext(ctx, omd)
|
return NewOutgoingContext(ctx, omd)
|
||||||
}
|
}
|
||||||
@@ -140,7 +140,7 @@ func AppendIncomingContext(ctx context.Context, kv ...string) context.Context {
|
|||||||
return NewIncomingContext(ctx, md)
|
return NewIncomingContext(ctx, md)
|
||||||
}
|
}
|
||||||
for k, v := range md {
|
for k, v := range md {
|
||||||
omd.Set(k, v)
|
omd[k] = v
|
||||||
}
|
}
|
||||||
return NewIncomingContext(ctx, omd)
|
return NewIncomingContext(ctx, omd)
|
||||||
}
|
}
|
||||||
|
@@ -24,7 +24,7 @@ var (
|
|||||||
// Metadata is our way of representing request headers internally.
|
// Metadata is our way of representing request headers internally.
|
||||||
// They're used at the RPC level and translate back and forth
|
// They're used at the RPC level and translate back and forth
|
||||||
// from Transport headers.
|
// from Transport headers.
|
||||||
type Metadata map[string]string
|
type Metadata map[string][]string
|
||||||
|
|
||||||
type rawMetadata struct {
|
type rawMetadata struct {
|
||||||
md Metadata
|
md Metadata
|
||||||
@@ -42,7 +42,7 @@ type Iterator struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Next advance iterator to next element
|
// Next advance iterator to next element
|
||||||
func (iter *Iterator) Next(k, v *string) bool {
|
func (iter *Iterator) Next(k *string, v *[]string) bool {
|
||||||
if iter.cur+1 > iter.cnt {
|
if iter.cur+1 > iter.cnt {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@@ -64,8 +64,11 @@ func (md Metadata) Iterator() *Iterator {
|
|||||||
return iter
|
return iter
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get returns value from metadata by key
|
// Values returns values from metadata by key
|
||||||
func (md Metadata) Get(key string) (string, bool) {
|
func (md Metadata) Values(key string) ([]string, bool) {
|
||||||
|
if md == nil {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
// fast path
|
// fast path
|
||||||
val, ok := md[key]
|
val, ok := md[key]
|
||||||
if !ok {
|
if !ok {
|
||||||
@@ -75,16 +78,42 @@ func (md Metadata) Get(key string) (string, bool) {
|
|||||||
return val, ok
|
return val, ok
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get returns value from metadata by key
|
||||||
|
func (md Metadata) Get(key string) (string, bool) {
|
||||||
|
if md == nil {
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
// fast path
|
||||||
|
val, ok := md[key]
|
||||||
|
if !ok {
|
||||||
|
// slow path
|
||||||
|
val, ok = md[textproto.CanonicalMIMEHeaderKey(key)]
|
||||||
|
}
|
||||||
|
if len(val) == 0 {
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
return val[0], ok
|
||||||
|
}
|
||||||
|
|
||||||
// Set is used to store value in metadata
|
// Set is used to store value in metadata
|
||||||
func (md Metadata) Set(kv ...string) {
|
func (md Metadata) Set(kv ...string) {
|
||||||
|
if md == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
if len(kv)%2 == 1 {
|
if len(kv)%2 == 1 {
|
||||||
kv = kv[:len(kv)-1]
|
kv = kv[:len(kv)-1]
|
||||||
}
|
}
|
||||||
for idx := 0; idx < len(kv); idx += 2 {
|
for idx := 0; idx < len(kv); idx += 2 {
|
||||||
md[textproto.CanonicalMIMEHeaderKey(kv[idx])] = kv[idx+1]
|
md[textproto.CanonicalMIMEHeaderKey(kv[idx])] = []string{kv[idx+1]}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add is used to append value in metadata
|
||||||
|
func (md Metadata) Add(k string, v string) {
|
||||||
|
kn := textproto.CanonicalMIMEHeaderKey(k)
|
||||||
|
md[kn] = append(md[kn], v)
|
||||||
|
}
|
||||||
|
|
||||||
// Del is used to remove value from metadata
|
// Del is used to remove value from metadata
|
||||||
func (md Metadata) Del(keys ...string) {
|
func (md Metadata) Del(keys ...string) {
|
||||||
for _, key := range keys {
|
for _, key := range keys {
|
||||||
@@ -99,7 +128,7 @@ func (md Metadata) Del(keys ...string) {
|
|||||||
func Copy(md Metadata) Metadata {
|
func Copy(md Metadata) Metadata {
|
||||||
nmd := New(len(md))
|
nmd := New(len(md))
|
||||||
for key, val := range md {
|
for key, val := range md {
|
||||||
nmd.Set(key, val)
|
nmd[key] = val
|
||||||
}
|
}
|
||||||
return nmd
|
return nmd
|
||||||
}
|
}
|
||||||
@@ -114,16 +143,24 @@ func New(size int) Metadata {
|
|||||||
|
|
||||||
// Merge merges metadata to existing metadata, overwriting if specified
|
// Merge merges metadata to existing metadata, overwriting if specified
|
||||||
func Merge(omd Metadata, mmd Metadata, overwrite bool) Metadata {
|
func Merge(omd Metadata, mmd Metadata, overwrite bool) Metadata {
|
||||||
var ok bool
|
|
||||||
nmd := Copy(omd)
|
nmd := Copy(omd)
|
||||||
for key, val := range mmd {
|
for key, nval := range mmd {
|
||||||
_, ok = nmd[key]
|
oval, ok := nmd[key]
|
||||||
switch {
|
switch {
|
||||||
case ok && !overwrite:
|
case ok && !overwrite:
|
||||||
continue
|
continue
|
||||||
case val != "":
|
case len(nval) == 1 && nval[0] != "":
|
||||||
nmd.Set(key, val)
|
nmd[key] = nval
|
||||||
case ok && val == "":
|
case ok && len(nval) > 1:
|
||||||
|
sort.Strings(nval)
|
||||||
|
sort.Strings(oval)
|
||||||
|
for idx, v := range nval {
|
||||||
|
if oval[idx] != v {
|
||||||
|
oval[idx] = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
nmd[key] = oval
|
||||||
|
case ok && nval[0] == "":
|
||||||
nmd.Del(key)
|
nmd.Del(key)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -68,10 +68,10 @@ func TestPassing(t *testing.T) {
|
|||||||
|
|
||||||
func TestMerge(t *testing.T) {
|
func TestMerge(t *testing.T) {
|
||||||
omd := Metadata{
|
omd := Metadata{
|
||||||
"key1": "val1",
|
"key1": []string{"val1"},
|
||||||
}
|
}
|
||||||
mmd := Metadata{
|
mmd := Metadata{
|
||||||
"key2": "val2",
|
"key2": []string{"val2"},
|
||||||
}
|
}
|
||||||
|
|
||||||
nmd := Merge(omd, mmd, true)
|
nmd := Merge(omd, mmd, true)
|
||||||
@@ -82,13 +82,14 @@ func TestMerge(t *testing.T) {
|
|||||||
|
|
||||||
func TestIterator(t *testing.T) {
|
func TestIterator(t *testing.T) {
|
||||||
md := Metadata{
|
md := Metadata{
|
||||||
"1Last": "last",
|
"1Last": []string{"last"},
|
||||||
"2First": "first",
|
"2First": []string{"first"},
|
||||||
"3Second": "second",
|
"3Second": []string{"second"},
|
||||||
}
|
}
|
||||||
|
|
||||||
iter := md.Iterator()
|
iter := md.Iterator()
|
||||||
var k, v string
|
var k string
|
||||||
|
var v []string
|
||||||
|
|
||||||
for iter.Next(&k, &v) {
|
for iter.Next(&k, &v) {
|
||||||
// fmt.Printf("k: %s, v: %s\n", k, v)
|
// fmt.Printf("k: %s, v: %s\n", k, v)
|
||||||
@@ -101,20 +102,20 @@ func TestMedataCanonicalKey(t *testing.T) {
|
|||||||
v, ok := md.Get("x-request-id")
|
v, ok := md.Get("x-request-id")
|
||||||
if !ok {
|
if !ok {
|
||||||
t.Fatalf("failed to get x-request-id")
|
t.Fatalf("failed to get x-request-id")
|
||||||
} else if v != "12345" {
|
} else if len(v) != 1 && v != "12345" {
|
||||||
t.Fatalf("invalid metadata value: %s != %s", "12345", v)
|
t.Fatalf("invalid metadata value: %s != %s", "12345", v)
|
||||||
}
|
}
|
||||||
|
|
||||||
v, ok = md.Get("X-Request-Id")
|
v, ok = md.Get("X-Request-Id")
|
||||||
if !ok {
|
if !ok {
|
||||||
t.Fatalf("failed to get x-request-id")
|
t.Fatalf("failed to get x-request-id")
|
||||||
} else if v != "12345" {
|
} else if len(v) != 1 && v != "12345" {
|
||||||
t.Fatalf("invalid metadata value: %s != %s", "12345", v)
|
t.Fatalf("invalid metadata value: %s != %s", "12345", v)
|
||||||
}
|
}
|
||||||
v, ok = md.Get("X-Request-ID")
|
v, ok = md.Get("X-Request-ID")
|
||||||
if !ok {
|
if !ok {
|
||||||
t.Fatalf("failed to get x-request-id")
|
t.Fatalf("failed to get x-request-id")
|
||||||
} else if v != "12345" {
|
} else if len(v) != 1 && v != "12345" {
|
||||||
t.Fatalf("invalid metadata value: %s != %s", "12345", v)
|
t.Fatalf("invalid metadata value: %s != %s", "12345", v)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -135,8 +136,8 @@ func TestMetadataSet(t *testing.T) {
|
|||||||
|
|
||||||
func TestMetadataDelete(t *testing.T) {
|
func TestMetadataDelete(t *testing.T) {
|
||||||
md := Metadata{
|
md := Metadata{
|
||||||
"Foo": "bar",
|
"Foo": []string{"bar"},
|
||||||
"Baz": "empty",
|
"Baz": []string{"empty"},
|
||||||
}
|
}
|
||||||
|
|
||||||
md.Del("Baz")
|
md.Del("Baz")
|
||||||
@@ -157,14 +158,14 @@ func TestNilContext(t *testing.T) {
|
|||||||
|
|
||||||
func TestMetadataCopy(t *testing.T) {
|
func TestMetadataCopy(t *testing.T) {
|
||||||
md := Metadata{
|
md := Metadata{
|
||||||
"Foo": "bar",
|
"Foo": []string{"bar"},
|
||||||
"Bar": "baz",
|
"Bar": []string{"baz"},
|
||||||
}
|
}
|
||||||
|
|
||||||
cp := Copy(md)
|
cp := Copy(md)
|
||||||
|
|
||||||
for k, v := range md {
|
for k, v := range md {
|
||||||
if cv := cp[k]; cv != v {
|
if cv := cp[k]; len(cv) != len(v) || (cv[0] != v[0] && cv[1] != v[1]) {
|
||||||
t.Fatalf("Got %s:%s for %s:%s", k, cv, k, v)
|
t.Fatalf("Got %s:%s for %s:%s", k, cv, k, v)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -172,7 +173,7 @@ func TestMetadataCopy(t *testing.T) {
|
|||||||
|
|
||||||
func TestMetadataContext(t *testing.T) {
|
func TestMetadataContext(t *testing.T) {
|
||||||
md := Metadata{
|
md := Metadata{
|
||||||
"Foo": "bar",
|
"Foo": []string{"bar"},
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx := NewContext(context.TODO(), md)
|
ctx := NewContext(context.TODO(), md)
|
||||||
@@ -182,7 +183,7 @@ func TestMetadataContext(t *testing.T) {
|
|||||||
t.Errorf("Unexpected error retrieving metadata, got %t", ok)
|
t.Errorf("Unexpected error retrieving metadata, got %t", ok)
|
||||||
}
|
}
|
||||||
|
|
||||||
if emd["Foo"] != md["Foo"] {
|
if len(emd["Foo"]) != len(md["Foo"]) || (emd["Foo"][0] != md["Foo"][0]) {
|
||||||
t.Errorf("Expected key: %s val: %s, got key: %s val: %s", "Foo", md["Foo"], "Foo", emd["Foo"])
|
t.Errorf("Expected key: %s val: %s, got key: %s val: %s", "Foo", md["Foo"], "Foo", emd["Foo"])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -6,6 +6,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"go.unistack.org/micro/v3/logger"
|
"go.unistack.org/micro/v3/logger"
|
||||||
|
md "go.unistack.org/micro/v3/metadata"
|
||||||
"go.unistack.org/micro/v3/util/id"
|
"go.unistack.org/micro/v3/util/id"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -23,7 +24,7 @@ type node struct {
|
|||||||
type record struct {
|
type record struct {
|
||||||
Name string
|
Name string
|
||||||
Version string
|
Version string
|
||||||
Metadata map[string]string
|
Metadata md.Metadata
|
||||||
Nodes map[string]*node
|
Nodes map[string]*node
|
||||||
Endpoints []*Endpoint
|
Endpoints []*Endpoint
|
||||||
}
|
}
|
||||||
@@ -136,9 +137,9 @@ func (m *memory) Register(ctx context.Context, s *Service, opts ...RegisterOptio
|
|||||||
|
|
||||||
// domain is set in metadata so it can be passed to watchers
|
// domain is set in metadata so it can be passed to watchers
|
||||||
if s.Metadata == nil {
|
if s.Metadata == nil {
|
||||||
s.Metadata = map[string]string{"domain": options.Domain}
|
s.Metadata = map[string][]string{"domain": {options.Domain}}
|
||||||
} else {
|
} else {
|
||||||
s.Metadata["domain"] = options.Domain
|
s.Metadata["domain"] = []string{options.Domain}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ensure the service name exists
|
// ensure the service name exists
|
||||||
@@ -164,15 +165,10 @@ func (m *memory) Register(ctx context.Context, s *Service, opts ...RegisterOptio
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
metadata := make(map[string]string, len(n.Metadata))
|
metadata := md.Copy(n.Metadata)
|
||||||
|
|
||||||
// make copy of metadata
|
|
||||||
for k, v := range n.Metadata {
|
|
||||||
metadata[k] = v
|
|
||||||
}
|
|
||||||
|
|
||||||
// set the domain
|
// set the domain
|
||||||
metadata["domain"] = options.Domain
|
metadata["domain"] = []string{options.Domain}
|
||||||
|
|
||||||
// add the node
|
// add the node
|
||||||
srvs[s.Name][s.Version].Nodes[n.ID] = &node{
|
srvs[s.Name][s.Version].Nodes[n.ID] = &node{
|
||||||
@@ -216,9 +212,9 @@ func (m *memory) Deregister(ctx context.Context, s *Service, opts ...DeregisterO
|
|||||||
|
|
||||||
// domain is set in metadata so it can be passed to watchers
|
// domain is set in metadata so it can be passed to watchers
|
||||||
if s.Metadata == nil {
|
if s.Metadata == nil {
|
||||||
s.Metadata = map[string]string{"domain": options.Domain}
|
s.Metadata = map[string][]string{"domain": {options.Domain}}
|
||||||
} else {
|
} else {
|
||||||
s.Metadata["domain"] = options.Domain
|
s.Metadata["domain"] = []string{options.Domain}
|
||||||
}
|
}
|
||||||
|
|
||||||
// if the domain doesn't exist, there is nothing to deregister
|
// if the domain doesn't exist, there is nothing to deregister
|
||||||
@@ -426,8 +422,8 @@ func (m *watcher) Next() (*Result, error) {
|
|||||||
|
|
||||||
// extract domain from service metadata
|
// extract domain from service metadata
|
||||||
var domain string
|
var domain string
|
||||||
if r.Service.Metadata != nil && len(r.Service.Metadata["domain"]) > 0 {
|
if r.Service.Metadata != nil && len(r.Service.Metadata["domain"]) > 0 && len(r.Service.Metadata["domain"][0]) > 0 {
|
||||||
domain = r.Service.Metadata["domain"]
|
domain = r.Service.Metadata["domain"][0]
|
||||||
} else {
|
} else {
|
||||||
domain = DefaultDomain
|
domain = DefaultDomain
|
||||||
}
|
}
|
||||||
@@ -452,10 +448,7 @@ func (m *watcher) Stop() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func serviceToRecord(s *Service, ttl time.Duration) *record {
|
func serviceToRecord(s *Service, ttl time.Duration) *record {
|
||||||
metadata := make(map[string]string, len(s.Metadata))
|
metadata := md.Copy(s.Metadata)
|
||||||
for k, v := range s.Metadata {
|
|
||||||
metadata[k] = v
|
|
||||||
}
|
|
||||||
|
|
||||||
nodes := make(map[string]*node, len(s.Nodes))
|
nodes := make(map[string]*node, len(s.Nodes))
|
||||||
for _, n := range s.Nodes {
|
for _, n := range s.Nodes {
|
||||||
@@ -481,41 +474,28 @@ func serviceToRecord(s *Service, ttl time.Duration) *record {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func recordToService(r *record, domain string) *Service {
|
func recordToService(r *record, domain string) *Service {
|
||||||
metadata := make(map[string]string, len(r.Metadata))
|
metadata := md.Copy(r.Metadata)
|
||||||
for k, v := range r.Metadata {
|
|
||||||
metadata[k] = v
|
|
||||||
}
|
|
||||||
|
|
||||||
// set the domain in metadata so it can be determined when a wildcard query is performed
|
// set the domain in metadata so it can be determined when a wildcard query is performed
|
||||||
metadata["domain"] = domain
|
metadata["domain"] = []string{domain}
|
||||||
|
|
||||||
endpoints := make([]*Endpoint, len(r.Endpoints))
|
endpoints := make([]*Endpoint, len(r.Endpoints))
|
||||||
for i, e := range r.Endpoints {
|
for i, e := range r.Endpoints {
|
||||||
md := make(map[string]string, len(e.Metadata))
|
|
||||||
for k, v := range e.Metadata {
|
|
||||||
md[k] = v
|
|
||||||
}
|
|
||||||
|
|
||||||
endpoints[i] = &Endpoint{
|
endpoints[i] = &Endpoint{
|
||||||
Name: e.Name,
|
Name: e.Name,
|
||||||
Request: e.Request,
|
Request: e.Request,
|
||||||
Response: e.Response,
|
Response: e.Response,
|
||||||
Metadata: md,
|
Metadata: md.Copy(e.Metadata),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
nodes := make([]*Node, len(r.Nodes))
|
nodes := make([]*Node, len(r.Nodes))
|
||||||
i := 0
|
i := 0
|
||||||
for _, n := range r.Nodes {
|
for _, n := range r.Nodes {
|
||||||
md := make(map[string]string, len(n.Metadata))
|
|
||||||
for k, v := range n.Metadata {
|
|
||||||
md[k] = v
|
|
||||||
}
|
|
||||||
|
|
||||||
nodes[i] = &Node{
|
nodes[i] = &Node{
|
||||||
ID: n.ID,
|
ID: n.ID,
|
||||||
Address: n.Address,
|
Address: n.Address,
|
||||||
Metadata: md,
|
Metadata: md.Copy(n.Metadata),
|
||||||
}
|
}
|
||||||
i++
|
i++
|
||||||
}
|
}
|
||||||
|
@@ -13,6 +13,7 @@ func (t *noopTracer) Start(ctx context.Context, name string, opts ...SpanOption)
|
|||||||
name: name,
|
name: name,
|
||||||
ctx: ctx,
|
ctx: ctx,
|
||||||
tracer: t,
|
tracer: t,
|
||||||
|
opts: NewSpanOptions(opts...),
|
||||||
}
|
}
|
||||||
if span.ctx == nil {
|
if span.ctx == nil {
|
||||||
span.ctx = context.Background()
|
span.ctx = context.Background()
|
||||||
@@ -32,10 +33,12 @@ func (t *noopTracer) Name() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type noopSpan struct {
|
type noopSpan struct {
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
tracer Tracer
|
tracer Tracer
|
||||||
name string
|
name string
|
||||||
opts SpanOptions
|
opts SpanOptions
|
||||||
|
status SpanStatus
|
||||||
|
statusMsg string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *noopSpan) Finish(opts ...SpanOption) {
|
func (s *noopSpan) Finish(opts ...SpanOption) {
|
||||||
@@ -64,6 +67,19 @@ func (s *noopSpan) AddLabels(labels ...interface{}) {
|
|||||||
s.opts.Labels = append(s.opts.Labels, labels...)
|
s.opts.Labels = append(s.opts.Labels, labels...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *noopSpan) Kind() SpanKind {
|
||||||
|
return s.opts.Kind
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *noopSpan) Status() (SpanStatus, string) {
|
||||||
|
return s.status, s.statusMsg
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *noopSpan) SetStatus(st SpanStatus, msg string) {
|
||||||
|
s.status = st
|
||||||
|
s.statusMsg = msg
|
||||||
|
}
|
||||||
|
|
||||||
// NewTracer returns new memory tracer
|
// NewTracer returns new memory tracer
|
||||||
func NewTracer(opts ...Option) Tracer {
|
func NewTracer(opts ...Option) Tracer {
|
||||||
return &noopTracer{
|
return &noopTracer{
|
||||||
|
@@ -6,9 +6,85 @@ import (
|
|||||||
"go.unistack.org/micro/v3/logger"
|
"go.unistack.org/micro/v3/logger"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type SpanStatus int
|
||||||
|
|
||||||
|
const (
|
||||||
|
// SpanStatusUnset is the default status code.
|
||||||
|
SpanStatusUnset SpanStatus = 0
|
||||||
|
|
||||||
|
// SpanStatusError indicates the operation contains an error.
|
||||||
|
SpanStatusError SpanStatus = 1
|
||||||
|
|
||||||
|
// SpanStatusOK indicates operation has been validated by an Application developers
|
||||||
|
// or Operator to have completed successfully, or contain no error.
|
||||||
|
SpanStatusOK SpanStatus = 2
|
||||||
|
)
|
||||||
|
|
||||||
|
func (s SpanStatus) String() string {
|
||||||
|
switch s {
|
||||||
|
case SpanStatusUnset:
|
||||||
|
return "Unset"
|
||||||
|
case SpanStatusError:
|
||||||
|
return "Error"
|
||||||
|
case SpanStatusOK:
|
||||||
|
return "OK"
|
||||||
|
default:
|
||||||
|
return "Unset"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type SpanKind int
|
||||||
|
|
||||||
|
const (
|
||||||
|
// SpanKindUnspecified is an unspecified SpanKind and is not a valid
|
||||||
|
// SpanKind. SpanKindUnspecified should be replaced with SpanKindInternal
|
||||||
|
// if it is received.
|
||||||
|
SpanKindUnspecified SpanKind = 0
|
||||||
|
// SpanKindInternal is a SpanKind for a Span that represents an internal
|
||||||
|
// operation within an application.
|
||||||
|
SpanKindInternal SpanKind = 1
|
||||||
|
// SpanKindServer is a SpanKind for a Span that represents the operation
|
||||||
|
// of handling a request from a client.
|
||||||
|
SpanKindServer SpanKind = 2
|
||||||
|
// SpanKindClient is a SpanKind for a Span that represents the operation
|
||||||
|
// of client making a request to a server.
|
||||||
|
SpanKindClient SpanKind = 3
|
||||||
|
// SpanKindProducer is a SpanKind for a Span that represents the operation
|
||||||
|
// of a producer sending a message to a message broker. Unlike
|
||||||
|
// SpanKindClient and SpanKindServer, there is often no direct
|
||||||
|
// relationship between this kind of Span and a SpanKindConsumer kind. A
|
||||||
|
// SpanKindProducer Span will end once the message is accepted by the
|
||||||
|
// message broker which might not overlap with the processing of that
|
||||||
|
// message.
|
||||||
|
SpanKindProducer SpanKind = 4
|
||||||
|
// SpanKindConsumer is a SpanKind for a Span that represents the operation
|
||||||
|
// of a consumer receiving a message from a message broker. Like
|
||||||
|
// SpanKindProducer Spans, there is often no direct relationship between
|
||||||
|
// this Span and the Span that produced the message.
|
||||||
|
SpanKindConsumer SpanKind = 5
|
||||||
|
)
|
||||||
|
|
||||||
|
func (sk SpanKind) String() string {
|
||||||
|
switch sk {
|
||||||
|
case SpanKindInternal:
|
||||||
|
return "internal"
|
||||||
|
case SpanKindServer:
|
||||||
|
return "server"
|
||||||
|
case SpanKindClient:
|
||||||
|
return "client"
|
||||||
|
case SpanKindProducer:
|
||||||
|
return "producer"
|
||||||
|
case SpanKindConsumer:
|
||||||
|
return "consumer"
|
||||||
|
default:
|
||||||
|
return "unspecified"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// SpanOptions contains span option
|
// SpanOptions contains span option
|
||||||
type SpanOptions struct {
|
type SpanOptions struct {
|
||||||
Labels []interface{}
|
Labels []interface{}
|
||||||
|
Kind SpanKind
|
||||||
}
|
}
|
||||||
|
|
||||||
// SpanOption func signature
|
// SpanOption func signature
|
||||||
@@ -20,20 +96,26 @@ type EventOptions struct{}
|
|||||||
// EventOption func signature
|
// EventOption func signature
|
||||||
type EventOption func(o *EventOptions)
|
type EventOption func(o *EventOptions)
|
||||||
|
|
||||||
func SpanLabels(labels ...interface{}) SpanOption {
|
func WithSpanLabels(labels ...interface{}) SpanOption {
|
||||||
return func(o *SpanOptions) {
|
return func(o *SpanOptions) {
|
||||||
o.Labels = labels
|
o.Labels = labels
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func WithSpanKind(k SpanKind) SpanOption {
|
||||||
|
return func(o *SpanOptions) {
|
||||||
|
o.Kind = k
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Options struct
|
// Options struct
|
||||||
type Options struct {
|
type Options struct {
|
||||||
|
// Context used to store custome tracer options
|
||||||
|
Context context.Context
|
||||||
// Logger used for logging
|
// Logger used for logging
|
||||||
Logger logger.Logger
|
Logger logger.Logger
|
||||||
// Name of the tracer
|
// Name of the tracer
|
||||||
Name string
|
Name string
|
||||||
// Context used to store custome tracer options
|
|
||||||
Context context.Context
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Option func signature
|
// Option func signature
|
||||||
@@ -46,6 +128,17 @@ func Logger(l logger.Logger) Option {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewSpanOptions returns default SpanOptions
|
||||||
|
func NewSpanOptions(opts ...SpanOption) SpanOptions {
|
||||||
|
options := SpanOptions{
|
||||||
|
Kind: SpanKindInternal,
|
||||||
|
}
|
||||||
|
for _, o := range opts {
|
||||||
|
o(&options)
|
||||||
|
}
|
||||||
|
return options
|
||||||
|
}
|
||||||
|
|
||||||
// NewOptions returns default options
|
// NewOptions returns default options
|
||||||
func NewOptions(opts ...Option) Options {
|
func NewOptions(opts ...Option) Options {
|
||||||
options := Options{
|
options := Options{
|
||||||
|
@@ -29,8 +29,14 @@ type Span interface {
|
|||||||
Context() context.Context
|
Context() context.Context
|
||||||
// SetName set the span name
|
// SetName set the span name
|
||||||
SetName(name string)
|
SetName(name string)
|
||||||
|
// SetStatus set the span status code and msg
|
||||||
|
SetStatus(status SpanStatus, msg string)
|
||||||
|
// Status returns span status and msg
|
||||||
|
Status() (SpanStatus, string)
|
||||||
// SetLabels set the span labels
|
// SetLabels set the span labels
|
||||||
SetLabels(labels ...interface{})
|
SetLabels(labels ...interface{})
|
||||||
// AddLabels append the span labels
|
// AddLabels append the span labels
|
||||||
AddLabels(labels ...interface{})
|
AddLabels(labels ...interface{})
|
||||||
|
// Kind returns span kind
|
||||||
|
Kind() SpanKind
|
||||||
}
|
}
|
||||||
|
@@ -13,22 +13,24 @@ import (
|
|||||||
|
|
||||||
var (
|
var (
|
||||||
DefaultClientCallObserver = func(ctx context.Context, req client.Request, rsp interface{}, opts []client.CallOption, sp tracer.Span, err error) {
|
DefaultClientCallObserver = func(ctx context.Context, req client.Request, rsp interface{}, opts []client.CallOption, sp tracer.Span, err error) {
|
||||||
sp.SetName(fmt.Sprintf("%s.%s", req.Service(), req.Endpoint()))
|
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 = make([]interface{}, 0, len(md))
|
labels = make([]interface{}, 0, len(md)+1)
|
||||||
for k, v := range md {
|
for k, v := range md {
|
||||||
labels = append(labels, k, v)
|
labels = append(labels, k, v)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
labels = append(labels, "error", true)
|
labels = append(labels, "error", err.Error())
|
||||||
|
sp.SetStatus(tracer.SpanStatusError, err.Error())
|
||||||
}
|
}
|
||||||
|
labels = append(labels, "kind", sp.Kind())
|
||||||
sp.SetLabels(labels...)
|
sp.SetLabels(labels...)
|
||||||
}
|
}
|
||||||
|
|
||||||
DefaultClientStreamObserver = func(ctx context.Context, req client.Request, opts []client.CallOption, stream client.Stream, sp tracer.Span, err error) {
|
DefaultClientStreamObserver = func(ctx context.Context, req client.Request, opts []client.CallOption, stream client.Stream, sp tracer.Span, err error) {
|
||||||
sp.SetName(fmt.Sprintf("%s.%s", req.Service(), req.Endpoint()))
|
sp.SetName(fmt.Sprintf("Stream %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 = make([]interface{}, 0, len(md))
|
labels = make([]interface{}, 0, len(md))
|
||||||
@@ -37,13 +39,15 @@ var (
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
labels = append(labels, "error", true)
|
labels = append(labels, "error", err.Error())
|
||||||
|
sp.SetStatus(tracer.SpanStatusError, err.Error())
|
||||||
}
|
}
|
||||||
|
labels = append(labels, "kind", sp.Kind())
|
||||||
sp.SetLabels(labels...)
|
sp.SetLabels(labels...)
|
||||||
}
|
}
|
||||||
|
|
||||||
DefaultClientPublishObserver = func(ctx context.Context, msg client.Message, opts []client.PublishOption, sp tracer.Span, err error) {
|
DefaultClientPublishObserver = func(ctx context.Context, msg client.Message, opts []client.PublishOption, sp tracer.Span, err error) {
|
||||||
sp.SetName(fmt.Sprintf("Pub to %s", msg.Topic()))
|
sp.SetName(fmt.Sprintf("Publish %s", msg.Topic()))
|
||||||
var labels []interface{}
|
var labels []interface{}
|
||||||
if md, ok := metadata.FromOutgoingContext(ctx); ok {
|
if md, ok := metadata.FromOutgoingContext(ctx); ok {
|
||||||
labels = make([]interface{}, 0, len(md))
|
labels = make([]interface{}, 0, len(md))
|
||||||
@@ -52,13 +56,15 @@ var (
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
labels = append(labels, "error", true)
|
labels = append(labels, "error", err.Error())
|
||||||
|
sp.SetStatus(tracer.SpanStatusError, err.Error())
|
||||||
}
|
}
|
||||||
|
labels = append(labels, "kind", sp.Kind())
|
||||||
sp.SetLabels(labels...)
|
sp.SetLabels(labels...)
|
||||||
}
|
}
|
||||||
|
|
||||||
DefaultServerHandlerObserver = func(ctx context.Context, req server.Request, rsp interface{}, sp tracer.Span, err error) {
|
DefaultServerHandlerObserver = func(ctx context.Context, req server.Request, rsp interface{}, sp tracer.Span, err error) {
|
||||||
sp.SetName(fmt.Sprintf("%s.%s", req.Service(), req.Endpoint()))
|
sp.SetName(fmt.Sprintf("Handler %s.%s", req.Service(), req.Method()))
|
||||||
var labels []interface{}
|
var labels []interface{}
|
||||||
if md, ok := metadata.FromIncomingContext(ctx); ok {
|
if md, ok := metadata.FromIncomingContext(ctx); ok {
|
||||||
labels = make([]interface{}, 0, len(md))
|
labels = make([]interface{}, 0, len(md))
|
||||||
@@ -67,13 +73,15 @@ var (
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
labels = append(labels, "error", true)
|
labels = append(labels, "error", err.Error())
|
||||||
|
sp.SetStatus(tracer.SpanStatusError, err.Error())
|
||||||
}
|
}
|
||||||
|
labels = append(labels, "kind", sp.Kind())
|
||||||
sp.SetLabels(labels...)
|
sp.SetLabels(labels...)
|
||||||
}
|
}
|
||||||
|
|
||||||
DefaultServerSubscriberObserver = func(ctx context.Context, msg server.Message, sp tracer.Span, err error) {
|
DefaultServerSubscriberObserver = func(ctx context.Context, msg server.Message, sp tracer.Span, err error) {
|
||||||
sp.SetName(fmt.Sprintf("Sub from %s", msg.Topic()))
|
sp.SetName(fmt.Sprintf("Subscriber %s", msg.Topic()))
|
||||||
var labels []interface{}
|
var labels []interface{}
|
||||||
if md, ok := metadata.FromIncomingContext(ctx); ok {
|
if md, ok := metadata.FromIncomingContext(ctx); ok {
|
||||||
labels = make([]interface{}, 0, len(md))
|
labels = make([]interface{}, 0, len(md))
|
||||||
@@ -82,13 +90,15 @@ var (
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
labels = append(labels, "error", true)
|
labels = append(labels, "error", err.Error())
|
||||||
|
sp.SetStatus(tracer.SpanStatusError, err.Error())
|
||||||
}
|
}
|
||||||
|
labels = append(labels, "kind", sp.Kind())
|
||||||
sp.SetLabels(labels...)
|
sp.SetLabels(labels...)
|
||||||
}
|
}
|
||||||
|
|
||||||
DefaultClientCallFuncObserver = func(ctx context.Context, addr string, req client.Request, rsp interface{}, opts client.CallOptions, sp tracer.Span, err error) {
|
DefaultClientCallFuncObserver = func(ctx context.Context, addr string, req client.Request, rsp interface{}, opts client.CallOptions, sp tracer.Span, err error) {
|
||||||
sp.SetName(fmt.Sprintf("%s.%s", req.Service(), req.Endpoint()))
|
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 = make([]interface{}, 0, len(md))
|
labels = make([]interface{}, 0, len(md))
|
||||||
@@ -97,8 +107,10 @@ var (
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
labels = append(labels, "error", true)
|
labels = append(labels, "error", err.Error())
|
||||||
|
sp.SetStatus(tracer.SpanStatusError, err.Error())
|
||||||
}
|
}
|
||||||
|
labels = append(labels, "kind", sp.Kind())
|
||||||
sp.SetLabels(labels...)
|
sp.SetLabels(labels...)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -231,7 +243,7 @@ func (ot *tWrapper) Call(ctx context.Context, req client.Request, rsp interface{
|
|||||||
|
|
||||||
sp, ok := tracer.SpanFromContext(ctx)
|
sp, ok := tracer.SpanFromContext(ctx)
|
||||||
if !ok {
|
if !ok {
|
||||||
ctx, sp = ot.opts.Tracer.Start(ctx, endpoint)
|
ctx, sp = ot.opts.Tracer.Start(ctx, "", tracer.WithSpanKind(tracer.SpanKindClient))
|
||||||
}
|
}
|
||||||
defer sp.Finish()
|
defer sp.Finish()
|
||||||
|
|
||||||
@@ -254,7 +266,7 @@ func (ot *tWrapper) Stream(ctx context.Context, req client.Request, opts ...clie
|
|||||||
|
|
||||||
sp, ok := tracer.SpanFromContext(ctx)
|
sp, ok := tracer.SpanFromContext(ctx)
|
||||||
if !ok {
|
if !ok {
|
||||||
ctx, sp = ot.opts.Tracer.Start(ctx, endpoint)
|
ctx, sp = ot.opts.Tracer.Start(ctx, "", tracer.WithSpanKind(tracer.SpanKindClient))
|
||||||
}
|
}
|
||||||
defer sp.Finish()
|
defer sp.Finish()
|
||||||
|
|
||||||
@@ -270,7 +282,7 @@ func (ot *tWrapper) Stream(ctx context.Context, req client.Request, opts ...clie
|
|||||||
func (ot *tWrapper) Publish(ctx context.Context, msg client.Message, opts ...client.PublishOption) error {
|
func (ot *tWrapper) Publish(ctx context.Context, msg client.Message, opts ...client.PublishOption) error {
|
||||||
sp, ok := tracer.SpanFromContext(ctx)
|
sp, ok := tracer.SpanFromContext(ctx)
|
||||||
if !ok {
|
if !ok {
|
||||||
ctx, sp = ot.opts.Tracer.Start(ctx, msg.Topic())
|
ctx, sp = ot.opts.Tracer.Start(ctx, "", tracer.WithSpanKind(tracer.SpanKindProducer))
|
||||||
}
|
}
|
||||||
defer sp.Finish()
|
defer sp.Finish()
|
||||||
|
|
||||||
@@ -284,7 +296,7 @@ func (ot *tWrapper) Publish(ctx context.Context, msg client.Message, opts ...cli
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (ot *tWrapper) ServerHandler(ctx context.Context, req server.Request, rsp interface{}) error {
|
func (ot *tWrapper) ServerHandler(ctx context.Context, req server.Request, rsp interface{}) error {
|
||||||
endpoint := req.Endpoint()
|
endpoint := fmt.Sprintf("%s.%s", req.Service(), req.Method())
|
||||||
for _, ep := range ot.opts.SkipEndpoints {
|
for _, ep := range ot.opts.SkipEndpoints {
|
||||||
if ep == endpoint {
|
if ep == endpoint {
|
||||||
return ot.serverHandler(ctx, req, rsp)
|
return ot.serverHandler(ctx, req, rsp)
|
||||||
@@ -293,7 +305,7 @@ func (ot *tWrapper) ServerHandler(ctx context.Context, req server.Request, rsp i
|
|||||||
|
|
||||||
sp, ok := tracer.SpanFromContext(ctx)
|
sp, ok := tracer.SpanFromContext(ctx)
|
||||||
if !ok {
|
if !ok {
|
||||||
ctx, sp = ot.opts.Tracer.Start(ctx, fmt.Sprintf("%s.%s", req.Service(), req.Endpoint()))
|
ctx, sp = ot.opts.Tracer.Start(ctx, "", tracer.WithSpanKind(tracer.SpanKindServer))
|
||||||
}
|
}
|
||||||
defer sp.Finish()
|
defer sp.Finish()
|
||||||
|
|
||||||
@@ -309,7 +321,7 @@ func (ot *tWrapper) ServerHandler(ctx context.Context, req server.Request, rsp i
|
|||||||
func (ot *tWrapper) ServerSubscriber(ctx context.Context, msg server.Message) error {
|
func (ot *tWrapper) ServerSubscriber(ctx context.Context, msg server.Message) error {
|
||||||
sp, ok := tracer.SpanFromContext(ctx)
|
sp, ok := tracer.SpanFromContext(ctx)
|
||||||
if !ok {
|
if !ok {
|
||||||
ctx, sp = ot.opts.Tracer.Start(ctx, msg.Topic())
|
ctx, sp = ot.opts.Tracer.Start(ctx, "", tracer.WithSpanKind(tracer.SpanKindConsumer))
|
||||||
}
|
}
|
||||||
defer sp.Finish()
|
defer sp.Finish()
|
||||||
|
|
||||||
@@ -347,7 +359,7 @@ func NewClientCallWrapper(opts ...Option) client.CallWrapper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (ot *tWrapper) ClientCallFunc(ctx context.Context, addr string, req client.Request, rsp interface{}, opts client.CallOptions) error {
|
func (ot *tWrapper) ClientCallFunc(ctx context.Context, addr string, req client.Request, rsp interface{}, opts client.CallOptions) error {
|
||||||
endpoint := fmt.Sprintf("%s.%s", req.Service(), req.Endpoint())
|
endpoint := fmt.Sprintf("%s.%s", req.Service(), req.Method())
|
||||||
for _, ep := range ot.opts.SkipEndpoints {
|
for _, ep := range ot.opts.SkipEndpoints {
|
||||||
if ep == endpoint {
|
if ep == endpoint {
|
||||||
return ot.ClientCallFunc(ctx, addr, req, rsp, opts)
|
return ot.ClientCallFunc(ctx, addr, req, rsp, opts)
|
||||||
@@ -356,7 +368,7 @@ func (ot *tWrapper) ClientCallFunc(ctx context.Context, addr string, req client.
|
|||||||
|
|
||||||
sp, ok := tracer.SpanFromContext(ctx)
|
sp, ok := tracer.SpanFromContext(ctx)
|
||||||
if !ok {
|
if !ok {
|
||||||
ctx, sp = ot.opts.Tracer.Start(ctx, endpoint)
|
ctx, sp = ot.opts.Tracer.Start(ctx, "", tracer.WithSpanKind(tracer.SpanKindClient))
|
||||||
}
|
}
|
||||||
defer sp.Finish()
|
defer sp.Finish()
|
||||||
|
|
||||||
|
48
util/time/duration.go
Normal file
48
util/time/duration.go
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
package time
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Duration int64
|
||||||
|
|
||||||
|
func ParseDuration(s string) (time.Duration, error) {
|
||||||
|
if s == "" {
|
||||||
|
return 0, fmt.Errorf(`time: invalid duration "` + s + `"`)
|
||||||
|
}
|
||||||
|
|
||||||
|
//var sb strings.Builder
|
||||||
|
/*
|
||||||
|
for i, r := range s {
|
||||||
|
switch r {
|
||||||
|
case 'd':
|
||||||
|
n, err := strconv.Atoi(s[idx:i])
|
||||||
|
if err != nil {
|
||||||
|
return 0, errors.New("time: invalid duration " + s)
|
||||||
|
}
|
||||||
|
s[idx:i] = fmt.Sprintf("%d", n*24)
|
||||||
|
default:
|
||||||
|
sb.WriteRune(r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
var td time.Duration
|
||||||
|
var err error
|
||||||
|
switch s[len(s)-1] {
|
||||||
|
case 's', 'm', 'h':
|
||||||
|
td, err = time.ParseDuration(s)
|
||||||
|
case 'd':
|
||||||
|
if td, err = time.ParseDuration(s[:len(s)-1] + "h"); err == nil {
|
||||||
|
td *= 24
|
||||||
|
}
|
||||||
|
case 'y':
|
||||||
|
if td, err = time.ParseDuration(s[:len(s)-1] + "h"); err == nil {
|
||||||
|
year := time.Date(time.Now().Year(), time.December, 31, 0, 0, 0, 0, time.Local)
|
||||||
|
days := year.YearDay()
|
||||||
|
td *= 24 * time.Duration(days)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return td, err
|
||||||
|
}
|
27
util/time/duration_test.go
Normal file
27
util/time/duration_test.go
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
package time
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestParseDuration(t *testing.T) {
|
||||||
|
var td time.Duration
|
||||||
|
var err error
|
||||||
|
t.Skip()
|
||||||
|
td, err = ParseDuration("14d4h")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("ParseDuration error: %v", err)
|
||||||
|
}
|
||||||
|
if td.String() != "336h0m0s" {
|
||||||
|
t.Fatalf("ParseDuration 14d != 336h0m0s : %s", td.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
td, err = ParseDuration("1y")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("ParseDuration error: %v", err)
|
||||||
|
}
|
||||||
|
if td.String() != "8760h0m0s" {
|
||||||
|
t.Fatalf("ParseDuration 1y != 8760h0m0s : %s", td.String())
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user