Compare commits
4 Commits
Author | SHA1 | Date | |
---|---|---|---|
f386bffd37 | |||
772bde7938 | |||
ea16f5f825 | |||
c2f34df493 |
@@ -2,12 +2,12 @@ package config
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/imdario/mergo"
|
||||
"github.com/unistack-org/micro/v3/util/jitter"
|
||||
rutil "github.com/unistack-org/micro/v3/util/reflect"
|
||||
)
|
||||
|
||||
@@ -271,17 +271,7 @@ func (c *defaultConfig) Name() string {
|
||||
}
|
||||
|
||||
func (c *defaultConfig) Watch(ctx context.Context, opts ...WatchOption) (Watcher, error) {
|
||||
w := &defaultWatcher{
|
||||
opts: c.opts,
|
||||
wopts: NewWatchOptions(opts...),
|
||||
done: make(chan struct{}),
|
||||
vchan: make(chan map[string]interface{}),
|
||||
echan: make(chan error),
|
||||
}
|
||||
|
||||
go w.run()
|
||||
|
||||
return w, nil
|
||||
return nil, fmt.Errorf("not implemented")
|
||||
}
|
||||
|
||||
// NewConfig returns new default config source
|
||||
@@ -292,76 +282,3 @@ func NewConfig(opts ...Option) Config {
|
||||
}
|
||||
return &defaultConfig{opts: options}
|
||||
}
|
||||
|
||||
type defaultWatcher struct {
|
||||
opts Options
|
||||
wopts WatchOptions
|
||||
done chan struct{}
|
||||
vchan chan map[string]interface{}
|
||||
echan chan error
|
||||
}
|
||||
|
||||
func (w *defaultWatcher) run() {
|
||||
ticker := jitter.NewTicker(w.wopts.MinInterval, w.wopts.MaxInterval)
|
||||
defer ticker.Stop()
|
||||
|
||||
src := w.opts.Struct
|
||||
if w.wopts.Struct != nil {
|
||||
src = w.wopts.Struct
|
||||
}
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-w.done:
|
||||
return
|
||||
case <-ticker.C:
|
||||
dst, err := rutil.Zero(src)
|
||||
if err == nil {
|
||||
err = fillValues(reflect.ValueOf(dst), w.opts.StructTag)
|
||||
}
|
||||
if err != nil {
|
||||
w.echan <- err
|
||||
return
|
||||
}
|
||||
srcmp, err := rutil.StructFieldsMap(src)
|
||||
if err != nil {
|
||||
w.echan <- err
|
||||
return
|
||||
}
|
||||
dstmp, err := rutil.StructFieldsMap(dst)
|
||||
if err != nil {
|
||||
w.echan <- err
|
||||
return
|
||||
}
|
||||
for sk, sv := range srcmp {
|
||||
if reflect.DeepEqual(dstmp[sk], sv) {
|
||||
delete(dstmp, sk)
|
||||
}
|
||||
}
|
||||
if len(dstmp) > 0 {
|
||||
w.vchan <- dstmp
|
||||
src = dst
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (w *defaultWatcher) Next() (map[string]interface{}, error) {
|
||||
select {
|
||||
case <-w.done:
|
||||
break
|
||||
case err := <-w.echan:
|
||||
return nil, err
|
||||
case v, ok := <-w.vchan:
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
return nil, ErrWatcherStopped
|
||||
}
|
||||
|
||||
func (w *defaultWatcher) Stop() error {
|
||||
close(w.done)
|
||||
return nil
|
||||
}
|
||||
|
@@ -4,7 +4,6 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/unistack-org/micro/v3/config"
|
||||
)
|
||||
@@ -18,57 +17,6 @@ type Cfg struct {
|
||||
IntValue int `default:"99"`
|
||||
}
|
||||
|
||||
func TestWatch(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
conf := &Cfg{IntValue: 10}
|
||||
|
||||
cfg := config.NewConfig(config.Struct(conf))
|
||||
if err := cfg.Init(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := cfg.Load(ctx); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
w, err := cfg.Watch(ctx, config.WatchInterval(200*time.Millisecond, 500*time.Millisecond))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
defer func() {
|
||||
_ = w.Stop()
|
||||
}()
|
||||
|
||||
done := make(chan struct{})
|
||||
|
||||
go func() {
|
||||
for {
|
||||
mp, err := w.Next()
|
||||
if err != nil && err != config.ErrWatcherStopped {
|
||||
t.Fatal(err)
|
||||
} else if err == config.ErrWatcherStopped {
|
||||
return
|
||||
}
|
||||
if len(mp) != 1 {
|
||||
t.Fatal(fmt.Errorf("default watcher err: %v", mp))
|
||||
}
|
||||
|
||||
v, ok := mp["IntValue"]
|
||||
if !ok {
|
||||
t.Fatal(fmt.Errorf("default watcher err: %v", v))
|
||||
}
|
||||
if nv, ok := v.(int); !ok || nv != 99 {
|
||||
t.Fatal(fmt.Errorf("default watcher err: %v", v))
|
||||
}
|
||||
close(done)
|
||||
return
|
||||
}
|
||||
}()
|
||||
|
||||
<-done
|
||||
}
|
||||
|
||||
func TestDefault(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
conf := &Cfg{IntValue: 10}
|
||||
|
@@ -40,7 +40,6 @@ func (l *defaultLogger) Init(opts ...Option) error {
|
||||
l.logFunc = l.opts.Wrappers[i-1].Log(l.logFunc)
|
||||
l.logfFunc = l.opts.Wrappers[i-1].Logf(l.logfFunc)
|
||||
}
|
||||
|
||||
l.Unlock()
|
||||
return nil
|
||||
}
|
||||
@@ -56,26 +55,20 @@ func (l *defaultLogger) V(level Level) bool {
|
||||
return ok
|
||||
}
|
||||
|
||||
func (l *defaultLogger) Fields(fields map[string]interface{}) Logger {
|
||||
func (l *defaultLogger) Fields(fields ...interface{}) Logger {
|
||||
nl := &defaultLogger{opts: l.opts, enc: l.enc}
|
||||
nl.opts.Fields = make(map[string]interface{}, len(l.opts.Fields)+len(fields))
|
||||
l.RLock()
|
||||
for k, v := range l.opts.Fields {
|
||||
nl.opts.Fields[k] = v
|
||||
}
|
||||
l.RUnlock()
|
||||
|
||||
for k, v := range fields {
|
||||
nl.opts.Fields[k] = v
|
||||
if len(fields) == 0 {
|
||||
return nl
|
||||
} else if len(fields)%2 != 0 {
|
||||
fields = fields[:len(fields)-1]
|
||||
}
|
||||
nl.opts.Fields = append(l.opts.Fields, fields...)
|
||||
return nl
|
||||
}
|
||||
|
||||
func copyFields(src map[string]interface{}) map[string]interface{} {
|
||||
dst := make(map[string]interface{}, len(src))
|
||||
for k, v := range src {
|
||||
dst[k] = v
|
||||
}
|
||||
func copyFields(src []interface{}) []interface{} {
|
||||
dst := make([]interface{}, len(src))
|
||||
copy(dst, src)
|
||||
return dst
|
||||
}
|
||||
|
||||
@@ -162,19 +155,23 @@ func (l *defaultLogger) Log(ctx context.Context, level Level, args ...interface{
|
||||
fields := copyFields(l.opts.Fields)
|
||||
l.RUnlock()
|
||||
|
||||
fields["level"] = level.String()
|
||||
fields = append(fields, "level", level.String())
|
||||
|
||||
if _, file, line, ok := runtime.Caller(l.opts.CallerSkipCount); ok {
|
||||
fields["caller"] = fmt.Sprintf("%s:%d", logCallerfilePath(file), line)
|
||||
fields = append(fields, "caller", fmt.Sprintf("%s:%d", logCallerfilePath(file), line))
|
||||
}
|
||||
fields = append(fields, "timestamp", time.Now().Format("2006-01-02 15:04:05"))
|
||||
|
||||
fields["timestamp"] = time.Now().Format("2006-01-02 15:04:05")
|
||||
if len(args) > 0 {
|
||||
fields["msg"] = fmt.Sprint(args...)
|
||||
fields = append(fields, "msg", fmt.Sprint(args...))
|
||||
}
|
||||
|
||||
out := make(map[string]interface{}, len(fields)/2)
|
||||
for i := 0; i < len(fields); i += 2 {
|
||||
out[fields[i].(string)] = fields[i+1]
|
||||
}
|
||||
l.RLock()
|
||||
_ = l.enc.Encode(fields)
|
||||
_ = l.enc.Encode(out)
|
||||
l.RUnlock()
|
||||
}
|
||||
|
||||
@@ -187,30 +184,30 @@ func (l *defaultLogger) Logf(ctx context.Context, level Level, msg string, args
|
||||
fields := copyFields(l.opts.Fields)
|
||||
l.RUnlock()
|
||||
|
||||
fields["level"] = level.String()
|
||||
fields = append(fields, "level", level.String())
|
||||
|
||||
if _, file, line, ok := runtime.Caller(l.opts.CallerSkipCount); ok {
|
||||
fields["caller"] = fmt.Sprintf("%s:%d", logCallerfilePath(file), line)
|
||||
fields = append(fields, "caller", fmt.Sprintf("%s:%d", logCallerfilePath(file), line))
|
||||
}
|
||||
|
||||
fields["timestamp"] = time.Now().Format("2006-01-02 15:04:05")
|
||||
fields = append(fields, "timestamp", time.Now().Format("2006-01-02 15:04:05"))
|
||||
if len(args) > 0 {
|
||||
fields["msg"] = fmt.Sprintf(msg, args...)
|
||||
fields = append(fields, "msg", fmt.Sprintf(msg, args...))
|
||||
} else if msg != "" {
|
||||
fields["msg"] = msg
|
||||
fields = append(fields, "msg", msg)
|
||||
}
|
||||
|
||||
out := make(map[string]interface{}, len(fields)/2)
|
||||
for i := 0; i < len(fields); i += 2 {
|
||||
out[fields[i].(string)] = fields[i+1]
|
||||
}
|
||||
l.RLock()
|
||||
_ = l.enc.Encode(fields)
|
||||
_ = l.enc.Encode(out)
|
||||
l.RUnlock()
|
||||
}
|
||||
|
||||
func (l *defaultLogger) Options() Options {
|
||||
// not guard against options Context values
|
||||
l.RLock()
|
||||
opts := l.opts
|
||||
opts.Fields = copyFields(l.opts.Fields)
|
||||
l.RUnlock()
|
||||
return opts
|
||||
return l.opts
|
||||
}
|
||||
|
||||
// NewLogger builds a new logger based on options
|
||||
|
@@ -20,8 +20,8 @@ type Logger interface {
|
||||
V(level Level) bool
|
||||
// The Logger options
|
||||
Options() Options
|
||||
// Fields set fields to always be logged
|
||||
Fields(fields map[string]interface{}) Logger
|
||||
// Fields set fields to always be logged with keyval pairs
|
||||
Fields(fields ...interface{}) Logger
|
||||
// Info level message
|
||||
Info(ctx context.Context, args ...interface{})
|
||||
// Trace level message
|
||||
@@ -54,6 +54,9 @@ type Logger interface {
|
||||
String() string
|
||||
}
|
||||
|
||||
// Field contains keyval pair
|
||||
type Field interface{}
|
||||
|
||||
// Info writes msg to default logger on info level
|
||||
func Info(ctx context.Context, args ...interface{}) {
|
||||
DefaultLogger.Info(ctx, args...)
|
||||
@@ -125,6 +128,6 @@ func Init(opts ...Option) error {
|
||||
}
|
||||
|
||||
// Fields create logger with specific fields
|
||||
func Fields(fields map[string]interface{}) Logger {
|
||||
return DefaultLogger.Fields(fields)
|
||||
func Fields(fields ...interface{}) Logger {
|
||||
return DefaultLogger.Fields(fields...)
|
||||
}
|
||||
|
@@ -15,7 +15,7 @@ func TestLogger(t *testing.T) {
|
||||
}
|
||||
l.Trace(ctx, "trace_msg1")
|
||||
l.Warn(ctx, "warn_msg1")
|
||||
l.Fields(map[string]interface{}{"error": "test"}).Info(ctx, "error message")
|
||||
l.Fields("error", "test").Info(ctx, "error message")
|
||||
l.Warn(ctx, "first", " ", "second")
|
||||
if !bytes.Contains(buf.Bytes(), []byte(`"level":"trace","msg":"trace_msg1"`)) {
|
||||
t.Fatalf("logger error, buf %s", buf.Bytes())
|
||||
|
@@ -16,7 +16,7 @@ type Options struct {
|
||||
// Context holds exernal options
|
||||
Context context.Context
|
||||
// Fields holds additional metadata
|
||||
Fields map[string]interface{}
|
||||
Fields []interface{}
|
||||
// Name holds the logger name
|
||||
Name string
|
||||
// CallerSkipCount number of frmaes to skip
|
||||
@@ -31,7 +31,7 @@ type Options struct {
|
||||
func NewOptions(opts ...Option) Options {
|
||||
options := Options{
|
||||
Level: DefaultLevel,
|
||||
Fields: make(map[string]interface{}),
|
||||
Fields: make([]interface{}, 0, 6),
|
||||
Out: os.Stderr,
|
||||
CallerSkipCount: DefaultCallerSkipCount,
|
||||
Context: context.Background(),
|
||||
@@ -43,7 +43,7 @@ func NewOptions(opts ...Option) Options {
|
||||
}
|
||||
|
||||
// WithFields set default fields for the logger
|
||||
func WithFields(fields map[string]interface{}) Option {
|
||||
func WithFields(fields ...interface{}) Option {
|
||||
return func(o *Options) {
|
||||
o.Fields = fields
|
||||
}
|
||||
|
@@ -44,8 +44,8 @@ func (w *OmitLogger) Options() Options {
|
||||
return w.l.Options()
|
||||
}
|
||||
|
||||
func (w *OmitLogger) Fields(fields map[string]interface{}) Logger {
|
||||
return w.l.Fields(fields)
|
||||
func (w *OmitLogger) Fields(fields ...interface{}) Logger {
|
||||
return w.l.Fields(fields...)
|
||||
}
|
||||
|
||||
func (w *OmitLogger) Info(ctx context.Context, args ...interface{}) {
|
||||
|
@@ -7,6 +7,7 @@ import (
|
||||
|
||||
"github.com/unistack-org/micro/v3/broker"
|
||||
"github.com/unistack-org/micro/v3/logger"
|
||||
"github.com/unistack-org/micro/v3/metadata"
|
||||
"github.com/unistack-org/micro/v3/network/transport"
|
||||
"github.com/unistack-org/micro/v3/network/tunnel"
|
||||
)
|
||||
|
Reference in New Issue
Block a user