Compare commits
14 Commits
Author | SHA1 | Date | |
---|---|---|---|
1db017d966 | |||
debf8cb03d | |||
1dc9c1891f | |||
930859a537 | |||
3141f1ed8b | |||
47943cfb05 | |||
ed4e9d54b1 | |||
b4b8583594 | |||
fb43e8c58c | |||
8863c10ef4 | |||
8058095bcc | |||
092f5d96b1 | |||
84552513f7 | |||
80a2db264e |
@@ -3,6 +3,7 @@ package client
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"go.unistack.org/micro/v3/broker"
|
||||
@@ -485,29 +486,35 @@ func (n *noopClient) publish(ctx context.Context, ps []Message, opts ...PublishO
|
||||
|
||||
msgs := make([]*broker.Message, 0, len(ps))
|
||||
|
||||
for _, p := range ps {
|
||||
md, ok := metadata.FromOutgoingContext(ctx)
|
||||
if !ok {
|
||||
md = metadata.New(0)
|
||||
}
|
||||
// get proxy
|
||||
exchange := ""
|
||||
if v, ok := os.LookupEnv("MICRO_PROXY"); ok {
|
||||
exchange = v
|
||||
}
|
||||
// get the exchange
|
||||
if len(options.Exchange) > 0 {
|
||||
exchange = options.Exchange
|
||||
}
|
||||
|
||||
omd, ok := metadata.FromOutgoingContext(ctx)
|
||||
if !ok {
|
||||
omd = metadata.New(0)
|
||||
}
|
||||
|
||||
for _, p := range ps {
|
||||
md := metadata.Copy(omd)
|
||||
md[metadata.HeaderContentType] = p.ContentType()
|
||||
topic := p.Topic()
|
||||
if len(exchange) > 0 {
|
||||
topic = exchange
|
||||
}
|
||||
md[metadata.HeaderTopic] = topic
|
||||
iter := p.Metadata().Iterator()
|
||||
var k, v string
|
||||
for iter.Next(&k, &v) {
|
||||
md.Set(k, v)
|
||||
}
|
||||
|
||||
md[metadata.HeaderContentType] = p.ContentType()
|
||||
|
||||
topic := p.Topic()
|
||||
|
||||
// get the exchange
|
||||
if len(options.Exchange) > 0 {
|
||||
topic = options.Exchange
|
||||
}
|
||||
|
||||
md[metadata.HeaderTopic] = topic
|
||||
|
||||
var body []byte
|
||||
|
||||
// passed in raw data
|
||||
|
@@ -39,6 +39,10 @@ func (c *defaultConfig) Init(opts ...Option) error {
|
||||
}
|
||||
|
||||
func (c *defaultConfig) Load(ctx context.Context, opts ...LoadOption) error {
|
||||
if c.opts.SkipLoad != nil && c.opts.SkipLoad(ctx, c) {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := DefaultBeforeLoad(ctx, c); err != nil && !c.opts.AllowFail {
|
||||
return err
|
||||
}
|
||||
@@ -291,7 +295,11 @@ func fillValues(valueOf reflect.Value, tname string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *defaultConfig) Save(ctx context.Context, opts ...SaveOption) error {
|
||||
func (c *defaultConfig) Save(ctx context.Context, _ ...SaveOption) error {
|
||||
if c.opts.SkipSave != nil && c.opts.SkipSave(ctx, c) {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := DefaultBeforeSave(ctx, c); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@@ -42,6 +42,10 @@ type Options struct {
|
||||
AfterInit []func(context.Context, Config) error
|
||||
// AllowFail flag to allow fail in config source
|
||||
AllowFail bool
|
||||
// SkipLoad runs only if condition returns true
|
||||
SkipLoad func(context.Context, Config) bool
|
||||
// SkipSave runs only if condition returns true
|
||||
SkipSave func(context.Context, Config) bool
|
||||
}
|
||||
|
||||
// Option function signature
|
||||
@@ -68,9 +72,9 @@ type LoadOption func(o *LoadOptions)
|
||||
// LoadOptions struct
|
||||
type LoadOptions struct {
|
||||
Struct interface{}
|
||||
Context context.Context
|
||||
Override bool
|
||||
Append bool
|
||||
Context context.Context
|
||||
}
|
||||
|
||||
// NewLoadOptions create LoadOptions struct with provided opts
|
||||
|
@@ -1,230 +0,0 @@
|
||||
package logger
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
type defaultLogger struct {
|
||||
enc *json.Encoder
|
||||
opts Options
|
||||
sync.RWMutex
|
||||
}
|
||||
|
||||
// Init(opts...) should only overwrite provided options
|
||||
func (l *defaultLogger) Init(opts ...Option) error {
|
||||
l.Lock()
|
||||
for _, o := range opts {
|
||||
o(&l.opts)
|
||||
}
|
||||
l.enc = json.NewEncoder(l.opts.Out)
|
||||
// wrap the Log func
|
||||
l.Unlock()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *defaultLogger) String() string {
|
||||
return "micro"
|
||||
}
|
||||
|
||||
func (l *defaultLogger) Clone(opts ...Option) Logger {
|
||||
newopts := NewOptions(opts...)
|
||||
oldopts := l.opts
|
||||
for _, o := range opts {
|
||||
o(&newopts)
|
||||
o(&oldopts)
|
||||
}
|
||||
|
||||
l.Lock()
|
||||
cl := &defaultLogger{opts: oldopts, enc: json.NewEncoder(l.opts.Out)}
|
||||
l.Unlock()
|
||||
|
||||
return cl
|
||||
}
|
||||
|
||||
func (l *defaultLogger) V(level Level) bool {
|
||||
l.RLock()
|
||||
ok := l.opts.Level.Enabled(level)
|
||||
l.RUnlock()
|
||||
return ok
|
||||
}
|
||||
|
||||
func (l *defaultLogger) Level(level Level) {
|
||||
l.Lock()
|
||||
l.opts.Level = level
|
||||
l.Unlock()
|
||||
}
|
||||
|
||||
func (l *defaultLogger) Fields(fields ...interface{}) Logger {
|
||||
l.RLock()
|
||||
nl := &defaultLogger{opts: l.opts, enc: l.enc}
|
||||
if len(fields) == 0 {
|
||||
l.RUnlock()
|
||||
return nl
|
||||
} else if len(fields)%2 != 0 {
|
||||
fields = fields[:len(fields)-1]
|
||||
}
|
||||
nl.opts.Fields = copyFields(l.opts.Fields)
|
||||
nl.opts.Fields = append(nl.opts.Fields, fields...)
|
||||
l.RUnlock()
|
||||
return nl
|
||||
}
|
||||
|
||||
func copyFields(src []interface{}) []interface{} {
|
||||
dst := make([]interface{}, len(src))
|
||||
copy(dst, src)
|
||||
return dst
|
||||
}
|
||||
|
||||
// logCallerfilePath returns a package/file:line description of the caller,
|
||||
// preserving only the leaf directory name and file name.
|
||||
func logCallerfilePath(loggingFilePath string) string {
|
||||
// To make sure we trim the path correctly on Windows too, we
|
||||
// counter-intuitively need to use '/' and *not* os.PathSeparator here,
|
||||
// because the path given originates from Go stdlib, specifically
|
||||
// runtime.Caller() which (as of Mar/17) returns forward slashes even on
|
||||
// Windows.
|
||||
//
|
||||
// See https://github.com/golang/go/issues/3335
|
||||
// and https://github.com/golang/go/issues/18151
|
||||
//
|
||||
// for discussion on the issue on Go side.
|
||||
idx := strings.LastIndexByte(loggingFilePath, '/')
|
||||
if idx == -1 {
|
||||
return loggingFilePath
|
||||
}
|
||||
idx = strings.LastIndexByte(loggingFilePath[:idx], '/')
|
||||
if idx == -1 {
|
||||
return loggingFilePath
|
||||
}
|
||||
return loggingFilePath[idx+1:]
|
||||
}
|
||||
|
||||
func (l *defaultLogger) Info(ctx context.Context, args ...interface{}) {
|
||||
l.Log(ctx, InfoLevel, args...)
|
||||
}
|
||||
|
||||
func (l *defaultLogger) Error(ctx context.Context, args ...interface{}) {
|
||||
l.Log(ctx, ErrorLevel, args...)
|
||||
}
|
||||
|
||||
func (l *defaultLogger) Debug(ctx context.Context, args ...interface{}) {
|
||||
l.Log(ctx, DebugLevel, args...)
|
||||
}
|
||||
|
||||
func (l *defaultLogger) Warn(ctx context.Context, args ...interface{}) {
|
||||
l.Log(ctx, WarnLevel, args...)
|
||||
}
|
||||
|
||||
func (l *defaultLogger) Trace(ctx context.Context, args ...interface{}) {
|
||||
l.Log(ctx, TraceLevel, args...)
|
||||
}
|
||||
|
||||
func (l *defaultLogger) Fatal(ctx context.Context, args ...interface{}) {
|
||||
l.Log(ctx, FatalLevel, args...)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
func (l *defaultLogger) Infof(ctx context.Context, msg string, args ...interface{}) {
|
||||
l.Logf(ctx, InfoLevel, msg, args...)
|
||||
}
|
||||
|
||||
func (l *defaultLogger) Errorf(ctx context.Context, msg string, args ...interface{}) {
|
||||
l.Logf(ctx, ErrorLevel, msg, args...)
|
||||
}
|
||||
|
||||
func (l *defaultLogger) Debugf(ctx context.Context, msg string, args ...interface{}) {
|
||||
l.Logf(ctx, DebugLevel, msg, args...)
|
||||
}
|
||||
|
||||
func (l *defaultLogger) Warnf(ctx context.Context, msg string, args ...interface{}) {
|
||||
l.Logf(ctx, WarnLevel, msg, args...)
|
||||
}
|
||||
|
||||
func (l *defaultLogger) Tracef(ctx context.Context, msg string, args ...interface{}) {
|
||||
l.Logf(ctx, TraceLevel, msg, args...)
|
||||
}
|
||||
|
||||
func (l *defaultLogger) Fatalf(ctx context.Context, msg string, args ...interface{}) {
|
||||
l.Logf(ctx, FatalLevel, msg, args...)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
func (l *defaultLogger) Log(ctx context.Context, level Level, args ...interface{}) {
|
||||
if !l.V(level) {
|
||||
return
|
||||
}
|
||||
|
||||
l.RLock()
|
||||
fields := copyFields(l.opts.Fields)
|
||||
l.RUnlock()
|
||||
|
||||
fields = append(fields, "level", level.String())
|
||||
|
||||
if _, file, line, ok := runtime.Caller(l.opts.CallerSkipCount); ok {
|
||||
fields = append(fields, "caller", fmt.Sprintf("%s:%d", logCallerfilePath(file), line))
|
||||
}
|
||||
fields = append(fields, "timestamp", time.Now().Format("2006-01-02 15:04:05"))
|
||||
|
||||
if len(args) > 0 {
|
||||
fields = append(fields, "msg", fmt.Sprint(args...))
|
||||
}
|
||||
|
||||
out := make(map[string]interface{}, len(fields)/2)
|
||||
for i := 0; i < len(fields); i += 2 {
|
||||
out[fields[i].(string)] = fields[i+1]
|
||||
}
|
||||
l.RLock()
|
||||
_ = l.enc.Encode(out)
|
||||
l.RUnlock()
|
||||
}
|
||||
|
||||
func (l *defaultLogger) Logf(ctx context.Context, level Level, msg string, args ...interface{}) {
|
||||
if !l.V(level) {
|
||||
return
|
||||
}
|
||||
|
||||
l.RLock()
|
||||
fields := copyFields(l.opts.Fields)
|
||||
l.RUnlock()
|
||||
|
||||
fields = append(fields, "level", level.String())
|
||||
|
||||
if _, file, line, ok := runtime.Caller(l.opts.CallerSkipCount); ok {
|
||||
fields = append(fields, "caller", fmt.Sprintf("%s:%d", logCallerfilePath(file), line))
|
||||
}
|
||||
|
||||
fields = append(fields, "timestamp", time.Now().Format("2006-01-02 15:04:05"))
|
||||
if len(args) > 0 {
|
||||
fields = append(fields, "msg", fmt.Sprintf(msg, args...))
|
||||
} else if msg != "" {
|
||||
fields = append(fields, "msg", msg)
|
||||
}
|
||||
|
||||
out := make(map[string]interface{}, len(fields)/2)
|
||||
for i := 0; i < len(fields); i += 2 {
|
||||
out[fields[i].(string)] = fields[i+1]
|
||||
}
|
||||
l.RLock()
|
||||
_ = l.enc.Encode(out)
|
||||
l.RUnlock()
|
||||
}
|
||||
|
||||
func (l *defaultLogger) Options() Options {
|
||||
return l.opts
|
||||
}
|
||||
|
||||
// NewLogger builds a new logger based on options
|
||||
func NewLogger(opts ...Option) Logger {
|
||||
l := &defaultLogger{
|
||||
opts: NewOptions(opts...),
|
||||
}
|
||||
l.enc = json.NewEncoder(l.opts.Out)
|
||||
return l
|
||||
}
|
90
logger/noop.go
Normal file
90
logger/noop.go
Normal file
@@ -0,0 +1,90 @@
|
||||
package logger
|
||||
|
||||
import (
|
||||
"context"
|
||||
)
|
||||
|
||||
type noopLogger struct {
|
||||
opts Options
|
||||
}
|
||||
|
||||
func NewLogger(opts ...Option) Logger {
|
||||
options := NewOptions(opts...)
|
||||
return &noopLogger{opts: options}
|
||||
}
|
||||
|
||||
func (l *noopLogger) V(lvl Level) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (l *noopLogger) Level(lvl Level) {
|
||||
}
|
||||
|
||||
func (l *noopLogger) Init(opts ...Option) error {
|
||||
for _, o := range opts {
|
||||
o(&l.opts)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *noopLogger) Clone(opts ...Option) Logger {
|
||||
nl := &noopLogger{opts: l.opts}
|
||||
for _, o := range opts {
|
||||
o(&nl.opts)
|
||||
}
|
||||
return nl
|
||||
}
|
||||
|
||||
func (l *noopLogger) Fields(attrs ...interface{}) Logger {
|
||||
return l
|
||||
}
|
||||
|
||||
func (l *noopLogger) Options() Options {
|
||||
return l.opts
|
||||
}
|
||||
|
||||
func (l *noopLogger) String() string {
|
||||
return "noop"
|
||||
}
|
||||
|
||||
func (l *noopLogger) Log(ctx context.Context, lvl Level, attrs ...interface{}) {
|
||||
}
|
||||
|
||||
func (l *noopLogger) Info(ctx context.Context, attrs ...interface{}) {
|
||||
}
|
||||
|
||||
func (l *noopLogger) Debug(ctx context.Context, attrs ...interface{}) {
|
||||
}
|
||||
|
||||
func (l *noopLogger) Error(ctx context.Context, attrs ...interface{}) {
|
||||
}
|
||||
|
||||
func (l *noopLogger) Trace(ctx context.Context, attrs ...interface{}) {
|
||||
}
|
||||
|
||||
func (l *noopLogger) Warn(ctx context.Context, attrs ...interface{}) {
|
||||
}
|
||||
|
||||
func (l *noopLogger) Fatal(ctx context.Context, attrs ...interface{}) {
|
||||
}
|
||||
|
||||
func (l *noopLogger) Logf(ctx context.Context, lvl Level, msg string, attrs ...interface{}) {
|
||||
}
|
||||
|
||||
func (l *noopLogger) Infof(ctx context.Context, msg string, attrs ...interface{}) {
|
||||
}
|
||||
|
||||
func (l *noopLogger) Debugf(ctx context.Context, msg string, attrs ...interface{}) {
|
||||
}
|
||||
|
||||
func (l *noopLogger) Errorf(ctx context.Context, msg string, attrs ...interface{}) {
|
||||
}
|
||||
|
||||
func (l *noopLogger) Tracef(ctx context.Context, msg string, attrs ...interface{}) {
|
||||
}
|
||||
|
||||
func (l *noopLogger) Warnf(ctx context.Context, msg string, attrs ...interface{}) {
|
||||
}
|
||||
|
||||
func (l *noopLogger) Fatalf(ctx context.Context, msg string, attrs ...interface{}) {
|
||||
}
|
27
logger/slog/options.go
Normal file
27
logger/slog/options.go
Normal file
@@ -0,0 +1,27 @@
|
||||
package slog
|
||||
|
||||
import "go.unistack.org/micro/v3/logger"
|
||||
|
||||
type sourceKey struct{}
|
||||
|
||||
func WithSourceKey(v string) logger.Option {
|
||||
return logger.SetOption(sourceKey{}, v)
|
||||
}
|
||||
|
||||
type timeKey struct{}
|
||||
|
||||
func WithTimeKey(v string) logger.Option {
|
||||
return logger.SetOption(timeKey{}, v)
|
||||
}
|
||||
|
||||
type messageKey struct{}
|
||||
|
||||
func WithMessageKey(v string) logger.Option {
|
||||
return logger.SetOption(messageKey{}, v)
|
||||
}
|
||||
|
||||
type levelKey struct{}
|
||||
|
||||
func WithLevelKey(v string) logger.Option {
|
||||
return logger.SetOption(levelKey{}, v)
|
||||
}
|
419
logger/slog/slog.go
Normal file
419
logger/slog/slog.go
Normal file
@@ -0,0 +1,419 @@
|
||||
package slog
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"os"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"go.unistack.org/micro/v3/logger"
|
||||
"go.unistack.org/micro/v3/tracer"
|
||||
)
|
||||
|
||||
var (
|
||||
DefaultSourceKey string = slog.SourceKey
|
||||
DefaultTimeKey string = slog.TimeKey
|
||||
DefaultMessageKey string = slog.MessageKey
|
||||
DefaultLevelKey string = slog.LevelKey
|
||||
)
|
||||
|
||||
var (
|
||||
traceValue = slog.StringValue("trace")
|
||||
debugValue = slog.StringValue("debug")
|
||||
infoValue = slog.StringValue("info")
|
||||
warnValue = slog.StringValue("warn")
|
||||
errorValue = slog.StringValue("error")
|
||||
fatalValue = slog.StringValue("fatal")
|
||||
)
|
||||
|
||||
func (s *slogLogger) renameAttr(_ []string, a slog.Attr) slog.Attr {
|
||||
switch a.Key {
|
||||
case slog.SourceKey:
|
||||
source := a.Value.Any().(*slog.Source)
|
||||
a.Value = slog.StringValue(source.File + ":" + strconv.Itoa(source.Line))
|
||||
a.Key = s.sourceKey
|
||||
case slog.TimeKey:
|
||||
a.Key = s.timeKey
|
||||
case slog.MessageKey:
|
||||
a.Key = s.messageKey
|
||||
case slog.LevelKey:
|
||||
level := a.Value.Any().(slog.Level)
|
||||
lvl := slogToLoggerLevel(level)
|
||||
a.Key = s.levelKey
|
||||
switch {
|
||||
case lvl < logger.DebugLevel:
|
||||
a.Value = traceValue
|
||||
case lvl < logger.InfoLevel:
|
||||
a.Value = debugValue
|
||||
case lvl < logger.WarnLevel:
|
||||
a.Value = infoValue
|
||||
case lvl < logger.ErrorLevel:
|
||||
a.Value = warnValue
|
||||
case lvl < logger.FatalLevel:
|
||||
a.Value = errorValue
|
||||
case lvl >= logger.FatalLevel:
|
||||
a.Value = fatalValue
|
||||
default:
|
||||
a.Value = infoValue
|
||||
}
|
||||
}
|
||||
|
||||
return a
|
||||
}
|
||||
|
||||
type slogLogger struct {
|
||||
slog *slog.Logger
|
||||
leveler *slog.LevelVar
|
||||
levelKey string
|
||||
messageKey string
|
||||
sourceKey string
|
||||
timeKey string
|
||||
opts logger.Options
|
||||
}
|
||||
|
||||
func (s *slogLogger) Clone(opts ...logger.Option) logger.Logger {
|
||||
options := s.opts
|
||||
|
||||
for _, o := range opts {
|
||||
o(&options)
|
||||
}
|
||||
|
||||
l := &slogLogger{
|
||||
opts: options,
|
||||
levelKey: s.levelKey,
|
||||
messageKey: s.messageKey,
|
||||
sourceKey: s.sourceKey,
|
||||
timeKey: s.timeKey,
|
||||
}
|
||||
|
||||
if v, ok := l.opts.Context.Value(levelKey{}).(string); ok && v != "" {
|
||||
l.levelKey = v
|
||||
}
|
||||
if v, ok := l.opts.Context.Value(messageKey{}).(string); ok && v != "" {
|
||||
l.messageKey = v
|
||||
}
|
||||
if v, ok := l.opts.Context.Value(sourceKey{}).(string); ok && v != "" {
|
||||
l.sourceKey = v
|
||||
}
|
||||
if v, ok := l.opts.Context.Value(timeKey{}).(string); ok && v != "" {
|
||||
l.timeKey = v
|
||||
}
|
||||
|
||||
l.leveler = new(slog.LevelVar)
|
||||
handleOpt := &slog.HandlerOptions{
|
||||
ReplaceAttr: s.renameAttr,
|
||||
Level: l.leveler,
|
||||
AddSource: true,
|
||||
}
|
||||
l.leveler.Set(loggerToSlogLevel(l.opts.Level))
|
||||
handler := slog.NewJSONHandler(options.Out, handleOpt)
|
||||
l.slog = slog.New(handler).With(options.Fields...)
|
||||
|
||||
return l
|
||||
}
|
||||
|
||||
func (s *slogLogger) V(level logger.Level) bool {
|
||||
return s.opts.Level.Enabled(level)
|
||||
}
|
||||
|
||||
func (s *slogLogger) Level(level logger.Level) {
|
||||
s.leveler.Set(loggerToSlogLevel(level))
|
||||
}
|
||||
|
||||
func (s *slogLogger) Options() logger.Options {
|
||||
return s.opts
|
||||
}
|
||||
|
||||
func (s *slogLogger) Fields(attrs ...interface{}) logger.Logger {
|
||||
nl := &slogLogger{
|
||||
opts: s.opts,
|
||||
levelKey: s.levelKey,
|
||||
messageKey: s.messageKey,
|
||||
sourceKey: s.sourceKey,
|
||||
timeKey: s.timeKey,
|
||||
}
|
||||
nl.leveler = new(slog.LevelVar)
|
||||
nl.leveler.Set(s.leveler.Level())
|
||||
|
||||
handleOpt := &slog.HandlerOptions{
|
||||
ReplaceAttr: nl.renameAttr,
|
||||
Level: s.leveler,
|
||||
AddSource: true,
|
||||
}
|
||||
|
||||
handler := slog.NewJSONHandler(s.opts.Out, handleOpt)
|
||||
nl.slog = slog.New(handler).With(attrs...)
|
||||
|
||||
return nl
|
||||
}
|
||||
|
||||
func (s *slogLogger) Init(opts ...logger.Option) error {
|
||||
for _, o := range opts {
|
||||
o(&s.opts)
|
||||
}
|
||||
|
||||
if v, ok := s.opts.Context.Value(levelKey{}).(string); ok && v != "" {
|
||||
s.levelKey = v
|
||||
}
|
||||
if v, ok := s.opts.Context.Value(messageKey{}).(string); ok && v != "" {
|
||||
s.messageKey = v
|
||||
}
|
||||
if v, ok := s.opts.Context.Value(sourceKey{}).(string); ok && v != "" {
|
||||
s.sourceKey = v
|
||||
}
|
||||
if v, ok := s.opts.Context.Value(timeKey{}).(string); ok && v != "" {
|
||||
s.timeKey = v
|
||||
}
|
||||
|
||||
s.leveler = new(slog.LevelVar)
|
||||
handleOpt := &slog.HandlerOptions{
|
||||
ReplaceAttr: s.renameAttr,
|
||||
Level: s.leveler,
|
||||
AddSource: true,
|
||||
}
|
||||
s.leveler.Set(loggerToSlogLevel(s.opts.Level))
|
||||
handler := slog.NewJSONHandler(s.opts.Out, handleOpt)
|
||||
s.slog = slog.New(handler).With(s.opts.Fields...)
|
||||
|
||||
slog.SetDefault(s.slog)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *slogLogger) Log(ctx context.Context, lvl logger.Level, attrs ...interface{}) {
|
||||
if !s.V(lvl) {
|
||||
return
|
||||
}
|
||||
var pcs [1]uintptr
|
||||
runtime.Callers(s.opts.CallerSkipCount, pcs[:]) // skip [Callers, Infof]
|
||||
r := slog.NewRecord(time.Now(), loggerToSlogLevel(lvl), fmt.Sprintf("%s", attrs[0]), pcs[0])
|
||||
// r.Add(attrs[1:]...)
|
||||
_ = s.slog.Handler().Handle(ctx, r)
|
||||
}
|
||||
|
||||
func (s *slogLogger) Logf(ctx context.Context, lvl logger.Level, msg string, attrs ...interface{}) {
|
||||
if !s.V(lvl) {
|
||||
return
|
||||
}
|
||||
var pcs [1]uintptr
|
||||
runtime.Callers(s.opts.CallerSkipCount, pcs[:]) // skip [Callers, Infof]
|
||||
r := slog.NewRecord(time.Now(), loggerToSlogLevel(lvl), fmt.Sprintf(msg, attrs...), pcs[0])
|
||||
// r.Add(attrs...)
|
||||
_ = s.slog.Handler().Handle(ctx, r)
|
||||
}
|
||||
|
||||
func (s *slogLogger) Info(ctx context.Context, attrs ...interface{}) {
|
||||
if !s.V(logger.InfoLevel) {
|
||||
return
|
||||
}
|
||||
var pcs [1]uintptr
|
||||
runtime.Callers(s.opts.CallerSkipCount, pcs[:]) // skip [Callers, Infof]
|
||||
r := slog.NewRecord(time.Now(), slog.LevelInfo, fmt.Sprintf("%s", attrs[0]), pcs[0])
|
||||
// r.Add(attrs[1:]...)
|
||||
_ = s.slog.Handler().Handle(ctx, r)
|
||||
}
|
||||
|
||||
func (s *slogLogger) Infof(ctx context.Context, msg string, attrs ...interface{}) {
|
||||
if !s.V(logger.InfoLevel) {
|
||||
return
|
||||
}
|
||||
var pcs [1]uintptr
|
||||
runtime.Callers(s.opts.CallerSkipCount, pcs[:]) // skip [Callers, Infof]
|
||||
r := slog.NewRecord(time.Now(), slog.LevelInfo, fmt.Sprintf(msg, attrs...), pcs[0])
|
||||
// r.Add(attrs...)
|
||||
_ = s.slog.Handler().Handle(ctx, r)
|
||||
}
|
||||
|
||||
func (s *slogLogger) Debug(ctx context.Context, attrs ...interface{}) {
|
||||
if !s.V(logger.DebugLevel) {
|
||||
return
|
||||
}
|
||||
var pcs [1]uintptr
|
||||
runtime.Callers(s.opts.CallerSkipCount, pcs[:]) // skip [Callers, Infof]
|
||||
r := slog.NewRecord(time.Now(), slog.LevelDebug, fmt.Sprintf("%s", attrs[0]), pcs[0])
|
||||
// r.Add(attrs[1:]...)
|
||||
_ = s.slog.Handler().Handle(ctx, r)
|
||||
}
|
||||
|
||||
func (s *slogLogger) Debugf(ctx context.Context, msg string, attrs ...interface{}) {
|
||||
if !s.V(logger.DebugLevel) {
|
||||
return
|
||||
}
|
||||
var pcs [1]uintptr
|
||||
runtime.Callers(s.opts.CallerSkipCount, pcs[:]) // skip [Callers, Infof]
|
||||
r := slog.NewRecord(time.Now(), slog.LevelDebug, fmt.Sprintf(msg, attrs...), pcs[0])
|
||||
// r.Add(attrs...)
|
||||
_ = s.slog.Handler().Handle(ctx, r)
|
||||
}
|
||||
|
||||
func (s *slogLogger) Trace(ctx context.Context, attrs ...interface{}) {
|
||||
if !s.V(logger.TraceLevel) {
|
||||
return
|
||||
}
|
||||
var pcs [1]uintptr
|
||||
runtime.Callers(s.opts.CallerSkipCount, pcs[:]) // skip [Callers, Infof]
|
||||
r := slog.NewRecord(time.Now(), slog.LevelDebug-1, fmt.Sprintf("%s", attrs[0]), pcs[0])
|
||||
// r.Add(attrs[1:]...)
|
||||
_ = s.slog.Handler().Handle(ctx, r)
|
||||
}
|
||||
|
||||
func (s *slogLogger) Tracef(ctx context.Context, msg string, attrs ...interface{}) {
|
||||
if !s.V(logger.TraceLevel) {
|
||||
return
|
||||
}
|
||||
var pcs [1]uintptr
|
||||
runtime.Callers(s.opts.CallerSkipCount, pcs[:]) // skip [Callers, Infof]
|
||||
r := slog.NewRecord(time.Now(), slog.LevelDebug-1, fmt.Sprintf(msg, attrs...), pcs[0])
|
||||
// r.Add(attrs...)
|
||||
_ = s.slog.Handler().Handle(ctx, r)
|
||||
}
|
||||
|
||||
func (s *slogLogger) Error(ctx context.Context, attrs ...interface{}) {
|
||||
if !s.V(logger.ErrorLevel) {
|
||||
return
|
||||
}
|
||||
var pcs [1]uintptr
|
||||
runtime.Callers(s.opts.CallerSkipCount, pcs[:]) // skip [Callers, Infof]
|
||||
r := slog.NewRecord(time.Now(), slog.LevelError, fmt.Sprintf("%s", attrs[0]), pcs[0])
|
||||
// r.Add(attrs[1:]...)
|
||||
r.Attrs(func(a slog.Attr) bool {
|
||||
if a.Key == "error" {
|
||||
if span, ok := tracer.SpanFromContext(ctx); ok {
|
||||
span.SetStatus(tracer.SpanStatusError, a.Value.String())
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
})
|
||||
_ = s.slog.Handler().Handle(ctx, r)
|
||||
}
|
||||
|
||||
func (s *slogLogger) Errorf(ctx context.Context, msg string, attrs ...interface{}) {
|
||||
if !s.V(logger.ErrorLevel) {
|
||||
return
|
||||
}
|
||||
var pcs [1]uintptr
|
||||
runtime.Callers(s.opts.CallerSkipCount, pcs[:]) // skip [Callers, Infof]
|
||||
r := slog.NewRecord(time.Now(), slog.LevelError, fmt.Sprintf(msg, attrs...), pcs[0])
|
||||
// r.Add(attrs...)
|
||||
r.Attrs(func(a slog.Attr) bool {
|
||||
if a.Key == "error" {
|
||||
if span, ok := tracer.SpanFromContext(ctx); ok {
|
||||
span.SetStatus(tracer.SpanStatusError, a.Value.String())
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
})
|
||||
_ = s.slog.Handler().Handle(ctx, r)
|
||||
}
|
||||
|
||||
func (s *slogLogger) Fatal(ctx context.Context, attrs ...interface{}) {
|
||||
if !s.V(logger.FatalLevel) {
|
||||
return
|
||||
}
|
||||
var pcs [1]uintptr
|
||||
runtime.Callers(s.opts.CallerSkipCount, pcs[:]) // skip [Callers, Infof]
|
||||
r := slog.NewRecord(time.Now(), slog.LevelError+1, fmt.Sprintf("%s", attrs[0]), pcs[0])
|
||||
// r.Add(attrs[1:]...)
|
||||
_ = s.slog.Handler().Handle(ctx, r)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
func (s *slogLogger) Fatalf(ctx context.Context, msg string, attrs ...interface{}) {
|
||||
if !s.V(logger.FatalLevel) {
|
||||
return
|
||||
}
|
||||
var pcs [1]uintptr
|
||||
runtime.Callers(s.opts.CallerSkipCount, pcs[:]) // skip [Callers, Infof]
|
||||
r := slog.NewRecord(time.Now(), slog.LevelError+1, fmt.Sprintf(msg, attrs...), pcs[0])
|
||||
// r.Add(attrs...)
|
||||
_ = s.slog.Handler().Handle(ctx, r)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
func (s *slogLogger) Warn(ctx context.Context, attrs ...interface{}) {
|
||||
if !s.V(logger.WarnLevel) {
|
||||
return
|
||||
}
|
||||
var pcs [1]uintptr
|
||||
runtime.Callers(s.opts.CallerSkipCount, pcs[:]) // skip [Callers, Infof]
|
||||
r := slog.NewRecord(time.Now(), slog.LevelWarn, fmt.Sprintf("%s", attrs[0]), pcs[0])
|
||||
// r.Add(attrs[1:]...)
|
||||
_ = s.slog.Handler().Handle(ctx, r)
|
||||
}
|
||||
|
||||
func (s *slogLogger) Warnf(ctx context.Context, msg string, attrs ...interface{}) {
|
||||
if !s.V(logger.WarnLevel) {
|
||||
return
|
||||
}
|
||||
var pcs [1]uintptr
|
||||
runtime.Callers(s.opts.CallerSkipCount, pcs[:]) // skip [Callers, Infof]
|
||||
r := slog.NewRecord(time.Now(), slog.LevelWarn, fmt.Sprintf(msg, attrs...), pcs[0])
|
||||
// r.Add(attrs...)
|
||||
_ = s.slog.Handler().Handle(ctx, r)
|
||||
}
|
||||
|
||||
func (s *slogLogger) String() string {
|
||||
return "slog"
|
||||
}
|
||||
|
||||
func NewLogger(opts ...logger.Option) logger.Logger {
|
||||
s := &slogLogger{
|
||||
opts: logger.NewOptions(opts...),
|
||||
sourceKey: DefaultSourceKey,
|
||||
timeKey: DefaultTimeKey,
|
||||
messageKey: DefaultMessageKey,
|
||||
levelKey: DefaultLevelKey,
|
||||
}
|
||||
if v, ok := s.opts.Context.Value(levelKey{}).(string); ok && v != "" {
|
||||
s.levelKey = v
|
||||
}
|
||||
if v, ok := s.opts.Context.Value(messageKey{}).(string); ok && v != "" {
|
||||
s.messageKey = v
|
||||
}
|
||||
if v, ok := s.opts.Context.Value(sourceKey{}).(string); ok && v != "" {
|
||||
s.sourceKey = v
|
||||
}
|
||||
if v, ok := s.opts.Context.Value(timeKey{}).(string); ok && v != "" {
|
||||
s.timeKey = v
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func loggerToSlogLevel(level logger.Level) slog.Level {
|
||||
switch level {
|
||||
case logger.DebugLevel:
|
||||
return slog.LevelDebug
|
||||
case logger.WarnLevel:
|
||||
return slog.LevelWarn
|
||||
case logger.ErrorLevel:
|
||||
return slog.LevelError
|
||||
case logger.TraceLevel:
|
||||
return slog.LevelDebug - 1
|
||||
case logger.FatalLevel:
|
||||
return slog.LevelError + 1
|
||||
default:
|
||||
return slog.LevelInfo
|
||||
}
|
||||
}
|
||||
|
||||
func slogToLoggerLevel(level slog.Level) logger.Level {
|
||||
switch level {
|
||||
case slog.LevelDebug:
|
||||
return logger.DebugLevel
|
||||
case slog.LevelWarn:
|
||||
return logger.WarnLevel
|
||||
case slog.LevelError:
|
||||
return logger.ErrorLevel
|
||||
case slog.LevelDebug - 1:
|
||||
return logger.TraceLevel
|
||||
case slog.LevelError + 1:
|
||||
return logger.FatalLevel
|
||||
default:
|
||||
return logger.InfoLevel
|
||||
}
|
||||
}
|
141
logger/slog/slog_test.go
Normal file
141
logger/slog/slog_test.go
Normal file
@@ -0,0 +1,141 @@
|
||||
package slog
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"log"
|
||||
"testing"
|
||||
|
||||
"go.unistack.org/micro/v3/logger"
|
||||
)
|
||||
|
||||
func TestContext(t *testing.T) {
|
||||
ctx := context.TODO()
|
||||
buf := bytes.NewBuffer(nil)
|
||||
l := NewLogger(logger.WithLevel(logger.TraceLevel), logger.WithOutput(buf))
|
||||
if err := l.Init(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
nl, ok := logger.FromContext(logger.NewContext(ctx, l.Fields("key", "val")))
|
||||
if !ok {
|
||||
t.Fatal("context without logger")
|
||||
}
|
||||
nl.Info(ctx, "message")
|
||||
if !bytes.Contains(buf.Bytes(), []byte(`"key":"val"`)) {
|
||||
t.Fatalf("logger fields not works, buf contains: %s", buf.Bytes())
|
||||
}
|
||||
}
|
||||
|
||||
func TestFields(t *testing.T) {
|
||||
ctx := context.TODO()
|
||||
buf := bytes.NewBuffer(nil)
|
||||
l := NewLogger(logger.WithLevel(logger.TraceLevel), logger.WithOutput(buf))
|
||||
if err := l.Init(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
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(logger.WithLevel(logger.TraceLevel), logger.WithOutput(buf))
|
||||
if err := l.Init(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
nl := l.Fields("key", "val")
|
||||
|
||||
ctx = logger.NewContext(ctx, nl)
|
||||
|
||||
l, ok = logger.FromContext(ctx)
|
||||
if !ok {
|
||||
t.Fatalf("context does not have logger")
|
||||
}
|
||||
|
||||
l.Info(ctx, "message")
|
||||
if !bytes.Contains(buf.Bytes(), []byte(`"key":"val"`)) {
|
||||
t.Fatalf("logger fields not works, buf contains: %s", buf.Bytes())
|
||||
}
|
||||
}
|
||||
|
||||
func TestClone(t *testing.T) {
|
||||
ctx := context.TODO()
|
||||
buf := bytes.NewBuffer(nil)
|
||||
l := NewLogger(logger.WithLevel(logger.TraceLevel), logger.WithOutput(buf))
|
||||
if err := l.Init(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
nl := l.Clone(logger.WithLevel(logger.ErrorLevel))
|
||||
if err := nl.Init(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
nl.Info(ctx, "info message")
|
||||
if len(buf.Bytes()) != 0 {
|
||||
t.Fatal("message must not be logged")
|
||||
}
|
||||
l.Info(ctx, "info message")
|
||||
if len(buf.Bytes()) == 0 {
|
||||
t.Fatal("message must be logged")
|
||||
}
|
||||
}
|
||||
|
||||
func TestRedirectStdLogger(t *testing.T) {
|
||||
buf := bytes.NewBuffer(nil)
|
||||
l := NewLogger(logger.WithLevel(logger.ErrorLevel), logger.WithOutput(buf))
|
||||
if err := l.Init(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
fn := logger.RedirectStdLogger(l, logger.ErrorLevel)
|
||||
defer fn()
|
||||
log.Print("test")
|
||||
if !(bytes.Contains(buf.Bytes(), []byte(`"level":"error"`)) && bytes.Contains(buf.Bytes(), []byte(`"msg":"test"`))) {
|
||||
t.Fatalf("logger error, buf %s", buf.Bytes())
|
||||
}
|
||||
}
|
||||
|
||||
func TestStdLogger(t *testing.T) {
|
||||
buf := bytes.NewBuffer(nil)
|
||||
l := NewLogger(logger.WithLevel(logger.TraceLevel), logger.WithOutput(buf))
|
||||
if err := l.Init(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
lg := logger.NewStdLogger(l, logger.ErrorLevel)
|
||||
lg.Print("test")
|
||||
if !(bytes.Contains(buf.Bytes(), []byte(`"level":"error"`)) && bytes.Contains(buf.Bytes(), []byte(`"msg":"test"`))) {
|
||||
t.Fatalf("logger error, buf %s", buf.Bytes())
|
||||
}
|
||||
}
|
||||
|
||||
func TestLogger(t *testing.T) {
|
||||
ctx := context.TODO()
|
||||
buf := bytes.NewBuffer(nil)
|
||||
l := NewLogger(logger.WithLevel(logger.TraceLevel), logger.WithOutput(buf))
|
||||
if err := l.Init(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
l.Trace(ctx, "trace_msg1")
|
||||
l.Warn(ctx, "warn_msg1")
|
||||
l.Fields("error", "test").Info(ctx, "error message")
|
||||
l.Warn(ctx, "first second")
|
||||
|
||||
if !(bytes.Contains(buf.Bytes(), []byte(`"level":"trace"`)) && bytes.Contains(buf.Bytes(), []byte(`"msg":"trace_msg1"`))) {
|
||||
t.Fatalf("logger tracer, buf %s", buf.Bytes())
|
||||
}
|
||||
if !(bytes.Contains(buf.Bytes(), []byte(`"level":"warn"`)) && bytes.Contains(buf.Bytes(), []byte(`"msg":"warn_msg1"`))) {
|
||||
t.Fatalf("logger warn, buf %s", buf.Bytes())
|
||||
}
|
||||
if !(bytes.Contains(buf.Bytes(), []byte(`"level":"info"`)) && bytes.Contains(buf.Bytes(), []byte(`"msg":"error message","error":"test"`))) {
|
||||
t.Fatalf("logger info, buf %s", buf.Bytes())
|
||||
}
|
||||
if !(bytes.Contains(buf.Bytes(), []byte(`"level":"warn"`)) && bytes.Contains(buf.Bytes(), []byte(`"msg":"first second"`))) {
|
||||
t.Fatalf("logger warn, buf %s", buf.Bytes())
|
||||
}
|
||||
}
|
@@ -342,9 +342,6 @@ func (n *noopServer) createSubHandler(sb *subscriber, opts Options) broker.Handl
|
||||
|
||||
hdr := metadata.New(len(msg.Header))
|
||||
for k, v := range msg.Header {
|
||||
if k == "Content-Type" {
|
||||
continue
|
||||
}
|
||||
hdr.Set(k, v)
|
||||
}
|
||||
|
||||
|
75
service.go
75
service.go
@@ -88,6 +88,7 @@ func (s *service) Name() string {
|
||||
// Init initialises options. Additionally it calls cmd.Init
|
||||
// which parses command line flags. cmd.Init is only called
|
||||
// on first Init.
|
||||
//
|
||||
//nolint:gocyclo
|
||||
func (s *service) Init(opts ...Option) error {
|
||||
var err error
|
||||
@@ -375,19 +376,71 @@ func (s *service) Run() error {
|
||||
return s.Stop()
|
||||
}
|
||||
|
||||
type nameIface interface {
|
||||
Name() string
|
||||
}
|
||||
|
||||
func getNameIndex(n string, ifaces interface{}) int {
|
||||
values, ok := ifaces.([]interface{})
|
||||
if !ok {
|
||||
return 0
|
||||
}
|
||||
for idx, iface := range values {
|
||||
if ifc, ok := iface.(nameIface); ok && ifc.Name() == n {
|
||||
return idx
|
||||
switch values := ifaces.(type) {
|
||||
case []router.Router:
|
||||
for idx, iface := range values {
|
||||
if iface.Name() == n {
|
||||
return idx
|
||||
}
|
||||
}
|
||||
case []register.Register:
|
||||
for idx, iface := range values {
|
||||
if iface.Name() == n {
|
||||
return idx
|
||||
}
|
||||
}
|
||||
case []store.Store:
|
||||
for idx, iface := range values {
|
||||
if iface.Name() == n {
|
||||
return idx
|
||||
}
|
||||
}
|
||||
case []tracer.Tracer:
|
||||
for idx, iface := range values {
|
||||
if iface.Name() == n {
|
||||
return idx
|
||||
}
|
||||
}
|
||||
case []server.Server:
|
||||
for idx, iface := range values {
|
||||
if iface.Name() == n {
|
||||
return idx
|
||||
}
|
||||
}
|
||||
case []config.Config:
|
||||
for idx, iface := range values {
|
||||
if iface.Name() == n {
|
||||
return idx
|
||||
}
|
||||
}
|
||||
case []meter.Meter:
|
||||
for idx, iface := range values {
|
||||
if iface.Name() == n {
|
||||
return idx
|
||||
}
|
||||
}
|
||||
case []broker.Broker:
|
||||
for idx, iface := range values {
|
||||
if iface.Name() == n {
|
||||
return idx
|
||||
}
|
||||
}
|
||||
case []client.Client:
|
||||
for idx, iface := range values {
|
||||
if iface.Name() == n {
|
||||
return idx
|
||||
}
|
||||
}
|
||||
/*
|
||||
case []logger.Logger:
|
||||
for idx, iface := range values {
|
||||
if iface.Name() == n {
|
||||
return idx
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
@@ -17,6 +17,21 @@ import (
|
||||
"go.unistack.org/micro/v3/tracer"
|
||||
)
|
||||
|
||||
func TestClient(t *testing.T) {
|
||||
c1 := client.NewClient(client.Name("test1"))
|
||||
c2 := client.NewClient(client.Name("test2"))
|
||||
|
||||
svc := NewService(Client(c1, c2))
|
||||
if err := svc.Init(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
x1 := svc.Client("test2")
|
||||
if x1.Name() != "test2" {
|
||||
t.Fatal("invalid client")
|
||||
}
|
||||
}
|
||||
|
||||
type testItem struct {
|
||||
name string
|
||||
}
|
||||
|
Reference in New Issue
Block a user