@@ -9,118 +9,15 @@ import (
9
9
"github.com/stretchr/testify/require"
10
10
"go.opentelemetry.io/otel/attribute"
11
11
"go.opentelemetry.io/otel/codes"
12
+ sdktrace "go.opentelemetry.io/otel/sdk/trace"
12
13
"go.opentelemetry.io/otel/sdk/trace/tracetest"
14
+ semconv "go.opentelemetry.io/otel/semconv/v1.25.0"
13
15
"go.opentelemetry.io/otel/trace"
14
- "go.opentelemetry.io/otel/trace/embedded"
15
16
16
17
"github.com/grafana/grafana-plugin-sdk-go/backend/httpclient"
17
18
"github.com/grafana/grafana-plugin-sdk-go/internal/tracerprovider"
18
19
)
19
20
20
- type mockTracerProvider struct {
21
- embedded.TracerProvider
22
- }
23
-
24
- var _ trace.TracerProvider = mockTracerProvider {}
25
-
26
- func (p mockTracerProvider ) Tracer (string , ... trace.TracerOption ) trace.Tracer {
27
- return & mockTracer {}
28
- }
29
-
30
- type mockTracer struct {
31
- embedded.Tracer
32
-
33
- spans []* mockSpan
34
- }
35
-
36
- var _ trace.Tracer = & mockTracer {}
37
-
38
- func (t * mockTracer ) Start (ctx context.Context , name string , opts ... trace.SpanStartOption ) (context.Context , trace.Span ) {
39
- config := trace .NewSpanStartConfig (opts ... )
40
- span := & mockSpan {}
41
- span .SetName (name )
42
- span .SetAttributes (config .Attributes ()... )
43
- t .spans = append (t .spans , span )
44
- return trace .ContextWithSpan (ctx , span ), span
45
- }
46
-
47
- // mockSpan is an implementation of Span that preforms no operations.
48
- type mockSpan struct {
49
- embedded.Span
50
-
51
- name string
52
- ended bool
53
-
54
- errored bool
55
- errs []error
56
-
57
- statusCode codes.Code
58
- statusMessage string
59
-
60
- attributes []attribute.KeyValue
61
- events []string
62
- }
63
-
64
- var _ trace.Span = & mockSpan {}
65
-
66
- // checkValid panics if s has already ended, otherwise it does nothing.
67
- // This ensures that ended spans are never edited afterwards.
68
- func (s * mockSpan ) checkValid () {
69
- if s .ended {
70
- panic ("span already ended" )
71
- }
72
- }
73
-
74
- func (s * mockSpan ) attributesMap () map [attribute.Key ]attribute.Value {
75
- m := make (map [attribute.Key ]attribute.Value , len (s .attributes ))
76
- for _ , attr := range s .attributes {
77
- m [attr .Key ] = attr .Value
78
- }
79
- return m
80
- }
81
-
82
- func (* mockSpan ) SpanContext () trace.SpanContext { return trace.SpanContext {} }
83
-
84
- func (* mockSpan ) IsRecording () bool { return false }
85
-
86
- func (s * mockSpan ) SetStatus (code codes.Code , message string ) {
87
- s .checkValid ()
88
- s .statusCode = code
89
- s .statusMessage = message
90
- }
91
-
92
- func (s * mockSpan ) SetError (errored bool ) {
93
- s .checkValid ()
94
- s .errored = errored
95
- }
96
-
97
- func (s * mockSpan ) SetAttributes (kv ... attribute.KeyValue ) {
98
- s .checkValid ()
99
- s .attributes = append (s .attributes , kv ... )
100
- }
101
-
102
- func (s * mockSpan ) End (... trace.SpanEndOption ) {
103
- s .checkValid ()
104
- s .ended = true
105
- }
106
-
107
- func (s * mockSpan ) RecordError (err error , _ ... trace.EventOption ) {
108
- s .checkValid ()
109
- s .errs = append (s .errs , err )
110
- }
111
-
112
- func (s * mockSpan ) AddEvent (event string , _ ... trace.EventOption ) {
113
- s .checkValid ()
114
- s .events = append (s .events , event )
115
- }
116
-
117
- func (s * mockSpan ) SetName (name string ) {
118
- s .checkValid ()
119
- s .name = name
120
- }
121
-
122
- func (* mockSpan ) TracerProvider () trace.TracerProvider { return mockTracerProvider {} }
123
-
124
21
func TestTracingMiddlewareWithDefaultTracerDataRace (t * testing.T ) {
125
22
var tracer trace.Tracer
126
23
@@ -141,7 +38,9 @@ func TestTracingMiddlewareWithDefaultTracerDataRace(t *testing.T) {
141
38
142
39
func TestTracingMiddleware (t * testing.T ) {
143
40
t .Run ("GET request that returns 200 OK should start and capture span" , func (t * testing.T ) {
144
- tracer := & mockTracer {}
41
+ spanRecorder := tracetest .NewSpanRecorder ()
42
+ provider := sdktrace .NewTracerProvider (sdktrace .WithSpanProcessor (spanRecorder ))
43
+ tracer := provider .Tracer ("test" )
145
44
146
45
finalRoundTripper := httpclient .RoundTripperFunc (func (req * http.Request ) (* http.Response , error ) {
147
46
return & http.Response {StatusCode : http .StatusOK , Request : req }, nil
@@ -169,24 +68,28 @@ func TestTracingMiddleware(t *testing.T) {
169
68
require .NoError (t , res .Body .Close ())
170
69
}
171
70
172
- require .Len (t , tracer .spans , 1 )
173
- span := tracer .spans [0 ]
174
- require .Equal (t , "HTTP Outgoing Request" , span .name )
175
- require .True (t , span .ended )
176
- require .False (t , span .errored )
177
- require .Equal (t , codes .Unset , span .statusCode )
178
- require .Empty (t , span .statusMessage )
179
- require .Equal (t , map [attribute.Key ]attribute.Value {
180
- "l1" : attribute .StringValue ("v1" ),
181
- "l2" : attribute .StringValue ("v2" ),
182
- "http.url" : attribute .StringValue ("http://test.com/query" ),
183
- "http.method" : attribute .StringValue ("GET" ),
184
- "http.status_code" : attribute .Int64Value (200 ),
185
- }, span .attributesMap ())
71
+ spans := spanRecorder .Ended ()
72
+
73
+ require .Len (t , spans , 1 )
74
+ span := spans [0 ]
75
+ require .Equal (t , "HTTP Outgoing Request" , span .Name ())
76
+ require .False (t , span .EndTime ().IsZero ())
77
+ require .False (t , span .Status ().Code == codes .Error )
78
+ require .Equal (t , codes .Unset , span .Status ().Code )
79
+ require .Empty (t , span .Status ().Description )
80
+ require .ElementsMatch (t , []attribute.KeyValue {
81
+ attribute .String ("l1" , "v1" ),
82
+ attribute .String ("l2" , "v2" ),
83
+ semconv .HTTPURL ("http://test.com/query" ),
84
+ semconv .HTTPMethod (http .MethodGet ),
85
+ semconv .HTTPStatusCode (http .StatusOK ),
86
+ }, span .Attributes ())
186
87
})
187
88
188
89
t .Run ("GET request that returns 400 Bad Request should start and capture span" , func (t * testing.T ) {
189
- tracer := & mockTracer {}
90
+ spanRecorder := tracetest .NewSpanRecorder ()
91
+ provider := sdktrace .NewTracerProvider (sdktrace .WithSpanProcessor (spanRecorder ))
92
+ tracer := provider .Tracer ("test" )
190
93
191
94
finalRoundTripper := httpclient .RoundTripperFunc (func (req * http.Request ) (* http.Response , error ) {
192
95
return & http.Response {StatusCode : http .StatusBadRequest , Request : req }, nil
@@ -214,27 +117,31 @@ func TestTracingMiddleware(t *testing.T) {
214
117
require .NoError (t , res .Body .Close ())
215
118
}
216
119
217
- require .Len (t , tracer .spans , 1 )
218
- span := tracer .spans [0 ]
219
- require .Equal (t , "HTTP Outgoing Request" , span .name )
220
- require .True (t , span .ended )
221
- require .False (t , span .errored )
222
- require .Equal (t , codes .Error , span .statusCode )
223
- require .Equal (t , "error with HTTP status code 400" , span .statusMessage )
224
- require .Equal (t , map [attribute.Key ]attribute.Value {
225
- "l1" : attribute .StringValue ("v1" ),
226
- "l2" : attribute .StringValue ("v2" ),
227
- "http.url" : attribute .StringValue ("http://test.com/query" ),
228
- "http.method" : attribute .StringValue ("GET" ),
229
- "http.status_code" : attribute .Int64Value (400 ),
230
- }, span .attributesMap ())
120
+ spans := spanRecorder .Ended ()
121
+
122
+ require .Len (t , spans , 1 )
123
+ span := spans [0 ]
124
+ require .Equal (t , "HTTP Outgoing Request" , span .Name ())
125
+ require .False (t , span .EndTime ().IsZero ())
126
+ require .Equal (t , codes .Error , span .Status ().Code )
127
+ require .Equal (t , "error with HTTP status code 400" , span .Status ().Description )
128
+ require .Equal (t , []attribute.KeyValue {
129
+ attribute .String ("l1" , "v1" ),
130
+ attribute .String ("l2" , "v2" ),
131
+ semconv .HTTPURL ("http://test.com/query" ),
132
+ semconv .HTTPMethod (http .MethodGet ),
133
+ semconv .HTTPStatusCode (http .StatusBadRequest ),
134
+ }, span .Attributes ())
231
135
})
232
136
233
137
t .Run ("POST request that returns 200 OK should start and capture span" , func (t * testing.T ) {
234
- tracer := & mockTracer {}
138
+ spanRecorder := tracetest .NewSpanRecorder ()
139
+ provider := sdktrace .NewTracerProvider (sdktrace .WithSpanProcessor (spanRecorder ))
140
+ tracer := provider .Tracer ("test" )
235
141
142
+ resContentLength := int64 (10 )
236
143
finalRoundTripper := httpclient .RoundTripperFunc (func (req * http.Request ) (* http.Response , error ) {
237
- return & http.Response {StatusCode : http .StatusOK , Request : req , ContentLength : 10 }, nil
144
+ return & http.Response {StatusCode : http .StatusOK , Request : req , ContentLength : resContentLength }, nil
238
145
})
239
146
240
147
mw := httpclient .TracingMiddleware (tracer )
@@ -259,24 +166,22 @@ func TestTracingMiddleware(t *testing.T) {
259
166
require .NoError (t , res .Body .Close ())
260
167
}
261
168
262
- require .Len (t , tracer .spans , 1 )
263
- span := tracer .spans [0 ]
264
- require .Equal (t , "HTTP Outgoing Request" , span .name )
265
- require .True (t , span .ended )
266
- require .False (t , span .errored )
267
- require .Equal (t , codes .Unset , span .statusCode )
268
- require .Empty (t , span .statusMessage )
269
- attrMap := span .attributesMap ()
270
- _ , ok = attrMap ["http.content_length" ]
271
- require .True (t , ok , "http.content_length does not exist" )
272
- delete (attrMap , "http.content_length" )
273
- require .Equal (t , map [attribute.Key ]attribute.Value {
274
- "l1" : attribute .StringValue ("v1" ),
275
- "l2" : attribute .StringValue ("v2" ),
276
- "http.url" : attribute .StringValue ("http://test.com/query" ),
277
- "http.method" : attribute .StringValue ("POST" ),
278
- "http.status_code" : attribute .Int64Value (200 ),
279
- }, attrMap )
169
+ spans := spanRecorder .Ended ()
170
+
171
+ require .Len (t , spans , 1 )
172
+ span := spans [0 ]
173
+ require .Equal (t , "HTTP Outgoing Request" , span .Name ())
174
+ require .False (t , span .EndTime ().IsZero ())
175
+ require .Equal (t , codes .Unset , span .Status ().Code )
176
+ require .Empty (t , span .Status ().Description )
177
+ require .ElementsMatch (t , []attribute.KeyValue {
178
+ attribute .String ("l1" , "v1" ),
179
+ attribute .String ("l2" , "v2" ),
180
+ semconv .HTTPURL ("http://test.com/query" ),
181
+ semconv .HTTPMethod (http .MethodPost ),
182
+ semconv .HTTPStatusCode (http .StatusOK ),
183
+ attribute .Int64 ("http.content_length" , resContentLength ),
184
+ }, span .Attributes ())
280
185
})
281
186
282
187
t .Run ("propagation" , func (t * testing.T ) {
0 commit comments