Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
This commit is contained in:
		| @@ -33,6 +33,8 @@ type Options struct { | |||||||
| 	LevelKey string | 	LevelKey string | ||||||
| 	// MessageKey is the key used for the message of the log call | 	// MessageKey is the key used for the message of the log call | ||||||
| 	MessageKey string | 	MessageKey string | ||||||
|  | 	// ErrorKey is the key used for the error info | ||||||
|  | 	ErrorKey string | ||||||
| 	// SourceKey is the key used for the source file and line of the log call | 	// SourceKey is the key used for the source file and line of the log call | ||||||
| 	SourceKey string | 	SourceKey string | ||||||
| 	// StacktraceKey is the key used for the stacktrace | 	// StacktraceKey is the key used for the stacktrace | ||||||
| @@ -52,10 +54,10 @@ func NewOptions(opts ...options.Option) Options { | |||||||
| 		ContextAttrFuncs: DefaultContextAttrFuncs, | 		ContextAttrFuncs: DefaultContextAttrFuncs, | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	WithMicroKeys()(&options) | 	_ = WithMicroKeys()(&options) | ||||||
|  |  | ||||||
| 	for _, o := range opts { | 	for _, o := range opts { | ||||||
| 		o(&options) | 		_ = o(&options) | ||||||
| 	} | 	} | ||||||
| 	return options | 	return options | ||||||
| } | } | ||||||
| @@ -123,6 +125,9 @@ func WithZapKeys() options.Option { | |||||||
| 		if err = options.Set(src, "stacktrace", ".StacktraceKey"); err != nil { | 		if err = options.Set(src, "stacktrace", ".StacktraceKey"); err != nil { | ||||||
| 			return err | 			return err | ||||||
| 		} | 		} | ||||||
|  | 		if err = options.Set(src, "error", ".ErrorKey"); err != nil { | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
| 		return nil | 		return nil | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| @@ -145,6 +150,9 @@ func WithZerologKeys() options.Option { | |||||||
| 		if err = options.Set(src, "stacktrace", ".StacktraceKey"); err != nil { | 		if err = options.Set(src, "stacktrace", ".StacktraceKey"); err != nil { | ||||||
| 			return err | 			return err | ||||||
| 		} | 		} | ||||||
|  | 		if err = options.Set(src, "error", ".ErrorKey"); err != nil { | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
| 		return nil | 		return nil | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| @@ -167,6 +175,9 @@ func WithSlogKeys() options.Option { | |||||||
| 		if err = options.Set(src, "stacktrace", ".StacktraceKey"); err != nil { | 		if err = options.Set(src, "stacktrace", ".StacktraceKey"); err != nil { | ||||||
| 			return err | 			return err | ||||||
| 		} | 		} | ||||||
|  | 		if err = options.Set(src, "error", ".ErrorKey"); err != nil { | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
| 		return nil | 		return nil | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| @@ -189,6 +200,9 @@ func WithMicroKeys() options.Option { | |||||||
| 		if err = options.Set(src, "stacktrace", ".StacktraceKey"); err != nil { | 		if err = options.Set(src, "stacktrace", ".StacktraceKey"); err != nil { | ||||||
| 			return err | 			return err | ||||||
| 		} | 		} | ||||||
|  | 		if err = options.Set(src, "error", ".ErrorKey"); err != nil { | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
| 		return nil | 		return nil | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|   | |||||||
| @@ -174,6 +174,12 @@ func (s *slogLogger) Log(ctx context.Context, lvl logger.Level, msg string, attr | |||||||
| 	for _, fn := range s.opts.ContextAttrFuncs { | 	for _, fn := range s.opts.ContextAttrFuncs { | ||||||
| 		attrs = append(attrs, fn(ctx)...) | 		attrs = append(attrs, fn(ctx)...) | ||||||
| 	} | 	} | ||||||
|  | 	for _, a := range attrs { | ||||||
|  | 		if ve, ok := a.(error); ok && ve != nil { | ||||||
|  | 			attrs = append(attrs, slog.String(s.opts.ErrorKey, ve.Error())) | ||||||
|  | 			break | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
| 	if s.opts.Stacktrace && lvl == logger.ErrorLevel { | 	if s.opts.Stacktrace && lvl == logger.ErrorLevel { | ||||||
| 		stackInfo := make([]byte, 1024*1024) | 		stackInfo := make([]byte, 1024*1024) | ||||||
| 		if stackSize := runtime.Stack(stackInfo, false); stackSize > 0 { | 		if stackSize := runtime.Stack(stackInfo, false); stackSize > 0 { | ||||||
| @@ -184,6 +190,15 @@ func (s *slogLogger) Log(ctx context.Context, lvl logger.Level, msg string, attr | |||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 	r.Add(attrs...) | 	r.Add(attrs...) | ||||||
|  | 	r.Attrs(func(a slog.Attr) bool { | ||||||
|  | 		if a.Key == s.opts.ErrorKey { | ||||||
|  | 			if span, ok := tracer.SpanFromContext(ctx); ok { | ||||||
|  | 				span.SetStatus(tracer.SpanStatusError, a.Value.String()) | ||||||
|  | 				return false | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		return true | ||||||
|  | 	}) | ||||||
| 	_ = s.slog.Handler().Handle(ctx, r) | 	_ = s.slog.Handler().Handle(ctx, r) | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -239,6 +254,12 @@ func (s *slogLogger) Error(ctx context.Context, msg string, attrs ...interface{} | |||||||
| 	for _, fn := range s.opts.ContextAttrFuncs { | 	for _, fn := range s.opts.ContextAttrFuncs { | ||||||
| 		attrs = append(attrs, fn(ctx)...) | 		attrs = append(attrs, fn(ctx)...) | ||||||
| 	} | 	} | ||||||
|  | 	for _, a := range attrs { | ||||||
|  | 		if ve, ok := a.(error); ok && ve != nil { | ||||||
|  | 			attrs = append(attrs, slog.String(s.opts.ErrorKey, ve.Error())) | ||||||
|  | 			break | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
| 	if s.opts.Stacktrace { | 	if s.opts.Stacktrace { | ||||||
| 		stackInfo := make([]byte, 1024*1024) | 		stackInfo := make([]byte, 1024*1024) | ||||||
| 		if stackSize := runtime.Stack(stackInfo, false); stackSize > 0 { | 		if stackSize := runtime.Stack(stackInfo, false); stackSize > 0 { | ||||||
| @@ -250,7 +271,7 @@ func (s *slogLogger) Error(ctx context.Context, msg string, attrs ...interface{} | |||||||
| 	} | 	} | ||||||
| 	r.Add(attrs...) | 	r.Add(attrs...) | ||||||
| 	r.Attrs(func(a slog.Attr) bool { | 	r.Attrs(func(a slog.Attr) bool { | ||||||
| 		if a.Key == "error" { | 		if a.Key == s.opts.ErrorKey { | ||||||
| 			if span, ok := tracer.SpanFromContext(ctx); ok { | 			if span, ok := tracer.SpanFromContext(ctx); ok { | ||||||
| 				span.SetStatus(tracer.SpanStatusError, a.Value.String()) | 				span.SetStatus(tracer.SpanStatusError, a.Value.String()) | ||||||
| 				return false | 				return false | ||||||
|   | |||||||
| @@ -3,6 +3,7 @@ package slog | |||||||
| import ( | import ( | ||||||
| 	"bytes" | 	"bytes" | ||||||
| 	"context" | 	"context" | ||||||
|  | 	"fmt" | ||||||
| 	"log" | 	"log" | ||||||
| 	"testing" | 	"testing" | ||||||
|  |  | ||||||
| @@ -16,7 +17,7 @@ func TestError(t *testing.T) { | |||||||
| 	if err := l.Init(); err != nil { | 	if err := l.Init(); err != nil { | ||||||
| 		t.Fatal(err) | 		t.Fatal(err) | ||||||
| 	} | 	} | ||||||
| 	l.Error(ctx, "message") | 	l.Error(ctx, "msg", fmt.Errorf("message")) | ||||||
| 	if !bytes.Contains(buf.Bytes(), []byte(`"stacktrace":"`)) { | 	if !bytes.Contains(buf.Bytes(), []byte(`"stacktrace":"`)) { | ||||||
| 		t.Fatalf("logger stacktrace not works, buf contains: %s", buf.Bytes()) | 		t.Fatalf("logger stacktrace not works, buf contains: %s", buf.Bytes()) | ||||||
| 	} | 	} | ||||||
|   | |||||||
							
								
								
									
										143
									
								
								tracer/memory/memory.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										143
									
								
								tracer/memory/memory.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,143 @@ | |||||||
|  | package memory | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"context" | ||||||
|  | 	"time" | ||||||
|  |  | ||||||
|  | 	"go.unistack.org/micro/v4/options" | ||||||
|  | 	"go.unistack.org/micro/v4/tracer" | ||||||
|  | 	"go.unistack.org/micro/v4/util/id" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | var _ tracer.Tracer = (*Tracer)(nil) | ||||||
|  |  | ||||||
|  | type Tracer struct { | ||||||
|  | 	opts  tracer.Options | ||||||
|  | 	spans []tracer.Span | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (t *Tracer) Spans() []tracer.Span { | ||||||
|  | 	return t.spans | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (t *Tracer) Start(ctx context.Context, name string, opts ...options.Option) (context.Context, tracer.Span) { | ||||||
|  | 	options := tracer.NewSpanOptions(opts...) | ||||||
|  | 	span := &Span{ | ||||||
|  | 		name:      name, | ||||||
|  | 		ctx:       ctx, | ||||||
|  | 		tracer:    t, | ||||||
|  | 		kind:      options.Kind, | ||||||
|  | 		startTime: time.Now(), | ||||||
|  | 	} | ||||||
|  | 	span.spanID.s, _ = id.New() | ||||||
|  | 	span.traceID.s, _ = id.New() | ||||||
|  | 	if span.ctx == nil { | ||||||
|  | 		span.ctx = context.Background() | ||||||
|  | 	} | ||||||
|  | 	t.spans = append(t.spans, span) | ||||||
|  | 	return tracer.NewSpanContext(ctx, span), span | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (t *Tracer) Flush(_ context.Context) error { | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (t *Tracer) Init(opts ...options.Option) error { | ||||||
|  | 	var err error | ||||||
|  | 	for _, o := range opts { | ||||||
|  | 		if err = o(&t.opts); err != nil { | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (t *Tracer) Name() string { | ||||||
|  | 	return t.opts.Name | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type noopStringer struct { | ||||||
|  | 	s string | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (s noopStringer) String() string { | ||||||
|  | 	return s.s | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type Span struct { | ||||||
|  | 	ctx        context.Context | ||||||
|  | 	tracer     tracer.Tracer | ||||||
|  | 	name       string | ||||||
|  | 	statusMsg  string | ||||||
|  | 	startTime  time.Time | ||||||
|  | 	finishTime time.Time | ||||||
|  | 	traceID    noopStringer | ||||||
|  | 	spanID     noopStringer | ||||||
|  | 	events     []*Event | ||||||
|  | 	labels     []interface{} | ||||||
|  | 	logs       []interface{} | ||||||
|  | 	kind       tracer.SpanKind | ||||||
|  | 	status     tracer.SpanStatus | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (s *Span) Finish(opts ...options.Option) { | ||||||
|  | 	s.finishTime = time.Now() | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (s *Span) Context() context.Context { | ||||||
|  | 	return s.ctx | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (s *Span) Tracer() tracer.Tracer { | ||||||
|  | 	return s.tracer | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type Event struct { | ||||||
|  | 	name   string | ||||||
|  | 	labels []interface{} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (s *Span) AddEvent(name string, opts ...options.Option) { | ||||||
|  | 	options := tracer.NewEventOptions(opts...) | ||||||
|  | 	s.events = append(s.events, &Event{name: name, labels: options.Labels}) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (s *Span) SetName(name string) { | ||||||
|  | 	s.name = name | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (s *Span) AddLogs(kv ...interface{}) { | ||||||
|  | 	s.logs = append(s.logs, kv...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (s *Span) AddLabels(kv ...interface{}) { | ||||||
|  | 	s.labels = append(s.labels, kv...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (s *Span) Kind() tracer.SpanKind { | ||||||
|  | 	return s.kind | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (s *Span) TraceID() string { | ||||||
|  | 	return s.traceID.String() | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (s *Span) SpanID() string { | ||||||
|  | 	return s.spanID.String() | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (s *Span) Status() (tracer.SpanStatus, string) { | ||||||
|  | 	return s.status, s.statusMsg | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (s *Span) SetStatus(st tracer.SpanStatus, msg string) { | ||||||
|  | 	s.status = st | ||||||
|  | 	s.statusMsg = msg | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // NewTracer returns new memory tracer | ||||||
|  | func NewTracer(opts ...options.Option) *Tracer { | ||||||
|  | 	return &Tracer{ | ||||||
|  | 		opts: tracer.NewOptions(opts...), | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										38
									
								
								tracer/memory/memory_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								tracer/memory/memory_test.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,38 @@ | |||||||
|  | package memory | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"bytes" | ||||||
|  | 	"context" | ||||||
|  | 	"fmt" | ||||||
|  | 	"strings" | ||||||
|  | 	"testing" | ||||||
|  |  | ||||||
|  | 	"go.unistack.org/micro/v4/logger" | ||||||
|  | 	"go.unistack.org/micro/v4/logger/slog" | ||||||
|  | 	"go.unistack.org/micro/v4/tracer" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func TestLoggerWithTracer(t *testing.T) { | ||||||
|  | 	ctx := context.TODO() | ||||||
|  | 	buf := bytes.NewBuffer(nil) | ||||||
|  | 	logger.DefaultLogger = slog.NewLogger(logger.WithOutput(buf)) | ||||||
|  |  | ||||||
|  | 	if err := logger.Init(); err != nil { | ||||||
|  | 		t.Fatal(err) | ||||||
|  | 	} | ||||||
|  | 	var span tracer.Span | ||||||
|  | 	tr := NewTracer() | ||||||
|  | 	ctx, span = tr.Start(ctx, "test1") | ||||||
|  |  | ||||||
|  | 	logger.Error(ctx, "my test error", fmt.Errorf("error")) | ||||||
|  |  | ||||||
|  | 	if !strings.Contains(buf.String(), span.TraceID()) { | ||||||
|  | 		t.Fatalf("log does not contains tracer id: %s", buf.Bytes()) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	_, _ = tr.Start(ctx, "test2") | ||||||
|  |  | ||||||
|  | 	for _, s := range tr.Spans() { | ||||||
|  | 		t.Logf("span %#+v\n", s) | ||||||
|  | 	} | ||||||
|  | } | ||||||
| @@ -24,7 +24,6 @@ func (t *noopTracer) Start(ctx context.Context, name string, opts ...options.Opt | |||||||
| 		name:   name, | 		name:   name, | ||||||
| 		ctx:    ctx, | 		ctx:    ctx, | ||||||
| 		tracer: t, | 		tracer: t, | ||||||
| 		labels: options.Labels, |  | ||||||
| 		kind:   options.Kind, | 		kind:   options.Kind, | ||||||
| 	} | 	} | ||||||
| 	span.spanID.s, _ = id.New() | 	span.spanID.s, _ = id.New() | ||||||
| @@ -36,7 +35,7 @@ func (t *noopTracer) Start(ctx context.Context, name string, opts ...options.Opt | |||||||
| 	return NewSpanContext(ctx, span), span | 	return NewSpanContext(ctx, span), span | ||||||
| } | } | ||||||
|  |  | ||||||
| func (t *noopTracer) Flush(ctx context.Context) error { | func (t *noopTracer) Flush(_ context.Context) error { | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -51,11 +50,6 @@ func (t *noopTracer) Name() string { | |||||||
| 	return t.opts.Name | 	return t.opts.Name | ||||||
| } | } | ||||||
|  |  | ||||||
| type noopEvent struct { |  | ||||||
| 	name   string |  | ||||||
| 	labels []interface{} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| type noopStringer struct { | type noopStringer struct { | ||||||
| 	s string | 	s string | ||||||
| } | } | ||||||
| @@ -71,14 +65,11 @@ type noopSpan struct { | |||||||
| 	statusMsg string | 	statusMsg string | ||||||
| 	traceID   noopStringer | 	traceID   noopStringer | ||||||
| 	spanID    noopStringer | 	spanID    noopStringer | ||||||
| 	events    []*noopEvent |  | ||||||
| 	labels    []interface{} |  | ||||||
| 	logs      []interface{} |  | ||||||
| 	kind      SpanKind | 	kind      SpanKind | ||||||
| 	status    SpanStatus | 	status    SpanStatus | ||||||
| } | } | ||||||
|  |  | ||||||
| func (s *noopSpan) Finish(opts ...options.Option) { | func (s *noopSpan) Finish(_ ...options.Option) { | ||||||
| } | } | ||||||
|  |  | ||||||
| func (s *noopSpan) Context() context.Context { | func (s *noopSpan) Context() context.Context { | ||||||
| @@ -89,21 +80,17 @@ func (s *noopSpan) Tracer() Tracer { | |||||||
| 	return s.tracer | 	return s.tracer | ||||||
| } | } | ||||||
|  |  | ||||||
| func (s *noopSpan) AddEvent(name string, opts ...options.Option) { | func (s *noopSpan) AddEvent(_ string, _ ...options.Option) { | ||||||
| 	options := NewEventOptions(opts...) |  | ||||||
| 	s.events = append(s.events, &noopEvent{name: name, labels: options.Labels}) |  | ||||||
| } | } | ||||||
|  |  | ||||||
| func (s *noopSpan) SetName(name string) { | func (s *noopSpan) SetName(name string) { | ||||||
| 	s.name = name | 	s.name = name | ||||||
| } | } | ||||||
|  |  | ||||||
| func (s *noopSpan) AddLogs(kv ...interface{}) { | func (s *noopSpan) AddLogs(_ ...interface{}) { | ||||||
| 	s.logs = append(s.logs, kv...) |  | ||||||
| } | } | ||||||
|  |  | ||||||
| func (s *noopSpan) AddLabels(kv ...interface{}) { | func (s *noopSpan) AddLabels(_ ...interface{}) { | ||||||
| 	s.labels = append(s.labels, kv...) |  | ||||||
| } | } | ||||||
|  |  | ||||||
| func (s *noopSpan) Kind() SpanKind { | func (s *noopSpan) Kind() SpanKind { | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user