334 lines
8.5 KiB
Go
334 lines
8.5 KiB
Go
package tracing
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"net/http"
|
|
"net/url"
|
|
"runtime"
|
|
"testing"
|
|
|
|
"github.com/go-chi/chi"
|
|
"github.com/influxdata/httprouter"
|
|
"github.com/opentracing/opentracing-go"
|
|
"github.com/opentracing/opentracing-go/mocktracer"
|
|
)
|
|
|
|
func TestInjectAndExtractHTTPRequest(t *testing.T) {
|
|
tracer := mocktracer.New()
|
|
|
|
oldTracer := opentracing.GlobalTracer()
|
|
opentracing.SetGlobalTracer(tracer)
|
|
defer opentracing.SetGlobalTracer(oldTracer)
|
|
|
|
request, err := http.NewRequest(http.MethodPost, "http://localhost/", nil)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
span := tracer.StartSpan("operation name")
|
|
|
|
InjectToHTTPRequest(span, request)
|
|
gotSpan, _ := ExtractFromHTTPRequest(request, "MyStruct")
|
|
|
|
if span.(*mocktracer.MockSpan).SpanContext.TraceID != gotSpan.(*mocktracer.MockSpan).SpanContext.TraceID {
|
|
t.Error("injected and extracted traceIDs not equal")
|
|
}
|
|
|
|
if span.(*mocktracer.MockSpan).SpanContext.SpanID != gotSpan.(*mocktracer.MockSpan).ParentID {
|
|
t.Error("injected span ID does not match extracted span parent ID")
|
|
}
|
|
}
|
|
|
|
func TestExtractHTTPRequest(t *testing.T) {
|
|
var (
|
|
tracer = mocktracer.New()
|
|
oldTracer = opentracing.GlobalTracer()
|
|
ctx = context.Background()
|
|
)
|
|
|
|
opentracing.SetGlobalTracer(tracer)
|
|
defer opentracing.SetGlobalTracer(oldTracer)
|
|
|
|
for _, test := range []struct {
|
|
name string
|
|
handlerName string
|
|
path string
|
|
ctx context.Context
|
|
tags map[string]interface{}
|
|
method string
|
|
}{
|
|
{
|
|
name: "happy path",
|
|
handlerName: "WriteHandler",
|
|
ctx: context.WithValue(ctx, httprouter.MatchedRouteKey, "/api/v2/write"),
|
|
method: http.MethodGet,
|
|
path: "/api/v2/write",
|
|
tags: map[string]interface{}{
|
|
"route": "/api/v2/write",
|
|
"handler": "WriteHandler",
|
|
},
|
|
},
|
|
{
|
|
name: "happy path bucket handler",
|
|
handlerName: "BucketHandler",
|
|
ctx: context.WithValue(ctx, httprouter.MatchedRouteKey, "/api/v2/buckets/:bucket_id"),
|
|
path: "/api/v2/buckets/12345",
|
|
method: http.MethodGet,
|
|
tags: map[string]interface{}{
|
|
"route": "/api/v2/buckets/:bucket_id",
|
|
"handler": "BucketHandler",
|
|
},
|
|
},
|
|
{
|
|
name: "happy path bucket handler (chi)",
|
|
handlerName: "BucketHandler",
|
|
ctx: context.WithValue(
|
|
ctx,
|
|
chi.RouteCtxKey,
|
|
&chi.Context{RoutePath: "/api/v2/buckets/:bucket_id", RouteMethod: "GET"},
|
|
),
|
|
path: "/api/v2/buckets/12345",
|
|
method: http.MethodGet,
|
|
tags: map[string]interface{}{
|
|
"route": "/api/v2/buckets/:bucket_id",
|
|
"method": "GET",
|
|
"handler": "BucketHandler",
|
|
},
|
|
},
|
|
{
|
|
name: "empty path",
|
|
handlerName: "Home",
|
|
ctx: ctx,
|
|
method: http.MethodGet,
|
|
tags: map[string]interface{}{
|
|
"handler": "Home",
|
|
},
|
|
},
|
|
} {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
request, err := http.NewRequest(test.method, "http://localhost"+test.path, nil)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
span := tracer.StartSpan("operation name")
|
|
|
|
InjectToHTTPRequest(span, request)
|
|
gotSpan, _ := ExtractFromHTTPRequest(request.WithContext(test.ctx), test.handlerName)
|
|
|
|
if op := gotSpan.(*mocktracer.MockSpan).OperationName; op != "request" {
|
|
t.Fatalf("operation name %q != request", op)
|
|
}
|
|
|
|
tags := gotSpan.(*mocktracer.MockSpan).Tags()
|
|
for k, v := range test.tags {
|
|
found, ok := tags[k]
|
|
if !ok {
|
|
t.Errorf("tag not found in span %q", k)
|
|
continue
|
|
}
|
|
|
|
if found != v {
|
|
t.Errorf("expected %v, found %v for tag %q", v, found, k)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestStartSpanFromContext(t *testing.T) {
|
|
tracer := mocktracer.New()
|
|
|
|
oldTracer := opentracing.GlobalTracer()
|
|
opentracing.SetGlobalTracer(tracer)
|
|
defer opentracing.SetGlobalTracer(oldTracer)
|
|
|
|
type testCase struct {
|
|
ctx context.Context
|
|
expectPanic bool
|
|
expectParent bool
|
|
}
|
|
var testCases []testCase
|
|
|
|
testCases = append(testCases,
|
|
testCase{
|
|
ctx: nil,
|
|
expectPanic: true,
|
|
expectParent: false,
|
|
},
|
|
testCase{
|
|
ctx: context.Background(),
|
|
expectPanic: false,
|
|
expectParent: false,
|
|
})
|
|
|
|
parentSpan := opentracing.StartSpan("parent operation name")
|
|
testCases = append(testCases, testCase{
|
|
ctx: opentracing.ContextWithSpan(context.Background(), parentSpan),
|
|
expectPanic: false,
|
|
expectParent: true,
|
|
})
|
|
|
|
for i, tc := range testCases {
|
|
t.Run(fmt.Sprint(i), func(t *testing.T) {
|
|
var span opentracing.Span
|
|
var ctx context.Context
|
|
var gotPanic bool
|
|
|
|
func(inputCtx context.Context) {
|
|
defer func() {
|
|
if recover() != nil {
|
|
gotPanic = true
|
|
}
|
|
}()
|
|
span, ctx = StartSpanFromContext(inputCtx)
|
|
}(tc.ctx)
|
|
|
|
if tc.expectPanic != gotPanic {
|
|
t.Errorf("panic: expect %v got %v", tc.expectPanic, gotPanic)
|
|
}
|
|
if tc.expectPanic {
|
|
// No other valid checks if panic.
|
|
return
|
|
}
|
|
if ctx == nil {
|
|
t.Error("never expect non-nil ctx")
|
|
}
|
|
if span == nil {
|
|
t.Error("never expect non-nil Span")
|
|
}
|
|
foundParent := span.(*mocktracer.MockSpan).ParentID != 0
|
|
if tc.expectParent != foundParent {
|
|
t.Errorf("parent: expect %v got %v", tc.expectParent, foundParent)
|
|
}
|
|
if ctx == tc.ctx {
|
|
t.Errorf("always expect fresh context")
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestLogErrorNil(t *testing.T) {
|
|
tracer := mocktracer.New()
|
|
span := tracer.StartSpan("test").(*mocktracer.MockSpan)
|
|
|
|
var err error
|
|
if err2 := LogError(span, err); err2 != nil {
|
|
t.Errorf("expected nil err, got '%s'", err2.Error())
|
|
}
|
|
|
|
if len(span.Logs()) > 0 {
|
|
t.Errorf("expected zero new span logs, got %d", len(span.Logs()))
|
|
println(span.Logs()[0].Fields[0].Key)
|
|
}
|
|
}
|
|
|
|
/*
|
|
BenchmarkLocal_StartSpanFromContext-8 2000000 681 ns/op 224 B/op 4 allocs/op
|
|
BenchmarkLocal_StartSpanFromContext_runtimeCaller-8 3000000 534 ns/op
|
|
BenchmarkLocal_StartSpanFromContext_runtimeCallers-8 10000000 196 ns/op
|
|
BenchmarkLocal_StartSpanFromContext_runtimeFuncForPC-8 200000000 7.28 ns/op
|
|
BenchmarkLocal_StartSpanFromContext_runtimeCallersFrames-8 10000000 234 ns/op
|
|
BenchmarkLocal_StartSpanFromContext_runtimeFuncFileLine-8 20000000 103 ns/op
|
|
BenchmarkOpentracing_StartSpanFromContext-8 10000000 155 ns/op 96 B/op 3 allocs/op
|
|
BenchmarkOpentracing_StartSpan_root-8 200000000 7.68 ns/op 0 B/op 0 allocs/op
|
|
BenchmarkOpentracing_StartSpan_child-8 20000000 71.2 ns/op 48 B/op 2 allocs/op
|
|
*/
|
|
|
|
func BenchmarkLocal_StartSpanFromContext(b *testing.B) {
|
|
b.ReportAllocs()
|
|
|
|
parentSpan := opentracing.StartSpan("parent operation name")
|
|
ctx := opentracing.ContextWithSpan(context.Background(), parentSpan)
|
|
|
|
for n := 0; n < b.N; n++ {
|
|
StartSpanFromContext(ctx)
|
|
}
|
|
}
|
|
|
|
func BenchmarkLocal_StartSpanFromContext_runtimeCaller(b *testing.B) {
|
|
for n := 0; n < b.N; n++ {
|
|
_, _, _, _ = runtime.Caller(1)
|
|
}
|
|
}
|
|
|
|
func BenchmarkLocal_StartSpanFromContext_runtimeCallers(b *testing.B) {
|
|
var pcs [1]uintptr
|
|
|
|
for n := 0; n < b.N; n++ {
|
|
_ = runtime.Callers(2, pcs[:])
|
|
}
|
|
}
|
|
|
|
func BenchmarkLocal_StartSpanFromContext_runtimeFuncForPC(b *testing.B) {
|
|
var pcs [1]uintptr
|
|
_ = runtime.Callers(2, pcs[:])
|
|
|
|
for n := 0; n < b.N; n++ {
|
|
_ = runtime.FuncForPC(pcs[0])
|
|
}
|
|
}
|
|
|
|
func BenchmarkLocal_StartSpanFromContext_runtimeCallersFrames(b *testing.B) {
|
|
pc, _, _, ok := runtime.Caller(1)
|
|
if !ok {
|
|
b.Fatal("runtime.Caller failed")
|
|
}
|
|
|
|
for n := 0; n < b.N; n++ {
|
|
_, _ = runtime.CallersFrames([]uintptr{pc}).Next()
|
|
}
|
|
}
|
|
|
|
func BenchmarkLocal_StartSpanFromContext_runtimeFuncFileLine(b *testing.B) {
|
|
var pcs [1]uintptr
|
|
_ = runtime.Callers(2, pcs[:])
|
|
fn := runtime.FuncForPC(pcs[0])
|
|
|
|
for n := 0; n < b.N; n++ {
|
|
_, _ = fn.FileLine(pcs[0])
|
|
}
|
|
}
|
|
|
|
func BenchmarkOpentracing_StartSpanFromContext(b *testing.B) {
|
|
b.ReportAllocs()
|
|
|
|
parentSpan := opentracing.StartSpan("parent operation name")
|
|
ctx := opentracing.ContextWithSpan(context.Background(), parentSpan)
|
|
|
|
for n := 0; n < b.N; n++ {
|
|
_, _ = opentracing.StartSpanFromContext(ctx, "operation name")
|
|
}
|
|
}
|
|
|
|
func BenchmarkOpentracing_StartSpan_root(b *testing.B) {
|
|
b.ReportAllocs()
|
|
|
|
for n := 0; n < b.N; n++ {
|
|
_ = opentracing.StartSpan("operation name")
|
|
}
|
|
}
|
|
|
|
func BenchmarkOpentracing_StartSpan_child(b *testing.B) {
|
|
b.ReportAllocs()
|
|
|
|
parentSpan := opentracing.StartSpan("parent operation name")
|
|
|
|
for n := 0; n < b.N; n++ {
|
|
_ = opentracing.StartSpan("operation name", opentracing.ChildOf(parentSpan.Context()))
|
|
}
|
|
}
|
|
|
|
func BenchmarkOpentracing_ExtractFromHTTPRequest(b *testing.B) {
|
|
b.ReportAllocs()
|
|
|
|
req := &http.Request{
|
|
URL: &url.URL{Path: "/api/v2/organization/12345"},
|
|
}
|
|
|
|
for n := 0; n < b.N; n++ {
|
|
_, _ = ExtractFromHTTPRequest(req, "OrganizationHandler")
|
|
}
|
|
}
|