diff --git a/go.mod b/go.mod index 093d93b..1c1d2ea 100644 --- a/go.mod +++ b/go.mod @@ -3,9 +3,9 @@ module go.unistack.org/micro-tracer-instana/v3 go 1.20 require ( - github.com/instana/go-sensor v1.55.2 + github.com/instana/go-sensor v1.60.0 github.com/opentracing/opentracing-go v1.2.0 - go.unistack.org/micro/v3 v3.10.25 + go.unistack.org/micro/v3 v3.10.50 ) require ( diff --git a/go.sum b/go.sum index 70f4240..fc1aad0 100644 --- a/go.sum +++ b/go.sum @@ -1,8 +1,8 @@ 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.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/instana/go-sensor v1.55.2 h1:XdLN38mSFsHpaL+jIDkE/ZrW7pxgPeUC/bV9bSwVHyM= -github.com/instana/go-sensor v1.55.2/go.mod h1:Ks06EG9Da5O3hbdJiHIePG/vNmToovkaJjMlUBd70Yc= +github.com/instana/go-sensor v1.60.0 h1:lsAiwmK5AtNQ9gLHPLvZt0aLddZxM4R0oakkO7rXO2U= +github.com/instana/go-sensor v1.60.0/go.mod h1:Ks06EG9Da5O3hbdJiHIePG/vNmToovkaJjMlUBd70Yc= github.com/looplab/fsm v1.0.1 h1:OEW0ORrIx095N/6lgoGkFkotqH6s7vaFPsgjLAaF5QU= github.com/looplab/fsm v1.0.1/go.mod h1:PmD3fFvQEIsjMEfvZdrCDZ6y8VwKTwWNjlpEr6IKPO4= github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs= @@ -18,8 +18,8 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= 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/v3 v3.10.25 h1:A0epdZHOqjnXx103wwFhPKgmvVVbScvfbmn3HmHz1wE= -go.unistack.org/micro/v3 v3.10.25/go.mod h1:ALkeXpqChYDjx8KPi7tz9mmIyOnob6nlNswsg8BnZjQ= +go.unistack.org/micro/v3 v3.10.50 h1:U0aYtKHrpGHFDYdTRgsIYxIejU4HKDKbLHcLZngvDIs= +go.unistack.org/micro/v3 v3.10.50/go.mod h1:erMgt3Bl7vQQ0e9UpQyR5NlLiZ9pKeEJ9+1tfYFaqUg= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= diff --git a/instana.go b/instana.go index a40f837..b2c7b18 100644 --- a/instana.go +++ b/instana.go @@ -2,60 +2,106 @@ package instana import ( "context" + "fmt" + instana "github.com/instana/go-sensor" sensor "github.com/instana/go-sensor" "github.com/opentracing/opentracing-go" "github.com/opentracing/opentracing-go/log" "go.unistack.org/micro/v3/metadata" "go.unistack.org/micro/v3/tracer" + rutil "go.unistack.org/micro/v3/util/reflect" ) -var _ tracer.Tracer = &Tracer{} +var _ tracer.Tracer = (*Tracer)(nil) type Tracer struct { opts tracer.Options sensor sensor.Tracer } -func (ot *Tracer) Name() string { - return ot.opts.Name +func (t *Tracer) Name() string { + return t.opts.Name } -func (ot *Tracer) Flush(ctx context.Context) error { +func (t *Tracer) Flush(ctx context.Context) error { + sensor.ShutdownSensor() return nil } -func (ot *Tracer) Init(opts ...tracer.Option) error { +func (t *Tracer) Init(opts ...tracer.Option) error { for _, o := range opts { - o(&ot.opts) + o(&t.opts) } sensorOptions := sensor.DefaultOptions() - if v, ok := ot.opts.Context.Value(tracerOptionsKey{}).(*sensor.Options); ok && v != nil { + if v, ok := t.opts.Context.Value(tracerOptionsKey{}).(*sensor.Options); ok && v != nil { sensorOptions = v } - ot.sensor = sensor.NewSensorWithTracer(sensor.NewTracerWithOptions(sensorOptions)) + if sensorOptions.Service == "" { + sensorOptions.Service = "micro" + } + var recorder instana.SpanRecorder + recorder = &sensor.Recorder{} + if v, ok := t.opts.Context.Value(recorderKey{}).(sensor.SpanRecorder); ok && v != nil { + recorder = v + } + + t.sensor = sensor.NewSensorWithTracer(sensor.NewTracerWithEverything(sensorOptions, recorder)) + + sensor.StartMetrics(sensorOptions) return nil } -func (ot *Tracer) Start(ctx context.Context, name string, opts ...tracer.SpanOption) (context.Context, tracer.Span) { +type idStringer struct { + s string +} + +func (s idStringer) String() string { + return s.s +} + +type spanContext interface { + TraceID() idStringer + SpanID() idStringer +} + +func (t *Tracer) Start(ctx context.Context, name string, opts ...tracer.SpanOption) (context.Context, tracer.Span) { options := tracer.NewSpanOptions(opts...) var span opentracing.Span switch options.Kind { case tracer.SpanKindInternal, tracer.SpanKindUnspecified: - ctx, span = ot.startSpanFromContext(ctx, name) + ctx, span = t.startSpanFromContext(ctx, name) case tracer.SpanKindClient, tracer.SpanKindProducer: - ctx, span = ot.startSpanFromOutgoingContext(ctx, name) + ctx, span = t.startSpanFromOutgoingContext(ctx, name) case tracer.SpanKindServer, tracer.SpanKindConsumer: - ctx, span = ot.startSpanFromIncomingContext(ctx, name) + ctx, span = t.startSpanFromIncomingContext(ctx, name) } - return ctx, &otSpan{span: span, opts: options} + + sp := &otSpan{topts: t.opts, span: span, opts: options, sensor: t.sensor, status: tracer.SpanStatusOK} + + spctx := span.Context() + if v, ok := spctx.(spanContext); ok { + sp.traceID = v.TraceID().String() + sp.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) + } + } + + return ctx, sp } type otSpan struct { span opentracing.Span + spanID string + traceID string sensor sensor.Tracer topts tracer.Options opts tracer.SpanOptions @@ -63,6 +109,14 @@ type otSpan struct { statusMsg string } +func (os *otSpan) TraceID() string { + return os.traceID +} + +func (os *otSpan) SpanID() string { + return os.spanID +} + func (os *otSpan) SetStatus(st tracer.SpanStatus, msg string) { switch st { case tracer.SpanStatusError: @@ -80,10 +134,37 @@ func (os *otSpan) Tracer() tracer.Tracer { return &Tracer{sensor: os.sensor, opts: os.topts} } +func (os *otSpan) AddLogs(kv ...interface{}) { + os.span.LogKV(kv...) +} + func (os *otSpan) Finish(opts ...tracer.SpanOption) { - if len(os.opts.Labels) > 0 { - os.span.LogKV(os.opts.Labels...) + 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 { + switch os.opts.Labels[idx] { + case "err": + os.status = tracer.SpanStatusError + os.statusMsg = fmt.Sprintf("%v", os.opts.Labels[idx+1]) + case "error": + continue + case "X-Request-Id", "x-request-id": + os.span.SetTag("x-request-id", os.opts.Labels[idx+1]) + 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(fmt.Sprintf("%v", os.opts.Labels[idx]), os.opts.Labels[idx+1]) + default: + os.span.LogKV(os.opts.Labels[idx], os.opts.Labels[idx+1]) + } + } + 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() } @@ -120,14 +201,34 @@ func spanFromContext(ctx context.Context) (opentracing.Span, bool) { return sensor.SpanFromContext(ctx) } -func (ot *Tracer) startSpanFromContext(ctx context.Context, name string, opts ...opentracing.StartSpanOption) (context.Context, opentracing.Span) { +func (t *Tracer) startSpanFromAny(ctx context.Context, name string, opts ...opentracing.StartSpanOption) (context.Context, opentracing.Span) { + if tracerSpan, ok := tracer.SpanFromContext(ctx); ok && tracerSpan != nil { + return t.startSpanFromContext(ctx, name, opts...) + } + + if otSpan := opentracing.SpanFromContext(ctx); otSpan != nil { + return t.startSpanFromContext(ctx, name, opts...) + } + + if md, ok := metadata.FromIncomingContext(ctx); ok && md != nil { + return t.startSpanFromIncomingContext(ctx, name, opts...) + } + + if md, ok := metadata.FromOutgoingContext(ctx); ok && md != nil { + return t.startSpanFromOutgoingContext(ctx, name, opts...) + } + + return t.startSpanFromContext(ctx, name, opts...) +} + +func (t *Tracer) startSpanFromContext(ctx context.Context, name string, opts ...opentracing.StartSpanOption) (context.Context, opentracing.Span) { if parentSpan := opentracing.SpanFromContext(ctx); parentSpan != nil { opts = append(opts, opentracing.ChildOf(parentSpan.Context())) } md := metadata.New(1) - sp := ot.sensor.StartSpan(name, opts...) + sp := t.sensor.StartSpan(name, opts...) if err := sp.Tracer().Inject(sp.Context(), opentracing.TextMap, opentracing.TextMapCarrier(md)); err != nil { return nil, nil } @@ -137,12 +238,12 @@ func (ot *Tracer) startSpanFromContext(ctx context.Context, name string, opts .. return ctx, sp } -func (ot *Tracer) startSpanFromOutgoingContext(ctx context.Context, name string, opts ...opentracing.StartSpanOption) (context.Context, opentracing.Span) { +func (t *Tracer) startSpanFromOutgoingContext(ctx context.Context, name string, opts ...opentracing.StartSpanOption) (context.Context, opentracing.Span) { var parentCtx opentracing.SpanContext md, ok := metadata.FromOutgoingContext(ctx) if ok && md != nil { - if spanCtx, err := ot.sensor.Extract(opentracing.TextMap, opentracing.TextMapCarrier(md)); err == nil && ok { + if spanCtx, err := t.sensor.Extract(opentracing.TextMap, opentracing.TextMapCarrier(md)); err == nil && ok { parentCtx = spanCtx } } @@ -153,7 +254,7 @@ func (ot *Tracer) startSpanFromOutgoingContext(ctx context.Context, name string, nmd := metadata.Copy(md) - sp := ot.sensor.StartSpan(name, opts...) + sp := t.sensor.StartSpan(name, opts...) if err := sp.Tracer().Inject(sp.Context(), opentracing.TextMap, opentracing.TextMapCarrier(nmd)); err != nil { return nil, nil } @@ -163,12 +264,12 @@ func (ot *Tracer) startSpanFromOutgoingContext(ctx context.Context, name string, return ctx, sp } -func (ot *Tracer) startSpanFromIncomingContext(ctx context.Context, name string, opts ...opentracing.StartSpanOption) (context.Context, opentracing.Span) { +func (t *Tracer) startSpanFromIncomingContext(ctx context.Context, name string, opts ...opentracing.StartSpanOption) (context.Context, opentracing.Span) { var parentCtx opentracing.SpanContext md, ok := metadata.FromIncomingContext(ctx) if ok && md != nil { - if spanCtx, err := ot.sensor.Extract(opentracing.TextMap, opentracing.TextMapCarrier(md)); err == nil { + if spanCtx, err := t.sensor.Extract(opentracing.TextMap, opentracing.TextMapCarrier(md)); err == nil { parentCtx = spanCtx } } @@ -179,7 +280,8 @@ func (ot *Tracer) startSpanFromIncomingContext(ctx context.Context, name string, nmd := metadata.Copy(md) - sp := ot.sensor.StartSpan(name, opts...) + sp := t.sensor.StartSpan(name, opts...) + // fmt.Printf("StartSpan %#+v\n", sp) if err := sp.Tracer().Inject(sp.Context(), opentracing.TextMap, opentracing.TextMapCarrier(nmd)); err != nil { return nil, nil } diff --git a/instana_test.go b/instana_test.go new file mode 100644 index 0000000..b0fb1c5 --- /dev/null +++ b/instana_test.go @@ -0,0 +1,59 @@ +package instana + +import ( + "context" + "testing" + + sensor "github.com/instana/go-sensor" + "go.unistack.org/micro/v3/metadata" + "go.unistack.org/micro/v3/tracer" +) + +func TestTraceID(t *testing.T) { + md := metadata.New(1) + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + ctx = metadata.NewIncomingContext(ctx, md) + + tr := NewTracer() + if err := tr.Init(); err != nil { + t.Fatal(err) + } + + var sp tracer.Span + + ctx, sp = tr.Start(ctx, "test") + if v := sp.TraceID(); v == "" { + t.Fatalf("invalid span trace id %#+v", v) + } + if v := sp.SpanID(); v == "" { + t.Fatalf("invalid span span id %#+v", v) + } +} + +func TestSpanPropogation(t *testing.T) { + md := metadata.New(0) + md.Set( + "X-Instana-T", "0000000000002435", + "X-Instana-S", "0000000000003546", + "X-Instana-L", "1", + "Traceparent", "00-4bf92f3577b34da6a3ce929d0e0e4736-0000000000003546-01", + "Tracestate", "in=0000000000002435;0000000000003546,rojo=00f067aa0ba902b7", + ) + + ctx := metadata.NewIncomingContext(context.TODO(), md) + var span tracer.Span + r := sensor.NewTestRecorder() + tr := NewTracer(Recorder(r)) + if err := tr.Init(); err != nil { + t.Fatal(err) + } + ctx, span = tr.Start(ctx, "test", tracer.WithSpanKind(tracer.SpanKindServer)) + span.Finish() + + spans := r.GetQueuedSpans() + for _, s := range spans { + t.Logf("spans %#+v\n", s) + } + tr.Flush(ctx) +} diff --git a/options.go b/options.go index 4d117e5..ce0fcda 100644 --- a/options.go +++ b/options.go @@ -1,6 +1,7 @@ package instana import ( + instana "github.com/instana/go-sensor" sensor "github.com/instana/go-sensor" "go.unistack.org/micro/v3/tracer" ) @@ -10,3 +11,9 @@ type tracerOptionsKey struct{} func Options(opts *sensor.Options) tracer.Option { return tracer.SetOption(tracerOptionsKey{}, opts) } + +type recorderKey struct{} + +func Recorder(r instana.SpanRecorder) tracer.Option { + return tracer.SetOption(recorderKey{}, r) +}