Files
spinifex/internal/testkit/tracing.go
T

75 lines
2.3 KiB
Go

package testutil
import (
"testing"
"time"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/propagation"
sdktrace "go.opentelemetry.io/otel/sdk/trace"
"go.opentelemetry.io/otel/sdk/trace/tracetest"
"go.opentelemetry.io/otel/trace"
)
// RecordSpans installs a recording tracer provider and the W3C trace-context
// propagator for the duration of the test, restoring both afterwards. The
// defaults are no-ops, so without this a span assertion sees nothing.
//
// Both are process globals, so a test using this cannot run in parallel.
func RecordSpans(t *testing.T) *tracetest.SpanRecorder {
t.Helper()
recorder := tracetest.NewSpanRecorder()
previousTracer := otel.GetTracerProvider()
previousPropagator := otel.GetTextMapPropagator()
otel.SetTracerProvider(sdktrace.NewTracerProvider(sdktrace.WithSpanProcessor(recorder)))
otel.SetTextMapPropagator(propagation.TraceContext{})
t.Cleanup(func() {
otel.SetTracerProvider(previousTracer)
otel.SetTextMapPropagator(previousPropagator)
})
return recorder
}
// SpanAttribute returns span's value for key, failing the test when absent.
func SpanAttribute(t *testing.T, span sdktrace.ReadOnlySpan, key string) attribute.Value {
t.Helper()
for _, attr := range span.Attributes() {
if string(attr.Key) == key {
return attr.Value
}
}
t.Fatalf("span %q has no attribute %q", span.Name(), key)
return attribute.Value{}
}
// SpansByKind returns the last recorded span of each kind (owner-first routing
// can record several client legs; the last answered). Servers reply before
// ending their span, so this waits for one and fails the test if none ends.
func SpansByKind(t *testing.T, recorder *tracetest.SpanRecorder) (client, server sdktrace.ReadOnlySpan) {
t.Helper()
deadline := time.Now().Add(5 * time.Second)
for {
client, server = lastSpansByKind(recorder.Ended())
if server != nil {
return client, server
}
if time.Now().After(deadline) {
t.Fatal("no server span recorded within 5s")
}
time.Sleep(time.Millisecond)
}
}
func lastSpansByKind(spans []sdktrace.ReadOnlySpan) (client, server sdktrace.ReadOnlySpan) {
for _, span := range spans {
switch span.SpanKind() {
case trace.SpanKindClient:
client = span
case trace.SpanKindServer:
server = span
}
}
return client, server
}