logger: improvements #270
| @@ -1,5 +1,5 @@ | ||||
| // Package logger provides a log interface | ||||
| package logger // import "go.unistack.org/micro/v4/logger" | ||||
| package logger | ||||
|  | ||||
| import ( | ||||
| 	"context" | ||||
| @@ -8,19 +8,22 @@ import ( | ||||
| 	"go.unistack.org/micro/v4/options" | ||||
| ) | ||||
|  | ||||
| type ContextAttrFunc func(ctx context.Context) []interface{} | ||||
|  | ||||
| var DefaultContextAttrFuncs []ContextAttrFunc | ||||
|  | ||||
| var ( | ||||
| 	// DefaultLogger variable | ||||
| 	DefaultLogger = NewLogger(WithLevel(ParseLevel(os.Getenv("MICRO_LOG_LEVEL")))) | ||||
| 	DefaultLogger = NewLogger( | ||||
| 		WithLevel(ParseLevel(os.Getenv("MICRO_LOG_LEVEL"))), | ||||
| 		WithContextAttrFuncs(DefaultContextAttrFuncs...), | ||||
| 	) | ||||
| 	// DefaultLevel used by logger | ||||
| 	DefaultLevel = InfoLevel | ||||
| 	// DefaultCallerSkipCount used by logger | ||||
| 	DefaultCallerSkipCount = 2 | ||||
| ) | ||||
|  | ||||
| type ContextAttrFunc func(ctx context.Context) []interface{} | ||||
|  | ||||
| var DefaultContextAttrFuncs []ContextAttrFunc | ||||
|  | ||||
| // Logger is a generic logging interface | ||||
| type Logger interface { | ||||
| 	// Init initialises options | ||||
|   | ||||
| @@ -2,12 +2,14 @@ package logger | ||||
|  | ||||
| import ( | ||||
| 	"context" | ||||
| 	"fmt" | ||||
| 	"io" | ||||
| 	"os" | ||||
| 	"reflect" | ||||
|  | ||||
| 	"go.unistack.org/micro/v4/options" | ||||
| 	rutil "go.unistack.org/micro/v4/util/reflect" | ||||
| 	"golang.org/x/exp/slog" | ||||
| ) | ||||
|  | ||||
| // Options holds logger options | ||||
| @@ -26,6 +28,14 @@ type Options struct { | ||||
| 	CallerSkipCount int | ||||
| 	// ContextAttrFuncs contains funcs that executed before log func on context | ||||
| 	ContextAttrFuncs []ContextAttrFunc | ||||
| 	// TimeKey is the key used for the time of the log call | ||||
| 	TimeKey string | ||||
| 	// LevelKey is the key used for the level of the log call | ||||
| 	LevelKey string | ||||
| 	// MessageKey is the key used for the message of the log call | ||||
| 	MessageKey string | ||||
| 	// SourceKey is the key used for the source file and line of the log call | ||||
| 	SourceKey string | ||||
| } | ||||
|  | ||||
| // NewOptions creates new options struct | ||||
| @@ -38,6 +48,9 @@ func NewOptions(opts ...options.Option) Options { | ||||
| 		Context:          context.Background(), | ||||
| 		ContextAttrFuncs: DefaultContextAttrFuncs, | ||||
| 	} | ||||
|  | ||||
| 	WithMicroKeys()(&options) | ||||
|  | ||||
| 	for _, o := range opts { | ||||
| 		o(&options) | ||||
| 	} | ||||
| @@ -45,18 +58,19 @@ func NewOptions(opts ...options.Option) Options { | ||||
| } | ||||
|  | ||||
| // WithContextAttrFuncs appends default funcs for the context arrts filler | ||||
| func WithContextAttrFuncs(attrs ...interface{}) options.Option { | ||||
| func WithContextAttrFuncs(fncs ...ContextAttrFunc) options.Option { | ||||
| 	return func(src interface{}) error { | ||||
| 		v, err := options.Get(src, ".ContextAttrFuncs") | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} else if rutil.IsZero(v) { | ||||
| 			v = reflect.MakeSlice(reflect.TypeOf(v), 0, len(attrs)).Interface() | ||||
| 			v = reflect.MakeSlice(reflect.TypeOf(v), 0, len(fncs)).Interface() | ||||
| 		} | ||||
| 		cv := reflect.ValueOf(v) | ||||
| 		for _, l := range attrs { | ||||
| 		for _, l := range fncs { | ||||
| 			cv = reflect.Append(cv, reflect.ValueOf(l)) | ||||
| 		} | ||||
| 		fmt.Printf("EEEE %#+v\n", cv.Interface()) | ||||
| 		return options.Set(src, cv.Interface(), ".ContextAttrFuncs") | ||||
| 	} | ||||
| } | ||||
| @@ -88,3 +102,79 @@ func WithCallerSkipCount(c int) options.Option { | ||||
| 		return options.Set(src, c, ".CallerSkipCount") | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func WithZapKeys() options.Option { | ||||
| 	return func(src interface{}) error { | ||||
| 		var err error | ||||
| 		if err = options.Set(src, "@timestamp", ".TimeKey"); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		if err = options.Set(src, "level", ".LevelKey"); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		if err = options.Set(src, "msg", ".MessageKey"); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		if err = options.Set(src, "caller", ".SourceKey"); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		return nil | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func WithZerologKeys() options.Option { | ||||
| 	return func(src interface{}) error { | ||||
| 		var err error | ||||
| 		if err = options.Set(src, "time", ".TimeKey"); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		if err = options.Set(src, "level", ".LevelKey"); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		if err = options.Set(src, "message", ".MessageKey"); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		if err = options.Set(src, "caller", ".SourceKey"); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		return nil | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func WithSlogKeys() options.Option { | ||||
| 	return func(src interface{}) error { | ||||
| 		var err error | ||||
| 		if err = options.Set(src, slog.TimeKey, ".TimeKey"); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		if err = options.Set(src, slog.LevelKey, ".LevelKey"); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		if err = options.Set(src, slog.MessageKey, ".MessageKey"); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		if err = options.Set(src, slog.SourceKey, ".SourceKey"); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		return nil | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func WithMicroKeys() options.Option { | ||||
| 	return func(src interface{}) error { | ||||
| 		var err error | ||||
| 		if err = options.Set(src, "timestamp", ".TimeKey"); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		if err = options.Set(src, "level", ".LevelKey"); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		if err = options.Set(src, "msg", ".MessageKey"); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		if err = options.Set(src, "caller", ".SourceKey"); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		return nil | ||||
| 	} | ||||
| } | ||||
|   | ||||
| @@ -117,12 +117,14 @@ func (s *slogLogger) Attrs(attrs ...interface{}) Logger { | ||||
| } | ||||
|  | ||||
| func (s *slogLogger) Init(opts ...options.Option) error { | ||||
| 	if len(s.opts.ContextAttrFuncs) == 0 { | ||||
| 		s.opts.ContextAttrFuncs = DefaultContextAttrFuncs | ||||
| 	} | ||||
| 	for _, o := range opts { | ||||
| 		if err := o(&s.opts); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if slog, ok := s.opts.Context.Value(loggerKey{}).(*slog.Logger); ok { | ||||
| 		s.slog = slog | ||||
| 		return nil | ||||
| @@ -149,7 +151,7 @@ func (s *slogLogger) Log(ctx context.Context, lvl Level, msg string, attrs ...in | ||||
| 	runtime.Callers(s.opts.CallerSkipCount, pcs[:]) // skip [Callers, Infof] | ||||
| 	r := slog.NewRecord(time.Now(), loggerToSlogLevel(lvl), msg, pcs[0]) | ||||
| 	for _, fn := range s.opts.ContextAttrFuncs { | ||||
| 		attrs = append(attrs, fn(ctx)) | ||||
| 		attrs = append(attrs, fn(ctx)...) | ||||
| 	} | ||||
| 	r.Add(attrs...) | ||||
| 	_ = s.slog.Handler().Handle(ctx, r) | ||||
| @@ -163,7 +165,7 @@ func (s *slogLogger) Info(ctx context.Context, msg string, attrs ...interface{}) | ||||
| 	runtime.Callers(s.opts.CallerSkipCount, pcs[:]) // skip [Callers, Infof] | ||||
| 	r := slog.NewRecord(time.Now(), slog.LevelInfo, msg, pcs[0]) | ||||
| 	for _, fn := range s.opts.ContextAttrFuncs { | ||||
| 		attrs = append(attrs, fn(ctx)) | ||||
| 		attrs = append(attrs, fn(ctx)...) | ||||
| 	} | ||||
| 	r.Add(attrs...) | ||||
| 	_ = s.slog.Handler().Handle(ctx, r) | ||||
| @@ -177,7 +179,7 @@ func (s *slogLogger) Debug(ctx context.Context, msg string, attrs ...interface{} | ||||
| 	runtime.Callers(s.opts.CallerSkipCount, pcs[:]) // skip [Callers, Infof] | ||||
| 	r := slog.NewRecord(time.Now(), slog.LevelDebug, msg, pcs[0]) | ||||
| 	for _, fn := range s.opts.ContextAttrFuncs { | ||||
| 		attrs = append(attrs, fn(ctx)) | ||||
| 		attrs = append(attrs, fn(ctx)...) | ||||
| 	} | ||||
| 	r.Add(attrs...) | ||||
| 	_ = s.slog.Handler().Handle(ctx, r) | ||||
| @@ -191,7 +193,7 @@ func (s *slogLogger) Trace(ctx context.Context, msg string, attrs ...interface{} | ||||
| 	runtime.Callers(s.opts.CallerSkipCount, pcs[:]) // skip [Callers, Infof] | ||||
| 	r := slog.NewRecord(time.Now(), slog.LevelDebug-1, msg, pcs[0]) | ||||
| 	for _, fn := range s.opts.ContextAttrFuncs { | ||||
| 		attrs = append(attrs, fn(ctx)) | ||||
| 		attrs = append(attrs, fn(ctx)...) | ||||
| 	} | ||||
| 	r.Add(attrs...) | ||||
| 	_ = s.slog.Handler().Handle(ctx, r) | ||||
| @@ -205,7 +207,7 @@ func (s *slogLogger) Error(ctx context.Context, msg string, attrs ...interface{} | ||||
| 	runtime.Callers(s.opts.CallerSkipCount, pcs[:]) // skip [Callers, Infof] | ||||
| 	r := slog.NewRecord(time.Now(), slog.LevelError, msg, pcs[0]) | ||||
| 	for _, fn := range s.opts.ContextAttrFuncs { | ||||
| 		attrs = append(attrs, fn(ctx)) | ||||
| 		attrs = append(attrs, fn(ctx)...) | ||||
| 	} | ||||
| 	r.Add(attrs...) | ||||
| 	_ = s.slog.Handler().Handle(ctx, r) | ||||
| @@ -219,7 +221,7 @@ func (s *slogLogger) Fatal(ctx context.Context, msg string, attrs ...interface{} | ||||
| 	runtime.Callers(s.opts.CallerSkipCount, pcs[:]) // skip [Callers, Infof] | ||||
| 	r := slog.NewRecord(time.Now(), slog.LevelError+1, msg, pcs[0]) | ||||
| 	for _, fn := range s.opts.ContextAttrFuncs { | ||||
| 		attrs = append(attrs, fn(ctx)) | ||||
| 		attrs = append(attrs, fn(ctx)...) | ||||
| 	} | ||||
| 	r.Add(attrs...) | ||||
| 	_ = s.slog.Handler().Handle(ctx, r) | ||||
| @@ -234,7 +236,7 @@ func (s *slogLogger) Warn(ctx context.Context, msg string, attrs ...interface{}) | ||||
| 	runtime.Callers(s.opts.CallerSkipCount, pcs[:]) // skip [Callers, Infof] | ||||
| 	r := slog.NewRecord(time.Now(), slog.LevelWarn, msg, pcs[0]) | ||||
| 	for _, fn := range s.opts.ContextAttrFuncs { | ||||
| 		attrs = append(attrs, fn(ctx)) | ||||
| 		attrs = append(attrs, fn(ctx)...) | ||||
| 	} | ||||
| 	r.Add(attrs...) | ||||
| 	_ = s.slog.Handler().Handle(ctx, r) | ||||
|   | ||||
| @@ -27,8 +27,8 @@ func (t *noopTracer) Start(ctx context.Context, name string, opts ...options.Opt | ||||
| 		labels: options.Labels, | ||||
| 		kind:   options.Kind, | ||||
| 	} | ||||
| 	span.spanID, _ = id.New() | ||||
| 	span.traceID, _ = id.New() | ||||
| 	span.spanID.s, _ = id.New() | ||||
| 	span.traceID.s, _ = id.New() | ||||
| 	if span.ctx == nil { | ||||
| 		span.ctx = context.Background() | ||||
| 	} | ||||
| @@ -56,18 +56,26 @@ type noopEvent struct { | ||||
| 	labels []interface{} | ||||
| } | ||||
|  | ||||
| type noopStringer struct { | ||||
| 	s string | ||||
| } | ||||
|  | ||||
| func (s noopStringer) String() string { | ||||
| 	return s.s | ||||
| } | ||||
|  | ||||
| type noopSpan struct { | ||||
| 	ctx       context.Context | ||||
| 	tracer    Tracer | ||||
| 	name      string | ||||
| 	statusMsg string | ||||
| 	traceID   noopStringer | ||||
| 	spanID    noopStringer | ||||
| 	events    []*noopEvent | ||||
| 	labels    []interface{} | ||||
| 	logs      []interface{} | ||||
| 	kind      SpanKind | ||||
| 	status    SpanStatus | ||||
| 	traceID   string | ||||
| 	spanID    string | ||||
| } | ||||
|  | ||||
| func (s *noopSpan) Finish(opts ...options.Option) { | ||||
| @@ -103,11 +111,11 @@ func (s *noopSpan) Kind() SpanKind { | ||||
| } | ||||
|  | ||||
| func (s *noopSpan) TraceID() string { | ||||
| 	return s.traceID | ||||
| 	return s.traceID.String() | ||||
| } | ||||
|  | ||||
| func (s *noopSpan) SpanID() string { | ||||
| 	return s.spanID | ||||
| 	return s.spanID.String() | ||||
| } | ||||
|  | ||||
| func (s *noopSpan) Status() (SpanStatus, string) { | ||||
|   | ||||
							
								
								
									
										24
									
								
								tracer/tracer_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								tracer/tracer_test.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,24 @@ | ||||
| package tracer | ||||
|  | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"context" | ||||
| 	"strings" | ||||
| 	"testing" | ||||
|  | ||||
| 	"go.unistack.org/micro/v4/logger" | ||||
| ) | ||||
|  | ||||
| func TestLoggerWithTracer(t *testing.T) { | ||||
| 	ctx := context.TODO() | ||||
| 	buf := bytes.NewBuffer(nil) | ||||
| 	if err := logger.Init(logger.WithOutput(buf)); err != nil { | ||||
| 		t.Fatal(err) | ||||
| 	} | ||||
| 	var span Span | ||||
| 	ctx, span = DefaultTracer.Start(ctx, "test") | ||||
| 	logger.Info(ctx, "msg") | ||||
| 	if !strings.Contains(buf.String(), span.TraceID()) { | ||||
| 		t.Fatalf("log does not contains tracer id") | ||||
| 	} | ||||
| } | ||||
		Reference in New Issue
	
	Block a user