tracer and logger improvements
Some checks failed
/ autoupdate (push) Failing after 1m2s

Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
This commit is contained in:
Василий Толстов 2024-03-05 01:27:22 +03:00
parent c766477aaa
commit 1cbab38d24
6 changed files with 226 additions and 22 deletions

View File

@ -33,6 +33,8 @@ type Options struct {
LevelKey string
// MessageKey is the key used for the message of the log call
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 string
// StacktraceKey is the key used for the stacktrace
@ -52,10 +54,10 @@ func NewOptions(opts ...options.Option) Options {
ContextAttrFuncs: DefaultContextAttrFuncs,
}
WithMicroKeys()(&options)
_ = WithMicroKeys()(&options)
for _, o := range opts {
o(&options)
_ = o(&options)
}
return options
}
@ -123,6 +125,9 @@ func WithZapKeys() options.Option {
if err = options.Set(src, "stacktrace", ".StacktraceKey"); err != nil {
return err
}
if err = options.Set(src, "error", ".ErrorKey"); err != nil {
return err
}
return nil
}
}
@ -145,6 +150,9 @@ func WithZerologKeys() options.Option {
if err = options.Set(src, "stacktrace", ".StacktraceKey"); err != nil {
return err
}
if err = options.Set(src, "error", ".ErrorKey"); err != nil {
return err
}
return nil
}
}
@ -167,6 +175,9 @@ func WithSlogKeys() options.Option {
if err = options.Set(src, "stacktrace", ".StacktraceKey"); err != nil {
return err
}
if err = options.Set(src, "error", ".ErrorKey"); err != nil {
return err
}
return nil
}
}
@ -189,6 +200,9 @@ func WithMicroKeys() options.Option {
if err = options.Set(src, "stacktrace", ".StacktraceKey"); err != nil {
return err
}
if err = options.Set(src, "error", ".ErrorKey"); err != nil {
return err
}
return nil
}
}

View File

@ -174,6 +174,12 @@ func (s *slogLogger) Log(ctx context.Context, lvl logger.Level, msg string, attr
for _, fn := range s.opts.ContextAttrFuncs {
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 {
stackInfo := make([]byte, 1024*1024)
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.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)
}
@ -239,6 +254,12 @@ func (s *slogLogger) Error(ctx context.Context, msg string, attrs ...interface{}
for _, fn := range s.opts.ContextAttrFuncs {
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 {
stackInfo := make([]byte, 1024*1024)
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.Attrs(func(a slog.Attr) bool {
if a.Key == "error" {
if a.Key == s.opts.ErrorKey {
if span, ok := tracer.SpanFromContext(ctx); ok {
span.SetStatus(tracer.SpanStatusError, a.Value.String())
return false

View File

@ -3,6 +3,7 @@ package slog
import (
"bytes"
"context"
"fmt"
"log"
"testing"
@ -16,7 +17,7 @@ func TestError(t *testing.T) {
if err := l.Init(); err != nil {
t.Fatal(err)
}
l.Error(ctx, "message")
l.Error(ctx, "msg", fmt.Errorf("message"))
if !bytes.Contains(buf.Bytes(), []byte(`"stacktrace":"`)) {
t.Fatalf("logger stacktrace not works, buf contains: %s", buf.Bytes())
}

143
tracer/memory/memory.go Normal file
View 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...),
}
}

View 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)
}
}

View File

@ -24,7 +24,6 @@ func (t *noopTracer) Start(ctx context.Context, name string, opts ...options.Opt
name: name,
ctx: ctx,
tracer: t,
labels: options.Labels,
kind: options.Kind,
}
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
}
func (t *noopTracer) Flush(ctx context.Context) error {
func (t *noopTracer) Flush(_ context.Context) error {
return nil
}
@ -51,11 +50,6 @@ func (t *noopTracer) Name() string {
return t.opts.Name
}
type noopEvent struct {
name string
labels []interface{}
}
type noopStringer struct {
s string
}
@ -71,14 +65,11 @@ type noopSpan struct {
statusMsg string
traceID noopStringer
spanID noopStringer
events []*noopEvent
labels []interface{}
logs []interface{}
kind SpanKind
status SpanStatus
}
func (s *noopSpan) Finish(opts ...options.Option) {
func (s *noopSpan) Finish(_ ...options.Option) {
}
func (s *noopSpan) Context() context.Context {
@ -89,21 +80,17 @@ func (s *noopSpan) Tracer() Tracer {
return s.tracer
}
func (s *noopSpan) AddEvent(name string, opts ...options.Option) {
options := NewEventOptions(opts...)
s.events = append(s.events, &noopEvent{name: name, labels: options.Labels})
func (s *noopSpan) AddEvent(_ string, _ ...options.Option) {
}
func (s *noopSpan) SetName(name string) {
s.name = name
}
func (s *noopSpan) AddLogs(kv ...interface{}) {
s.logs = append(s.logs, kv...)
func (s *noopSpan) AddLogs(_ ...interface{}) {
}
func (s *noopSpan) AddLabels(kv ...interface{}) {
s.labels = append(s.labels, kv...)
func (s *noopSpan) AddLabels(_ ...interface{}) {
}
func (s *noopSpan) Kind() SpanKind {