Skip to content

Commit 1a55b96

Browse files
committed
Add context to validation tracing
Context is needed for tracing to access the current span, in order to add tags to it, or create child spans. As presently defined (without a context), this cannot be done: new spans could be created, but they would not be associated with the existing request trace. OpenTracingTracer implements the new interface (it never implemented the old one). Via this 'extension interface', the tracer configured (or the default tracer) will be used as the validation tracer if: * The tracer implements the (optional) interface; and * A validation tracer isn't provided using the deprecated option What this means is that the deprecated option is _preferred_ as an override. This allows users to migrate in a non-breaking, non-behaviour changing way, until such time as they intentionally remove the use of the deprecated option. For those who are currently using the default tracer, and not supplying a validation tracer, validation will be traced immediately with no change required to configuration options.
1 parent beb923f commit 1a55b96

File tree

4 files changed

+49
-9
lines changed

4 files changed

+49
-9
lines changed

graphql.go

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -25,16 +25,23 @@ import (
2525
// resolver, then the schema can not be executed, but it may be inspected (e.g. with ToJSON).
2626
func ParseSchema(schemaString string, resolver interface{}, opts ...SchemaOpt) (*Schema, error) {
2727
s := &Schema{
28-
schema: schema.New(),
29-
maxParallelism: 10,
30-
tracer: trace.OpenTracingTracer{},
31-
validationTracer: trace.NoopValidationTracer{},
32-
logger: &log.DefaultLogger{},
28+
schema: schema.New(),
29+
maxParallelism: 10,
30+
tracer: trace.OpenTracingTracer{},
31+
logger: &log.DefaultLogger{},
3332
}
3433
for _, opt := range opts {
3534
opt(s)
3635
}
3736

37+
if s.validationTracer == nil {
38+
if tracer, ok := s.tracer.(trace.ValidationTracerContext); ok {
39+
s.validationTracer = tracer
40+
} else {
41+
s.validationTracer = &validationBridgingTracer{tracer: trace.NoopValidationTracer{}}
42+
}
43+
}
44+
3845
if err := s.schema.Parse(schemaString, s.useStringDescriptions); err != nil {
3946
return nil, err
4047
}
@@ -68,7 +75,7 @@ type Schema struct {
6875
maxDepth int
6976
maxParallelism int
7077
tracer trace.Tracer
71-
validationTracer trace.ValidationTracer
78+
validationTracer trace.ValidationTracerContext
7279
logger log.Logger
7380
useStringDescriptions bool
7481
disableIntrospection bool
@@ -117,9 +124,10 @@ func Tracer(tracer trace.Tracer) SchemaOpt {
117124
}
118125

119126
// ValidationTracer is used to trace validation errors. It defaults to trace.NoopValidationTracer.
127+
// Deprecated: context is needed to support tracing correctly. Use a Tracer which implements trace.ValidationTracerContext.
120128
func ValidationTracer(tracer trace.ValidationTracer) SchemaOpt {
121129
return func(s *Schema) {
122-
s.validationTracer = tracer
130+
s.validationTracer = &validationBridgingTracer{tracer: tracer}
123131
}
124132
}
125133

@@ -186,7 +194,7 @@ func (s *Schema) exec(ctx context.Context, queryString string, operationName str
186194
return &Response{Errors: []*errors.QueryError{qErr}}
187195
}
188196

189-
validationFinish := s.validationTracer.TraceValidation()
197+
validationFinish := s.validationTracer.TraceValidation(ctx)
190198
errs := validation.Validate(s.schema, doc, variables, s.maxDepth)
191199
validationFinish(errs)
192200
if len(errs) != 0 {
@@ -272,6 +280,14 @@ func (s *Schema) validateSchema() error {
272280
return nil
273281
}
274282

283+
type validationBridgingTracer struct {
284+
tracer trace.ValidationTracer
285+
}
286+
287+
func (t *validationBridgingTracer) TraceValidation(context.Context) trace.TraceValidationFinishFunc {
288+
return t.tracer.TraceValidation()
289+
}
290+
275291
func validateRootOp(s *schema.Schema, name string, mandatory bool) error {
276292
t, ok := s.EntryPoints[name]
277293
if !ok {

subscriptions.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ func (s *Schema) subscribe(ctx context.Context, queryString string, operationNam
3636
return sendAndReturnClosed(&Response{Errors: []*qerrors.QueryError{qErr}})
3737
}
3838

39-
validationFinish := s.validationTracer.TraceValidation()
39+
validationFinish := s.validationTracer.TraceValidation(ctx)
4040
errs := validation.Validate(s.schema, doc, variables, s.maxDepth)
4141
validationFinish(errs)
4242
if len(errs) != 0 {

trace/trace.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,22 @@ func (OpenTracingTracer) TraceField(ctx context.Context, label, typeName, fieldN
6767
}
6868
}
6969

70+
func (OpenTracingTracer) TraceValidation(ctx context.Context) TraceValidationFinishFunc {
71+
span, _ := opentracing.StartSpanFromContext(ctx, "Validate Query")
72+
73+
return func(errs []*errors.QueryError) {
74+
if len(errs) > 0 {
75+
msg := errs[0].Error()
76+
if len(errs) > 1 {
77+
msg += fmt.Sprintf(" (and %d more errors)", len(errs)-1)
78+
}
79+
ext.Error.Set(span, true)
80+
span.SetTag("graphql.error", msg)
81+
}
82+
span.Finish()
83+
}
84+
}
85+
7086
func noop(*errors.QueryError) {}
7187

7288
type NoopTracer struct{}

trace/validation_trace.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,25 @@
11
package trace
22

33
import (
4+
"context"
5+
46
"github.com/graph-gophers/graphql-go/errors"
57
)
68

79
type TraceValidationFinishFunc = TraceQueryFinishFunc
810

11+
// Deprecated: use ValidationTracerContext.
912
type ValidationTracer interface {
1013
TraceValidation() TraceValidationFinishFunc
1114
}
1215

16+
type ValidationTracerContext interface {
17+
TraceValidation(ctx context.Context) TraceValidationFinishFunc
18+
}
19+
1320
type NoopValidationTracer struct{}
1421

22+
// Deprecated: use a Tracer which implements ValidationTracerContext.
1523
func (NoopValidationTracer) TraceValidation() TraceValidationFinishFunc {
1624
return func(errs []*errors.QueryError) {}
1725
}

0 commit comments

Comments
 (0)