Skip to content

Commit 6f1bdde

Browse files
author
aereal
authored
Merge pull request #84 from aereal/propagate-errors
faet: Propagate errors
2 parents 0abc1b9 + 63a2213 commit 6f1bdde

File tree

4 files changed

+89
-9
lines changed

4 files changed

+89
-9
lines changed

test/resolvers/main.resolvers.go

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

test/resolvers/resolver.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,9 @@ package resolvers
55
// It serves as dependency injection for your app, add any dependencies you require here.
66

77
type Resolver struct{}
8+
9+
type ForbiddenError struct{}
10+
11+
func (ForbiddenError) Error() string {
12+
return "forbidden"
13+
}

test/tracer_test.go

Lines changed: 61 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ func TestTracer(t *testing.T) {
8686
attribute.Int("graphql.operation.complexity.calculated", 3),
8787
},
8888
},
89+
{Name: "http_handler", SpanKind: trace.SpanKindInternal},
8990
},
9091
},
9192
{
@@ -136,6 +137,7 @@ func TestTracer(t *testing.T) {
136137
attribute.Int("graphql.operation.complexity.calculated", 3),
137138
},
138139
},
140+
{Name: "http_handler", SpanKind: trace.SpanKindInternal},
139141
},
140142
},
141143
{
@@ -187,6 +189,7 @@ func TestTracer(t *testing.T) {
187189
attribute.Int("graphql.operation.complexity.calculated", 3),
188190
},
189191
},
192+
{Name: "http_handler", SpanKind: trace.SpanKindInternal},
190193
},
191194
},
192195
{
@@ -241,6 +244,7 @@ func TestTracer(t *testing.T) {
241244
attribute.Int("graphql.operation.complexity.calculated", 3),
242245
},
243246
},
247+
{Name: "http_handler", SpanKind: trace.SpanKindInternal},
244248
},
245249
},
246250
{
@@ -281,8 +285,24 @@ func TestTracer(t *testing.T) {
281285
Name: semconv.ExceptionEventName,
282286
Attributes: []attribute.KeyValue{
283287
attribute.String("graphql.errors.path", "user"),
284-
semconv.ExceptionTypeKey.String("*gqlerror.Error"),
285-
semconv.ExceptionMessageKey.String("input: user forbidden"),
288+
semconv.ExceptionTypeKey.String("github.com/aereal/otelgqlgen/test/resolvers.ForbiddenError"),
289+
semconv.ExceptionMessageKey.String("forbidden"),
290+
attrStacktrace,
291+
},
292+
},
293+
},
294+
},
295+
{
296+
Name: "http_handler",
297+
SpanKind: trace.SpanKindInternal,
298+
Status: sdktrace.Status{Code: codes.Error, Description: "input: user forbidden\n"},
299+
Events: []sdktrace.Event{
300+
{
301+
Name: semconv.ExceptionEventName,
302+
Attributes: []attribute.KeyValue{
303+
attribute.String("graphql.errors.path", "user"),
304+
semconv.ExceptionTypeKey.String("github.com/aereal/otelgqlgen/test/resolvers.ForbiddenError"),
305+
semconv.ExceptionMessageKey.String("forbidden"),
286306
attrStacktrace,
287307
},
288308
},
@@ -352,17 +372,42 @@ func TestTracer(t *testing.T) {
352372
Name: semconv.ExceptionEventName,
353373
Attributes: []attribute.KeyValue{
354374
attribute.String("graphql.errors.path", "user.name"),
355-
semconv.ExceptionTypeKey.String("*gqlerror.Error"),
356-
semconv.ExceptionMessageKey.String("input: user.name invalid name"),
375+
semconv.ExceptionTypeKey.String("*errors.errorString"),
376+
semconv.ExceptionMessageKey.String("invalid name"),
377+
attrStacktrace,
378+
},
379+
},
380+
{
381+
Name: semconv.ExceptionEventName,
382+
Attributes: []attribute.KeyValue{
383+
attribute.String("graphql.errors.path", "user.age"),
384+
semconv.ExceptionTypeKey.String("*errors.errorString"),
385+
semconv.ExceptionMessageKey.String("invalid age"),
386+
attrStacktrace,
387+
},
388+
},
389+
},
390+
},
391+
{
392+
Name: "http_handler",
393+
SpanKind: trace.SpanKindInternal,
394+
Status: sdktrace.Status{Code: codes.Error, Description: "input: user.name invalid name\ninput: user.age invalid age\n"},
395+
Events: []sdktrace.Event{
396+
{
397+
Name: semconv.ExceptionEventName,
398+
Attributes: []attribute.KeyValue{
399+
attribute.String("graphql.errors.path", "user.name"),
400+
semconv.ExceptionTypeKey.String("*errors.errorString"),
401+
semconv.ExceptionMessageKey.String("invalid name"),
357402
attrStacktrace,
358403
},
359404
},
360405
{
361406
Name: semconv.ExceptionEventName,
362407
Attributes: []attribute.KeyValue{
363408
attribute.String("graphql.errors.path", "user.age"),
364-
semconv.ExceptionTypeKey.String("*gqlerror.Error"),
365-
semconv.ExceptionMessageKey.String("input: user.age invalid age"),
409+
semconv.ExceptionTypeKey.String("*errors.errorString"),
410+
semconv.ExceptionMessageKey.String("invalid age"),
366411
attrStacktrace,
367412
},
368413
},
@@ -404,6 +449,7 @@ func TestTracer(t *testing.T) {
404449
attribute.Int("graphql.operation.complexity.calculated", 1),
405450
},
406451
},
452+
{Name: "http_handler", SpanKind: trace.SpanKindInternal},
407453
},
408454
},
409455
{
@@ -439,6 +485,7 @@ func TestTracer(t *testing.T) {
439485
attribute.Int("graphql.operation.complexity.calculated", 1),
440486
},
441487
},
488+
{Name: "http_handler", SpanKind: trace.SpanKindInternal},
442489
},
443490
},
444491
{
@@ -501,6 +548,7 @@ func TestTracer(t *testing.T) {
501548
attribute.Int("graphql.operation.complexity.calculated", 3),
502549
},
503550
},
551+
{Name: "http_handler", SpanKind: trace.SpanKindInternal},
504552
},
505553
},
506554
{
@@ -537,6 +585,7 @@ func TestTracer(t *testing.T) {
537585
attribute.Int("graphql.operation.complexity.calculated", 1),
538586
},
539587
},
588+
{Name: "http_handler", SpanKind: trace.SpanKindInternal},
540589
},
541590
},
542591
}
@@ -556,7 +605,12 @@ func TestTracer(t *testing.T) {
556605
options = append(options, otelgqlgen.WithTracerProvider(tp))
557606
gqlsrv.Use(otelgqlgen.New(options...))
558607
gqlsrv.Use(extension.FixedComplexityLimit(1000))
559-
srv := httptest.NewServer(gqlsrv)
608+
testTracer := tp.Tracer("test")
609+
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
610+
ctx, span := testTracer.Start(r.Context(), "http_handler")
611+
defer span.End()
612+
gqlsrv.ServeHTTP(w, r.WithContext(ctx))
613+
}))
560614
defer srv.Close()
561615
body, err := json.Marshal(tc.params)
562616
if err != nil {

tracer.go

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@ func (t Tracer) startResponseSpan(ctx context.Context) (context.Context, trace.S
115115
}
116116

117117
func (t Tracer) InterceptResponse(ctx context.Context, next graphql.ResponseHandler) *graphql.Response {
118+
parentSpan := trace.SpanFromContext(ctx)
118119
ctx, span := t.startResponseSpan(ctx)
119120
defer span.End()
120121
if !span.IsRecording() {
@@ -143,6 +144,9 @@ func (t Tracer) InterceptResponse(ctx context.Context, next graphql.ResponseHand
143144
resp := next(ctx)
144145
if resp != nil && len(resp.Errors) > 0 {
145146
recordGQLErrors(span, resp.Errors)
147+
if parentSpan.SpanContext().IsValid() {
148+
recordGQLErrors(parentSpan, resp.Errors)
149+
}
146150
}
147151
return resp
148152
}
@@ -225,7 +229,23 @@ func recordGQLErrors(span trace.Span, errs gqlerror.List) {
225229
attrs := []attribute.KeyValue{
226230
keyErrorPath.String(e.Path.String()),
227231
}
228-
span.RecordError(e, trace.WithStackTrace(true), trace.WithAttributes(attrs...))
232+
err := unwrapErr(e)
233+
span.RecordError(err, trace.WithStackTrace(true), trace.WithAttributes(attrs...))
234+
}
235+
}
236+
237+
func unwrapErr(err error) error {
238+
var underlying error = err
239+
for {
240+
wrapped, ok := underlying.(interface{ Unwrap() error })
241+
if !ok {
242+
return underlying
243+
}
244+
unwrapped := wrapped.Unwrap()
245+
if unwrapped == nil {
246+
return underlying
247+
}
248+
underlying = unwrapped
229249
}
230250
}
231251

0 commit comments

Comments
 (0)