mirror of
https://github.com/open-telemetry/opentelemetry-go.git
synced 2025-02-05 13:15:41 +02:00
Decouple instrumentation from SDK (#983)
* Remove otel/sdk dependency from grpctrace Use otel/trace/testtrace instead and cleanup testing code. * Update httptrace to not depend on the SDK Update testing to use api/trace/testtrace instead. * Add changes to Changelog * Restore check for `http.local` attr on `http.getconn`
This commit is contained in:
parent
42c2a86ea4
commit
d6bf2fbfc3
@ -45,6 +45,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
|
||||
- Use `global.Handle` for span export errors in the OTLP exporter. (#946)
|
||||
- Correct Go language formatting in the README documentation. (#961)
|
||||
- Remove default SDK dependencies from the `go.opentelemetry.io/otel/api` package. (#977)
|
||||
- Remove default SDK dependencies from the `go.opentelemetry.io/otel/instrumentation` package. (#983)
|
||||
- Move documented examples for `go.opentelemetry.io/otel/instrumentation/grpctrace` interceptors into Go example tests. (#984)
|
||||
|
||||
## [0.9.0] - 2020-07-20
|
||||
|
@ -20,6 +20,7 @@ import (
|
||||
"time"
|
||||
|
||||
"go.opentelemetry.io/otel/api/standard"
|
||||
"go.opentelemetry.io/otel/api/trace/testtrace"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
@ -32,19 +33,30 @@ import (
|
||||
"google.golang.org/grpc/status"
|
||||
|
||||
"go.opentelemetry.io/otel/api/kv"
|
||||
export "go.opentelemetry.io/otel/sdk/export/trace"
|
||||
sdktrace "go.opentelemetry.io/otel/sdk/trace"
|
||||
)
|
||||
|
||||
type testExporter struct {
|
||||
mu sync.Mutex
|
||||
spanMap map[string]*export.SpanData
|
||||
type SpanRecorder struct {
|
||||
mu sync.RWMutex
|
||||
spans map[string]*testtrace.Span
|
||||
}
|
||||
|
||||
func (t *testExporter) ExportSpan(ctx context.Context, s *export.SpanData) {
|
||||
t.mu.Lock()
|
||||
defer t.mu.Unlock()
|
||||
t.spanMap[s.Name] = s
|
||||
func NewSpanRecorder() *SpanRecorder {
|
||||
return &SpanRecorder{spans: make(map[string]*testtrace.Span)}
|
||||
}
|
||||
|
||||
func (sr *SpanRecorder) OnStart(span *testtrace.Span) {}
|
||||
|
||||
func (sr *SpanRecorder) OnEnd(span *testtrace.Span) {
|
||||
sr.mu.Lock()
|
||||
defer sr.mu.Unlock()
|
||||
sr.spans[span.Name()] = span
|
||||
}
|
||||
|
||||
func (sr *SpanRecorder) Get(name string) (*testtrace.Span, bool) {
|
||||
sr.mu.RLock()
|
||||
defer sr.mu.RUnlock()
|
||||
s, ok := sr.spans[name]
|
||||
return s, ok
|
||||
}
|
||||
|
||||
type mockUICInvoker struct {
|
||||
@ -69,18 +81,13 @@ func (mm *mockProtoMessage) ProtoMessage() {
|
||||
}
|
||||
|
||||
func TestUnaryClientInterceptor(t *testing.T) {
|
||||
exp := &testExporter{spanMap: make(map[string]*export.SpanData)}
|
||||
tp, _ := sdktrace.NewProvider(sdktrace.WithSyncer(exp),
|
||||
sdktrace.WithConfig(sdktrace.Config{
|
||||
DefaultSampler: sdktrace.AlwaysSample(),
|
||||
},
|
||||
))
|
||||
|
||||
clientConn, err := grpc.Dial("fake:connection", grpc.WithInsecure())
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create client connection: %v", err)
|
||||
}
|
||||
|
||||
sr := NewSpanRecorder()
|
||||
tp := testtrace.NewProvider(testtrace.WithSpanRecorder(sr))
|
||||
tracer := tp.Tracer("grpctrace/client")
|
||||
unaryInterceptor := UnaryClientInterceptor(tracer)
|
||||
|
||||
@ -210,70 +217,26 @@ func TestUnaryClientInterceptor(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, check := range checks {
|
||||
err = unaryInterceptor(context.Background(), check.method, req, reply, clientConn, uniInterceptorInvoker.invoker)
|
||||
if err != nil {
|
||||
t.Errorf("failed to run unary interceptor: %v", err)
|
||||
if !assert.NoError(t, unaryInterceptor(context.Background(), check.method, req, reply, clientConn, uniInterceptorInvoker.invoker)) {
|
||||
continue
|
||||
}
|
||||
|
||||
spanData, ok := exp.spanMap[check.name]
|
||||
if !ok {
|
||||
t.Errorf("no span data found for name < %s >", check.name)
|
||||
span, ok := sr.Get(check.name)
|
||||
if !assert.True(t, ok, "missing span %q", check.name) {
|
||||
continue
|
||||
}
|
||||
|
||||
attrs := spanData.Attributes
|
||||
if len(check.expectedAttr) > len(attrs) {
|
||||
t.Errorf("attributes received are less than expected attributes, received %d, expected %d",
|
||||
len(attrs), len(check.expectedAttr))
|
||||
}
|
||||
for _, attr := range attrs {
|
||||
expectedAttr, ok := check.expectedAttr[attr.Key]
|
||||
if ok {
|
||||
if expectedAttr != attr.Value {
|
||||
t.Errorf("name: %s invalid %s found. expected %s, actual %s", check.name, string(attr.Key),
|
||||
expectedAttr.AsString(), attr.Value.AsString())
|
||||
}
|
||||
delete(check.expectedAttr, attr.Key)
|
||||
} else {
|
||||
t.Errorf("attribute %s not found in expected attributes map", string(attr.Key))
|
||||
}
|
||||
}
|
||||
|
||||
// Check if any expected attr not seen
|
||||
if len(check.expectedAttr) > 0 {
|
||||
for attr := range check.expectedAttr {
|
||||
t.Errorf("missing attribute %s in span", string(attr))
|
||||
}
|
||||
}
|
||||
|
||||
events := spanData.MessageEvents
|
||||
if len(check.eventsAttr) > len(events) {
|
||||
t.Errorf("events received are less than expected events, received %d, expected %d",
|
||||
len(events), len(check.eventsAttr))
|
||||
}
|
||||
for event := 0; event < len(check.eventsAttr); event++ {
|
||||
for _, attr := range events[event].Attributes {
|
||||
expectedAttr, ok := check.eventsAttr[event][attr.Key]
|
||||
if ok {
|
||||
if attr.Value != expectedAttr {
|
||||
t.Errorf("invalid value for attribute %s in events, expected %s actual %s",
|
||||
string(attr.Key), attr.Value.AsString(), expectedAttr.AsString())
|
||||
}
|
||||
delete(check.eventsAttr[event], attr.Key)
|
||||
} else {
|
||||
t.Errorf("attribute in event %s not found in expected attributes map", string(attr.Key))
|
||||
}
|
||||
}
|
||||
if len(check.eventsAttr[event]) > 0 {
|
||||
for attr := range check.eventsAttr[event] {
|
||||
t.Errorf("missing attribute %s in span event", string(attr))
|
||||
}
|
||||
}
|
||||
}
|
||||
assert.Equal(t, check.expectedAttr, span.Attributes())
|
||||
assert.Equal(t, check.eventsAttr, eventAttrMap(span.Events()))
|
||||
}
|
||||
}
|
||||
|
||||
func eventAttrMap(events []testtrace.Event) []map[kv.Key]kv.Value {
|
||||
maps := make([]map[kv.Key]kv.Value, len(events))
|
||||
for i, event := range events {
|
||||
maps[i] = event.Attributes
|
||||
}
|
||||
return maps
|
||||
}
|
||||
|
||||
type mockClientStream struct {
|
||||
Desc *grpc.StreamDesc
|
||||
Ctx context.Context
|
||||
@ -287,18 +250,14 @@ func (mockClientStream) Header() (metadata.MD, error) { return nil, nil }
|
||||
func (mockClientStream) Trailer() metadata.MD { return nil }
|
||||
|
||||
func TestStreamClientInterceptor(t *testing.T) {
|
||||
exp := &testExporter{spanMap: make(map[string]*export.SpanData)}
|
||||
tp, _ := sdktrace.NewProvider(sdktrace.WithSyncer(exp),
|
||||
sdktrace.WithConfig(sdktrace.Config{
|
||||
DefaultSampler: sdktrace.AlwaysSample(),
|
||||
},
|
||||
))
|
||||
clientConn, err := grpc.Dial("fake:connection", grpc.WithInsecure())
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create client connection: %v", err)
|
||||
}
|
||||
|
||||
// tracer
|
||||
sr := NewSpanRecorder()
|
||||
tp := testtrace.NewProvider(testtrace.WithSpanRecorder(sr))
|
||||
tracer := tp.Tracer("grpctrace/Server")
|
||||
streamCI := StreamClientInterceptor(tracer)
|
||||
|
||||
@ -306,7 +265,8 @@ func TestStreamClientInterceptor(t *testing.T) {
|
||||
method := "/github.com.serviceName/bar"
|
||||
name := "github.com.serviceName/bar"
|
||||
|
||||
streamClient, err := streamCI(context.Background(),
|
||||
streamClient, err := streamCI(
|
||||
context.Background(),
|
||||
&grpc.StreamDesc{ServerStreams: true},
|
||||
clientConn,
|
||||
method,
|
||||
@ -317,16 +277,11 @@ func TestStreamClientInterceptor(t *testing.T) {
|
||||
opts ...grpc.CallOption) (grpc.ClientStream, error) {
|
||||
mockClStr = mockClientStream{Desc: desc, Ctx: ctx}
|
||||
return mockClStr, nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("failed to initialize grpc stream client: %v", err)
|
||||
}
|
||||
|
||||
// no span exported while stream is open
|
||||
if _, ok := exp.spanMap[name]; ok {
|
||||
t.Fatalf("span shouldn't end while stream is open")
|
||||
}
|
||||
},
|
||||
)
|
||||
require.NoError(t, err, "initialize grpc stream client")
|
||||
_, ok := sr.Get(name)
|
||||
require.False(t, ok, "span should ended while stream is open")
|
||||
|
||||
req := &mockProtoMessage{}
|
||||
reply := &mockProtoMessage{}
|
||||
@ -343,53 +298,36 @@ func TestStreamClientInterceptor(t *testing.T) {
|
||||
_ = streamClient.RecvMsg(reply)
|
||||
|
||||
// added retry because span end is called in separate go routine
|
||||
var spanData *export.SpanData
|
||||
var span *testtrace.Span
|
||||
for retry := 0; retry < 5; retry++ {
|
||||
ok := false
|
||||
exp.mu.Lock()
|
||||
spanData, ok = exp.spanMap[name]
|
||||
exp.mu.Unlock()
|
||||
span, ok = sr.Get(name)
|
||||
if ok {
|
||||
break
|
||||
}
|
||||
time.Sleep(time.Second * 1)
|
||||
}
|
||||
if spanData == nil {
|
||||
t.Fatalf("no span data found for name < %s >", name)
|
||||
}
|
||||
require.True(t, ok, "missing span %s", name)
|
||||
|
||||
attrs := spanData.Attributes
|
||||
expectedAttr := map[kv.Key]string{
|
||||
standard.RPCSystemKey: "grpc",
|
||||
standard.RPCServiceKey: "github.com.serviceName",
|
||||
standard.RPCMethodKey: "bar",
|
||||
standard.NetPeerIPKey: "fake",
|
||||
standard.NetPeerPortKey: "connection",
|
||||
expectedAttr := map[kv.Key]kv.Value{
|
||||
standard.RPCSystemKey: kv.StringValue("grpc"),
|
||||
standard.RPCServiceKey: kv.StringValue("github.com.serviceName"),
|
||||
standard.RPCMethodKey: kv.StringValue("bar"),
|
||||
standard.NetPeerIPKey: kv.StringValue("fake"),
|
||||
standard.NetPeerPortKey: kv.StringValue("connection"),
|
||||
}
|
||||
assert.Equal(t, expectedAttr, span.Attributes())
|
||||
|
||||
for _, attr := range attrs {
|
||||
expected, ok := expectedAttr[attr.Key]
|
||||
if ok {
|
||||
if expected != attr.Value.AsString() {
|
||||
t.Errorf("name: %s invalid %s found. expected %s, actual %s", name, string(attr.Key),
|
||||
expected, attr.Value.AsString())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
events := spanData.MessageEvents
|
||||
if len(events) != 20 {
|
||||
t.Fatalf("incorrect number of events expected 20 got %d", len(events))
|
||||
}
|
||||
events := span.Events()
|
||||
require.Len(t, events, 20)
|
||||
for i := 0; i < 20; i += 2 {
|
||||
msgID := i/2 + 1
|
||||
validate := func(eventName string, attrs []kv.KeyValue) {
|
||||
for _, attr := range attrs {
|
||||
if attr.Key == standard.RPCMessageTypeKey && attr.Value.AsString() != eventName {
|
||||
t.Errorf("invalid event on index: %d expecting %s event, receive %s event", i, eventName, attr.Value.AsString())
|
||||
validate := func(eventName string, attrs map[kv.Key]kv.Value) {
|
||||
for k, v := range attrs {
|
||||
if k == standard.RPCMessageTypeKey && v.AsString() != eventName {
|
||||
t.Errorf("invalid event on index: %d expecting %s event, receive %s event", i, eventName, v.AsString())
|
||||
}
|
||||
if attr.Key == standard.RPCMessageIDKey && attr.Value != kv.IntValue(msgID) {
|
||||
t.Errorf("invalid id for message event expected %d received %d", msgID, attr.Value.AsInt32())
|
||||
if k == standard.RPCMessageIDKey && v != kv.IntValue(msgID) {
|
||||
t.Errorf("invalid id for message event expected %d received %d", msgID, v.AsInt32())
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -402,37 +340,30 @@ func TestStreamClientInterceptor(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestServerInterceptorError(t *testing.T) {
|
||||
exp := &testExporter{spanMap: make(map[string]*export.SpanData)}
|
||||
tp, err := sdktrace.NewProvider(
|
||||
sdktrace.WithSyncer(exp),
|
||||
sdktrace.WithConfig(sdktrace.Config{
|
||||
DefaultSampler: sdktrace.AlwaysSample(),
|
||||
}),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
sr := NewSpanRecorder()
|
||||
tp := testtrace.NewProvider(testtrace.WithSpanRecorder(sr))
|
||||
tracer := tp.Tracer("grpctrace/Server")
|
||||
usi := UnaryServerInterceptor(tracer)
|
||||
deniedErr := status.Error(codes.PermissionDenied, "PERMISSION_DENIED_TEXT")
|
||||
handler := func(_ context.Context, _ interface{}) (interface{}, error) {
|
||||
return nil, deniedErr
|
||||
}
|
||||
_, err = usi(context.Background(), &mockProtoMessage{}, &grpc.UnaryServerInfo{}, handler)
|
||||
_, err := usi(context.Background(), &mockProtoMessage{}, &grpc.UnaryServerInfo{}, handler)
|
||||
require.Error(t, err)
|
||||
assert.Equal(t, err, deniedErr)
|
||||
|
||||
span, ok := exp.spanMap[""]
|
||||
span, ok := sr.Get("")
|
||||
if !ok {
|
||||
t.Fatalf("failed to export error span")
|
||||
}
|
||||
assert.Equal(t, span.StatusCode, codes.PermissionDenied)
|
||||
assert.Contains(t, deniedErr.Error(), span.StatusMessage)
|
||||
assert.Len(t, span.MessageEvents, 2)
|
||||
assert.Equal(t, []kv.KeyValue{
|
||||
kv.String("message.type", "SENT"),
|
||||
kv.Int("message.id", 1),
|
||||
kv.Int("message.uncompressed_size", 26),
|
||||
}, span.MessageEvents[1].Attributes)
|
||||
assert.Equal(t, span.StatusCode(), codes.PermissionDenied)
|
||||
assert.Contains(t, deniedErr.Error(), span.StatusMessage())
|
||||
assert.Len(t, span.Events(), 2)
|
||||
assert.Equal(t, map[kv.Key]kv.Value{
|
||||
kv.Key("message.type"): kv.StringValue("SENT"),
|
||||
kv.Key("message.id"): kv.IntValue(1),
|
||||
kv.Key("message.uncompressed_size"): kv.IntValue(26),
|
||||
}, span.Events()[1].Attributes)
|
||||
}
|
||||
|
||||
func TestParseFullMethod(t *testing.T) {
|
||||
|
@ -18,46 +18,26 @@ import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
nhtrace "net/http/httptrace"
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"go.opentelemetry.io/otel/api/global"
|
||||
"go.opentelemetry.io/otel/api/kv"
|
||||
"go.opentelemetry.io/otel/api/trace/testtrace"
|
||||
"go.opentelemetry.io/otel/instrumentation/httptrace"
|
||||
export "go.opentelemetry.io/otel/sdk/export/trace"
|
||||
sdktrace "go.opentelemetry.io/otel/sdk/trace"
|
||||
)
|
||||
|
||||
type testExporter struct {
|
||||
mu sync.Mutex
|
||||
spanMap map[string][]*export.SpanData
|
||||
}
|
||||
type SpanRecorder map[string]*testtrace.Span
|
||||
|
||||
func (t *testExporter) ExportSpan(ctx context.Context, s *export.SpanData) {
|
||||
t.mu.Lock()
|
||||
defer t.mu.Unlock()
|
||||
var spans []*export.SpanData
|
||||
var ok bool
|
||||
|
||||
if spans, ok = t.spanMap[s.Name]; !ok {
|
||||
spans = []*export.SpanData{}
|
||||
t.spanMap[s.Name] = spans
|
||||
}
|
||||
spans = append(spans, s)
|
||||
t.spanMap[s.Name] = spans
|
||||
}
|
||||
|
||||
var _ export.SpanSyncer = (*testExporter)(nil)
|
||||
func (sr *SpanRecorder) OnStart(span *testtrace.Span) {}
|
||||
func (sr *SpanRecorder) OnEnd(span *testtrace.Span) { (*sr)[span.Name()] = span }
|
||||
|
||||
func TestHTTPRequestWithClientTrace(t *testing.T) {
|
||||
exp := &testExporter{
|
||||
spanMap: make(map[string][]*export.SpanData),
|
||||
}
|
||||
tp, _ := sdktrace.NewProvider(sdktrace.WithSyncer(exp), sdktrace.WithConfig(sdktrace.Config{DefaultSampler: sdktrace.AlwaysSample()}))
|
||||
sr := SpanRecorder{}
|
||||
tp := testtrace.NewProvider(testtrace.WithSpanRecorder(&sr))
|
||||
global.SetTraceProvider(tp)
|
||||
|
||||
tr := tp.Tracer("httptrace/client")
|
||||
|
||||
// Mock http server
|
||||
@ -86,34 +66,23 @@ func TestHTTPRequestWithClientTrace(t *testing.T) {
|
||||
panic("unexpected error in http request: " + err.Error())
|
||||
}
|
||||
|
||||
getSpan := func(name string) *export.SpanData {
|
||||
spans, ok := exp.spanMap[name]
|
||||
if !ok {
|
||||
t.Fatalf("no spans found with the name %s, %v", name, exp.spanMap)
|
||||
}
|
||||
|
||||
if len(spans) != 1 {
|
||||
t.Fatalf("Expected exactly one span for %s but found %d", name, len(spans))
|
||||
}
|
||||
|
||||
return spans[0]
|
||||
}
|
||||
|
||||
testLen := []struct {
|
||||
name string
|
||||
attributes []kv.KeyValue
|
||||
attributes map[kv.Key]kv.Value
|
||||
parent string
|
||||
}{
|
||||
{
|
||||
name: "http.connect",
|
||||
attributes: []kv.KeyValue{kv.String("http.remote", address.String())},
|
||||
parent: "http.getconn",
|
||||
name: "http.connect",
|
||||
attributes: map[kv.Key]kv.Value{
|
||||
kv.Key("http.remote"): kv.StringValue(address.String()),
|
||||
},
|
||||
parent: "http.getconn",
|
||||
},
|
||||
{
|
||||
name: "http.getconn",
|
||||
attributes: []kv.KeyValue{
|
||||
kv.String("http.remote", address.String()),
|
||||
kv.String("http.host", address.String()),
|
||||
attributes: map[kv.Key]kv.Value{
|
||||
kv.Key("http.remote"): kv.StringValue(address.String()),
|
||||
kv.Key("http.host"): kv.StringValue(address.String()),
|
||||
},
|
||||
parent: "test",
|
||||
},
|
||||
@ -134,51 +103,42 @@ func TestHTTPRequestWithClientTrace(t *testing.T) {
|
||||
},
|
||||
}
|
||||
for _, tl := range testLen {
|
||||
span := getSpan(tl.name)
|
||||
|
||||
if !assert.Contains(t, sr, tl.name) {
|
||||
continue
|
||||
}
|
||||
span := sr[tl.name]
|
||||
if tl.parent != "" {
|
||||
parentSpan := getSpan(tl.parent)
|
||||
|
||||
if span.ParentSpanID != parentSpan.SpanContext.SpanID {
|
||||
t.Fatalf("[span %s] does not have expected parent span %s", tl.name, tl.parent)
|
||||
if assert.Contains(t, sr, tl.parent) {
|
||||
assert.Equal(t, span.ParentSpanID(), sr[tl.parent].SpanContext().SpanID)
|
||||
}
|
||||
}
|
||||
|
||||
actualAttrs := make(map[kv.Key]string)
|
||||
for _, attr := range span.Attributes {
|
||||
actualAttrs[attr.Key] = attr.Value.Emit()
|
||||
}
|
||||
|
||||
expectedAttrs := make(map[kv.Key]string)
|
||||
for _, attr := range tl.attributes {
|
||||
expectedAttrs[attr.Key] = attr.Value.Emit()
|
||||
}
|
||||
|
||||
if tl.name == "http.getconn" {
|
||||
local := kv.Key("http.local")
|
||||
// http.local attribute is not deterministic, just make sure it exists for `getconn`.
|
||||
if _, ok := actualAttrs[local]; ok {
|
||||
delete(actualAttrs, local)
|
||||
} else {
|
||||
t.Fatalf("[span %s] is missing attribute %v", tl.name, local)
|
||||
if len(tl.attributes) > 0 {
|
||||
attrs := span.Attributes()
|
||||
if tl.name == "http.getconn" {
|
||||
// http.local attribute uses a non-deterministic port.
|
||||
local := kv.Key("http.local")
|
||||
assert.Contains(t, attrs, local)
|
||||
delete(attrs, local)
|
||||
}
|
||||
}
|
||||
|
||||
if diff := cmp.Diff(actualAttrs, expectedAttrs); diff != "" {
|
||||
t.Fatalf("[span %s] Attributes are different: %v", tl.name, diff)
|
||||
assert.Equal(t, tl.attributes, attrs)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type MultiSpanRecorder map[string][]*testtrace.Span
|
||||
|
||||
func (sr *MultiSpanRecorder) Reset() { (*sr) = MultiSpanRecorder{} }
|
||||
func (sr *MultiSpanRecorder) OnStart(span *testtrace.Span) {}
|
||||
func (sr *MultiSpanRecorder) OnEnd(span *testtrace.Span) {
|
||||
(*sr)[span.Name()] = append((*sr)[span.Name()], span)
|
||||
}
|
||||
|
||||
func TestConcurrentConnectionStart(t *testing.T) {
|
||||
exp := &testExporter{
|
||||
spanMap: make(map[string][]*export.SpanData),
|
||||
}
|
||||
tp, _ := sdktrace.NewProvider(sdktrace.WithSyncer(exp), sdktrace.WithConfig(sdktrace.Config{DefaultSampler: sdktrace.AlwaysSample()}))
|
||||
global.SetTraceProvider(tp)
|
||||
|
||||
sr := MultiSpanRecorder{}
|
||||
global.SetTraceProvider(
|
||||
testtrace.NewProvider(testtrace.WithSpanRecorder(&sr)),
|
||||
)
|
||||
ct := httptrace.NewClientTrace(context.Background())
|
||||
|
||||
tts := []struct {
|
||||
name string
|
||||
run func()
|
||||
@ -186,8 +146,6 @@ func TestConcurrentConnectionStart(t *testing.T) {
|
||||
{
|
||||
name: "Open1Close1Open2Close2",
|
||||
run: func() {
|
||||
exp.spanMap = make(map[string][]*export.SpanData)
|
||||
|
||||
ct.ConnectStart("tcp", "127.0.0.1:3000")
|
||||
ct.ConnectDone("tcp", "127.0.0.1:3000", nil)
|
||||
ct.ConnectStart("tcp", "[::1]:3000")
|
||||
@ -197,8 +155,6 @@ func TestConcurrentConnectionStart(t *testing.T) {
|
||||
{
|
||||
name: "Open2Close2Open1Close1",
|
||||
run: func() {
|
||||
exp.spanMap = make(map[string][]*export.SpanData)
|
||||
|
||||
ct.ConnectStart("tcp", "[::1]:3000")
|
||||
ct.ConnectDone("tcp", "[::1]:3000", nil)
|
||||
ct.ConnectStart("tcp", "127.0.0.1:3000")
|
||||
@ -208,8 +164,6 @@ func TestConcurrentConnectionStart(t *testing.T) {
|
||||
{
|
||||
name: "Open1Open2Close1Close2",
|
||||
run: func() {
|
||||
exp.spanMap = make(map[string][]*export.SpanData)
|
||||
|
||||
ct.ConnectStart("tcp", "127.0.0.1:3000")
|
||||
ct.ConnectStart("tcp", "[::1]:3000")
|
||||
ct.ConnectDone("tcp", "127.0.0.1:3000", nil)
|
||||
@ -219,8 +173,6 @@ func TestConcurrentConnectionStart(t *testing.T) {
|
||||
{
|
||||
name: "Open1Open2Close2Close1",
|
||||
run: func() {
|
||||
exp.spanMap = make(map[string][]*export.SpanData)
|
||||
|
||||
ct.ConnectStart("tcp", "127.0.0.1:3000")
|
||||
ct.ConnectStart("tcp", "[::1]:3000")
|
||||
ct.ConnectDone("tcp", "[::1]:3000", nil)
|
||||
@ -230,8 +182,6 @@ func TestConcurrentConnectionStart(t *testing.T) {
|
||||
{
|
||||
name: "Open2Open1Close1Close2",
|
||||
run: func() {
|
||||
exp.spanMap = make(map[string][]*export.SpanData)
|
||||
|
||||
ct.ConnectStart("tcp", "[::1]:3000")
|
||||
ct.ConnectStart("tcp", "127.0.0.1:3000")
|
||||
ct.ConnectDone("tcp", "127.0.0.1:3000", nil)
|
||||
@ -241,8 +191,6 @@ func TestConcurrentConnectionStart(t *testing.T) {
|
||||
{
|
||||
name: "Open2Open1Close2Close1",
|
||||
run: func() {
|
||||
exp.spanMap = make(map[string][]*export.SpanData)
|
||||
|
||||
ct.ConnectStart("tcp", "[::1]:3000")
|
||||
ct.ConnectStart("tcp", "127.0.0.1:3000")
|
||||
ct.ConnectDone("tcp", "[::1]:3000", nil)
|
||||
@ -251,70 +199,40 @@ func TestConcurrentConnectionStart(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
expectedRemotes := []kv.KeyValue{
|
||||
kv.String("http.remote", "127.0.0.1:3000"),
|
||||
kv.String("http.remote", "[::1]:3000"),
|
||||
}
|
||||
for _, tt := range tts {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
sr.Reset()
|
||||
tt.run()
|
||||
spans := exp.spanMap["http.connect"]
|
||||
spans := sr["http.connect"]
|
||||
require.Len(t, spans, 2)
|
||||
|
||||
if l := len(spans); l != 2 {
|
||||
t.Fatalf("Expected 2 'http.connect' traces but found %d", l)
|
||||
}
|
||||
|
||||
remotes := make(map[string]struct{})
|
||||
var gotRemotes []kv.KeyValue
|
||||
for _, span := range spans {
|
||||
if l := len(span.Attributes); l != 1 {
|
||||
t.Fatalf("Expected 1 attribute on each span but found %d", l)
|
||||
}
|
||||
|
||||
attr := span.Attributes[0]
|
||||
if attr.Key != "http.remote" {
|
||||
t.Fatalf("Expected attribute to be 'http.remote' but found %s", attr.Key)
|
||||
}
|
||||
remotes[attr.Value.Emit()] = struct{}{}
|
||||
}
|
||||
|
||||
if l := len(remotes); l != 2 {
|
||||
t.Fatalf("Expected 2 different 'http.remote' but found %d", l)
|
||||
}
|
||||
|
||||
for _, remote := range []string{"127.0.0.1:3000", "[::1]:3000"} {
|
||||
if _, ok := remotes[remote]; !ok {
|
||||
t.Fatalf("Missing remote %s", remote)
|
||||
for k, v := range span.Attributes() {
|
||||
gotRemotes = append(gotRemotes, kv.Any(string(k), v.AsInterface()))
|
||||
}
|
||||
}
|
||||
assert.ElementsMatch(t, expectedRemotes, gotRemotes)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestEndBeforeStartCreatesSpan(t *testing.T) {
|
||||
exp := &testExporter{
|
||||
spanMap: make(map[string][]*export.SpanData),
|
||||
}
|
||||
tp, _ := sdktrace.NewProvider(sdktrace.WithSyncer(exp), sdktrace.WithConfig(sdktrace.Config{DefaultSampler: sdktrace.AlwaysSample()}))
|
||||
global.SetTraceProvider(tp)
|
||||
|
||||
tr := tp.Tracer("httptrace/client")
|
||||
ctx, span := tr.Start(context.Background(), "test")
|
||||
defer span.End()
|
||||
|
||||
ct := httptrace.NewClientTrace(ctx)
|
||||
sr := MultiSpanRecorder{}
|
||||
global.SetTraceProvider(
|
||||
testtrace.NewProvider(testtrace.WithSpanRecorder(&sr)),
|
||||
)
|
||||
|
||||
ct := httptrace.NewClientTrace(context.Background())
|
||||
ct.DNSDone(nhtrace.DNSDoneInfo{})
|
||||
ct.DNSStart(nhtrace.DNSStartInfo{Host: "example.com"})
|
||||
|
||||
getSpan := func(name string) *export.SpanData {
|
||||
spans, ok := exp.spanMap[name]
|
||||
if !ok {
|
||||
t.Fatalf("no spans found with the name %s, %v", name, exp.spanMap)
|
||||
}
|
||||
|
||||
if len(spans) != 1 {
|
||||
t.Fatalf("Expected exactly one span for %s but found %d", name, len(spans))
|
||||
}
|
||||
|
||||
return spans[0]
|
||||
}
|
||||
|
||||
getSpan("http.dns")
|
||||
|
||||
name := "http.dns"
|
||||
require.Contains(t, sr, name)
|
||||
spans := sr[name]
|
||||
require.Len(t, spans, 1)
|
||||
}
|
||||
|
@ -24,23 +24,15 @@ import (
|
||||
"github.com/google/go-cmp/cmp"
|
||||
|
||||
"go.opentelemetry.io/otel/api/correlation"
|
||||
"go.opentelemetry.io/otel/api/global"
|
||||
"go.opentelemetry.io/otel/api/kv"
|
||||
"go.opentelemetry.io/otel/api/propagation"
|
||||
"go.opentelemetry.io/otel/api/standard"
|
||||
"go.opentelemetry.io/otel/api/trace/testtrace"
|
||||
"go.opentelemetry.io/otel/instrumentation/httptrace"
|
||||
export "go.opentelemetry.io/otel/sdk/export/trace"
|
||||
sdktrace "go.opentelemetry.io/otel/sdk/trace"
|
||||
)
|
||||
|
||||
func TestRoundtrip(t *testing.T) {
|
||||
exp := &testExporter{
|
||||
spanMap: make(map[string][]*export.SpanData),
|
||||
}
|
||||
tp, _ := sdktrace.NewProvider(sdktrace.WithSyncer(exp), sdktrace.WithConfig(sdktrace.Config{DefaultSampler: sdktrace.AlwaysSample()}))
|
||||
global.SetTraceProvider(tp)
|
||||
|
||||
tr := tp.Tracer("httptrace/client")
|
||||
tr := testtrace.NewProvider().Tracer("httptrace/client")
|
||||
|
||||
var expectedAttrs map[kv.Key]string
|
||||
expectedCorrs := map[kv.Key]string{kv.Key("foo"): "bar"}
|
||||
@ -121,13 +113,7 @@ func TestRoundtrip(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestSpecifyPropagators(t *testing.T) {
|
||||
exp := &testExporter{
|
||||
spanMap: make(map[string][]*export.SpanData),
|
||||
}
|
||||
tp, _ := sdktrace.NewProvider(sdktrace.WithSyncer(exp), sdktrace.WithConfig(sdktrace.Config{DefaultSampler: sdktrace.AlwaysSample()}))
|
||||
global.SetTraceProvider(tp)
|
||||
|
||||
tr := tp.Tracer("httptrace/client")
|
||||
tr := testtrace.NewProvider().Tracer("httptrace/client")
|
||||
|
||||
expectedCorrs := map[kv.Key]string{kv.Key("foo"): "bar"}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user