From d44110c2a9ef70bee84624ce7241ec1e6cc8b407 Mon Sep 17 00:00:00 2001 From: Vasiliy Tolstov Date: Thu, 24 Apr 2025 12:22:56 +0300 Subject: [PATCH] update from v3 Signed-off-by: Vasiliy Tolstov --- go.mod | 13 +++- go.sum | 27 +++++-- opentracing.go | 185 +++++++++++++++++++++++++++++--------------- opentracing_test.go | 88 ++++++++++++++++++++- options.go | 6 +- 5 files changed, 242 insertions(+), 77 deletions(-) diff --git a/go.mod b/go.mod index 1cdfe04..b387e01 100644 --- a/go.mod +++ b/go.mod @@ -1,10 +1,17 @@ module go.unistack.org/micro-tracer-opentracing/v4 -go 1.20 +go 1.22.0 + +toolchain go1.23.4 require ( github.com/opentracing/opentracing-go v1.2.0 - go.unistack.org/micro/v4 v4.0.13 + go.opentelemetry.io/otel v1.35.0 + go.unistack.org/micro/v4 v4.1.6 ) -require github.com/stretchr/testify v1.8.3 // indirect +require ( + github.com/ash3in/uuidv8 v1.2.0 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/matoous/go-nanoid v1.5.1 // indirect +) diff --git a/go.sum b/go.sum index 75338ca..d8a90d9 100644 --- a/go.sum +++ b/go.sum @@ -1,13 +1,28 @@ +github.com/DATA-DOG/go-sqlmock v1.5.2 h1:OcvFkGmslmlZibjAjaHm3L//6LiuBgolP7OputlJIzU= +github.com/DATA-DOG/go-sqlmock v1.5.2/go.mod h1:88MAG/4G7SMwSE3CeA0ZKzrT5CiOU3OJ+JlNzwDqpNU= +github.com/ash3in/uuidv8 v1.2.0 h1:2oogGdtCPwaVtyvPPGin4TfZLtOGE5F+W++E880G6SI= +github.com/ash3in/uuidv8 v1.2.0/go.mod h1:BnU0wJBxnzdEKmVg4xckBkD+VZuecTFTUP3M0dWgyY4= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/matoous/go-nanoid v1.5.1 h1:aCjdvTyO9LLnTIi0fgdXhOPPvOHjpXN6Ik9DaNjIct4= +github.com/matoous/go-nanoid v1.5.1/go.mod h1:zyD2a71IubI24efhpvkJz+ZwfwagzgSO6UNiFsZKN7U= github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs= github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY= -github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -go.unistack.org/micro/v4 v4.0.13 h1:wyprk+KNAv/zR6bri6p6ucmXcJ9WyZ/vzru4zjPd8dA= -go.unistack.org/micro/v4 v4.0.13/go.mod h1:ZDgU9931vm2l7X6RN/6UuwRIVp24GRdmQ7dKmegArk4= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ= +go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y= +go.unistack.org/micro/v4 v4.1.6 h1:sYLpe1Vd8/lDwddtV0BLTvJ+i+fllXAS4fZngT1wKZ4= +go.unistack.org/micro/v4 v4.1.6/go.mod h1:lr3oYED8Ay1vjK68QqRw30QOtdk/ffpZqMFDasOUhKw= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/opentracing.go b/opentracing.go index 3ee301e..bca5c9f 100644 --- a/opentracing.go +++ b/opentracing.go @@ -3,14 +3,13 @@ package opentracing import ( "context" "errors" - "fmt" + "strings" ot "github.com/opentracing/opentracing-go" otlog "github.com/opentracing/opentracing-go/log" + "go.opentelemetry.io/otel/attribute" "go.unistack.org/micro/v4/metadata" - "go.unistack.org/micro/v4/options" "go.unistack.org/micro/v4/tracer" - rutil "go.unistack.org/micro/v4/util/reflect" ) var _ tracer.Tracer = &otTracer{} @@ -28,11 +27,9 @@ func (t *otTracer) Flush(ctx context.Context) error { return nil } -func (t *otTracer) Init(opts ...options.Option) error { +func (t *otTracer) Init(opts ...tracer.Option) error { for _, o := range opts { - if err := o(&t.opts); err != nil { - return err - } + o(&t.opts) } if tr, ok := t.opts.Context.Value(tracerKey{}).(ot.Tracer); ok { @@ -44,13 +41,22 @@ func (t *otTracer) Init(opts ...options.Option) error { return nil } -type spanContext interface { +type otSpanContext interface { TraceID() idStringer SpanID() idStringer } -func (t *otTracer) Start(ctx context.Context, name string, opts ...options.Option) (context.Context, tracer.Span) { +func (t *otTracer) Start(ctx context.Context, name string, opts ...tracer.SpanOption) (context.Context, tracer.Span) { options := tracer.NewSpanOptions(opts...) + + if len(options.Labels)%2 != 0 { + options.Labels = options.Labels[:len(options.Labels)-1] + } + + for _, fn := range t.opts.ContextAttrFuncs { + options.Labels = append(options.Labels, fn(ctx)...) + } + var span ot.Span switch options.Kind { case tracer.SpanKindUnspecified: @@ -66,18 +72,41 @@ func (t *otTracer) Start(ctx context.Context, name string, opts ...options.Optio sp := &otSpan{span: span, opts: options} spctx := span.Context() - if v, ok := spctx.(spanContext); ok { - sp.traceID = v.TraceID().String() - sp.spanID = v.SpanID().String() + + var traceID, spanID, parentID string + + if v, ok := spctx.(otSpanContext); ok { + traceID = v.TraceID().String() + spanID = v.SpanID().String() } else { - if val, err := rutil.StructFieldByName(spctx, "TraceID"); err == nil { - sp.traceID = fmt.Sprintf("%v", val) - } - if val, err := rutil.StructFieldByName(spctx, "SpanID"); err == nil { - sp.spanID = fmt.Sprintf("%v", val) + carrier := make(map[string]string, 1) + _ = span.Tracer().Inject(spctx, ot.TextMap, ot.TextMapCarrier(carrier)) + for k, v := range carrier { + switch k { + case "mockpfx-ids-sampled": + continue + case "mockpfx-ids-spanid": + spanID = v + case "mockpfx-ids-traceid": + traceID = v + default: // reasonable default + p := strings.Split(v, ":") + traceID = p[0] + spanID = p[1] + parentID = p[2] + case "uber-trace-id": // jaeger trace span + p := strings.Split(v, ":") + traceID = p[0] + spanID = p[1] + parentID = p[2] + } } } + sp.traceID = traceID + sp.spanID = spanID + sp.parentID = parentID + return tracer.NewSpanContext(ctx, sp), sp } @@ -93,9 +122,12 @@ type otSpan struct { span ot.Span spanID string traceID string + parentID string opts tracer.SpanOptions status tracer.SpanStatus statusMsg string + labels []interface{} + finished bool } func (os *otSpan) TraceID() string { @@ -106,6 +138,14 @@ func (os *otSpan) SpanID() string { return os.spanID } +func (os *otSpan) ParentID() string { + return os.parentID +} + +func (os *otSpan) IsRecording() bool { + return true +} + func (os *otSpan) SetStatus(st tracer.SpanStatus, msg string) { os.status = st os.statusMsg = msg @@ -119,52 +159,63 @@ func (os *otSpan) Tracer() tracer.Tracer { return &otTracer{tracer: os.span.Tracer()} } +func (os *otSpan) Finish(opts ...tracer.SpanOption) { + if os.finished { + return + } + + options := os.opts + + options.Status = os.status + options.StatusMsg = os.statusMsg + options.Labels = append(options.Labels, os.labels...) + + for _, o := range opts { + o(&options) + } + + if !options.Record { + return + } + + if len(options.Labels)%2 != 0 { + options.Labels = options.Labels[:len(options.Labels)-1] + } + + // options.Labels = sort.Uniq(options.Labels) + + l := len(options.Labels) + for idx := 0; idx < l; idx++ { + switch lt := options.Labels[idx].(type) { + case attribute.KeyValue: + os.span.SetTag(string(lt.Key), lt.Value.AsInterface()) + case string: + if l > idx+1 { + os.span.SetTag(lt, options.Labels[idx+1]) + idx++ + } + } + } + + if options.Status == tracer.SpanStatusError { + os.span.SetTag("error", true) + os.span.LogKV("error", options.StatusMsg) + } + + os.span.SetTag("span.kind", options.Kind) + os.span.Finish() + + os.finished = true +} + +func (os *otSpan) AddEvent(name string, opts ...tracer.EventOption) { + os.span.LogFields(otlog.Event(name)) +} + func (os *otSpan) AddLogs(kv ...interface{}) { os.span.LogKV(kv...) } -func (os *otSpan) Finish(opts ...options.Option) { - if len(os.opts.Labels)%2 != 0 { - os.opts.Labels = os.opts.Labels[:len(os.opts.Labels)-1] - } - os.opts.Labels = tracer.UniqLabels(os.opts.Labels) - for idx := 0; idx < len(os.opts.Labels); idx += 2 { - k, ok := os.opts.Labels[idx].(string) - if !ok { - continue - } - v, ok := os.opts.Labels[idx+1].(string) - if !ok { - v = fmt.Sprintf("%v", os.opts.Labels[idx+1]) - } - switch k { - case "err": - os.status = tracer.SpanStatusError - os.statusMsg = v - case "error": - continue - case "X-Request-Id", "x-request-id": - os.span.SetTag("x-request-id", v) - case "rpc.call", "rpc.call_type", "rpc.flavor", "rpc.service", "rpc.method", - "sdk.database", "db.statement", "db.args", "db.query", "db.method", - "messaging.destination.name", "messaging.source.name", "messaging.operation": - os.span.SetTag(k, v) - default: - os.span.LogKV(k, v) - } - } - if os.status == tracer.SpanStatusError { - os.span.SetTag("error", true) - os.span.LogKV("error", os.statusMsg) - } - os.span.SetTag("span.kind", os.opts.Kind) - os.span.Finish() -} - -func (os *otSpan) AddEvent(name string, opts ...options.Option) { - os.span.LogFields(otlog.Event(name)) -} - func (os *otSpan) Context() context.Context { return ot.ContextWithSpan(context.Background(), os.span) } @@ -182,10 +233,10 @@ func (os *otSpan) Kind() tracer.SpanKind { } func (os *otSpan) AddLabels(labels ...interface{}) { - os.opts.Labels = append(os.opts.Labels, labels...) + os.labels = append(os.labels, labels...) } -func NewTracer(opts ...options.Option) *otTracer { +func NewTracer(opts ...tracer.Option) *otTracer { options := tracer.NewOptions(opts...) return &otTracer{opts: options} } @@ -271,11 +322,15 @@ func (t *otTracer) startSpanFromOutgoingContext(ctx context.Context, name string } nmd := metadata.Copy(md) + smd := metadata.New(1) sp := t.tracer.StartSpan(name, opts...) - if err := sp.Tracer().Inject(sp.Context(), ot.TextMap, ot.TextMapCarrier(nmd)); err != nil { + if err := sp.Tracer().Inject(sp.Context(), ot.TextMap, ot.TextMapCarrier(smd)); err != nil { return nil, nil } + for k, v := range smd { + nmd.Set(k, v) + } ctx = metadata.NewOutgoingContext(ot.ContextWithSpan(ctx, sp), nmd) @@ -314,11 +369,15 @@ func (t *otTracer) startSpanFromIncomingContext(ctx context.Context, name string } nmd := metadata.Copy(md) + smd := metadata.New(1) sp := t.tracer.StartSpan(name, opts...) - if err := sp.Tracer().Inject(sp.Context(), ot.TextMap, ot.TextMapCarrier(nmd)); err != nil { + if err := sp.Tracer().Inject(sp.Context(), ot.TextMap, ot.TextMapCarrier(smd)); err != nil { return nil, nil } + for k, v := range smd { + nmd.Set(k, v) + } ctx = metadata.NewIncomingContext(ot.ContextWithSpan(ctx, sp), nmd) diff --git a/opentracing_test.go b/opentracing_test.go index 5f53c88..593e0bd 100644 --- a/opentracing_test.go +++ b/opentracing_test.go @@ -2,14 +2,17 @@ package opentracing import ( "context" + "fmt" "testing" "github.com/opentracing/opentracing-go/mocktracer" + // jconfig "github.com/uber/jaeger-client-go/config" + "go.unistack.org/micro/v4/logger/slog" "go.unistack.org/micro/v4/metadata" "go.unistack.org/micro/v4/tracer" ) -func TestTraceID(t *testing.T) { +func TestNoopTraceID(t *testing.T) { md := metadata.New(1) ctx, cancel := context.WithCancel(context.Background()) defer cancel() @@ -29,5 +32,86 @@ func TestTraceID(t *testing.T) { if v := sp.SpanID(); v != "44" { t.Fatalf("invalid span span id %#+v", v) } - _ = ctx + + l := slog.NewLogger() + if err := l.Init(); err != nil { + t.Fatal(err) + } + // l.Info(ctx, "msg") +} + +/* +func TestRealTraceID(t *testing.T) { + md := metadata.New(1) + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + ctx = metadata.NewIncomingContext(ctx, md) + + jcfg := &jconfig.Configuration{ + ServiceName: "test", + Sampler: &jconfig.SamplerConfig{ + Type: "const", + Param: 1, + }, + Reporter: &jconfig.ReporterConfig{ + LogSpans: true, + QueueSize: 100, + }, + } + + jtr, closer, err := jcfg.NewTracer() + if err != nil { + t.Fatal(err) + } + defer closer.Close() + + tr := NewTracer(Tracer(jtr)) + if err := tr.Init(); err != nil { + t.Fatal(err) + } + + var sp tracer.Span + + ctx, sp = tr.Start(ctx, "test") + if v := sp.TraceID(); v != "43" { + t.Fatalf("invalid span trace id %#+v", v) + } + if v := sp.SpanID(); v != "44" { + t.Fatalf("invalid span span id %#+v", v) + } + + l := slog.NewLogger() + if err := l.Init(); err != nil { + t.Fatal(err) + } + l.Info(ctx, "msg") +} +*/ + +func TestTraceTags(t *testing.T) { + md := metadata.New(1) + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + ctx = metadata.NewIncomingContext(ctx, md) + + mtr := mocktracer.New() + tr := NewTracer(Tracer(mtr)) + if err := tr.Init(); err != nil { + t.Fatal(err) + } + + var sp tracer.Span + + ctx, sp = tr.Start(ctx, "test", tracer.WithSpanLabels("key", "val", "odd")) + sp.Finish(tracer.WithSpanLabels("xkey", "xval")) + _ = ctx + msp := mtr.FinishedSpans()[0] + + if "val" != fmt.Sprintf("%v", msp.Tags()["key"]) { + t.Fatal("mock span invalid") + } + + if "xval" != fmt.Sprintf("%v", msp.Tags()["xkey"]) { + t.Fatalf("mock span invalid %#+v", msp) + } } diff --git a/options.go b/options.go index dd233a7..b8ffae8 100644 --- a/options.go +++ b/options.go @@ -2,11 +2,11 @@ package opentracing import ( "github.com/opentracing/opentracing-go" - "go.unistack.org/micro/v4/options" + "go.unistack.org/micro/v4/tracer" ) type tracerKey struct{} -func Tracer(ot opentracing.Tracer) options.Option { - return options.ContextOption(tracerKey{}, ot) +func Tracer(ot opentracing.Tracer) tracer.Option { + return tracer.SetOption(tracerKey{}, ot) }