Skip to content

Commit 24abfa5

Browse files
authored
Refactor trace package (#513)
Remove dependency for graphql-go on OpenTracing and OpenTelemetry except where those tracers are explicitly configured for use.
1 parent 7c39d63 commit 24abfa5

16 files changed

+369
-166
lines changed

README.md

+44-3
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ safe for production use.
1111

1212
- minimal API
1313
- support for `context.Context`
14-
- support for the `OpenTracing` standard
14+
- support for the `OpenTelemetry` and `OpenTracing` standards
1515
- schema type-checking against resolvers
1616
- resolvers are matched to the schema based on method sets (can resolve a GraphQL schema with a Go interface or Go struct).
1717
- handles panics in resolvers
@@ -108,8 +108,7 @@ func (r *helloWorldResolver) Hello(ctx context.Context) (string, error) {
108108
- `UseFieldResolvers()` specifies whether to use struct field resolvers.
109109
- `MaxDepth(n int)` specifies the maximum field nesting depth in a query. The default is 0 which disables max depth checking.
110110
- `MaxParallelism(n int)` specifies the maximum number of resolvers per request allowed to run in parallel. The default is 10.
111-
- `Tracer(tracer trace.Tracer)` is used to trace queries and fields. It defaults to `trace.OpenTracingTracer`.
112-
- `ValidationTracer(tracer trace.ValidationTracer)` is used to trace validation errors. It defaults to `trace.NoopValidationTracer`.
111+
- `Tracer(tracer trace.Tracer)` is used to trace queries and fields. It defaults to `noop.Tracer`.
113112
- `Logger(logger log.Logger)` is used to log panics during query execution. It defaults to `exec.DefaultLogger`.
114113
- `PanicHandler(panicHandler errors.PanicHandler)` is used to transform panics into errors during query execution. It defaults to `errors.DefaultPanicHandler`.
115114
- `DisableIntrospection()` disables introspection queries.
@@ -165,6 +164,48 @@ Which could produce a GraphQL error such as:
165164
}
166165
```
167166

167+
### Tracing
168+
169+
By default the library uses `noop.Tracer`. If you want to change that you can use the OpenTelemetry or the OpenTracing implementations, respectively:
170+
171+
```go
172+
// OpenTelemetry tracer
173+
package main
174+
175+
import (
176+
"github.com/graph-gophers/graphql-go"
177+
"github.com/graph-gophers/graphql-go/example/starwars"
178+
otelgraphql "github.com/graph-gophers/graphql-go/trace/otel"
179+
"github.com/graph-gophers/graphql-go/trace/tracer"
180+
)
181+
// ...
182+
_, err := graphql.ParseSchema(starwars.Schema, nil, graphql.Tracer(otelgraphql.DefaultTracer()))
183+
// ...
184+
```
185+
Alternatively you can pass an existing trace.Tracer instance:
186+
```go
187+
tr := otel.Tracer("example")
188+
_, err = graphql.ParseSchema(starwars.Schema, nil, graphql.Tracer(&otelgraphql.Tracer{Tracer: tr}))
189+
```
190+
191+
192+
```go
193+
// OpenTracing tracer
194+
package main
195+
196+
import (
197+
"github.com/graph-gophers/graphql-go"
198+
"github.com/graph-gophers/graphql-go/example/starwars"
199+
"github.com/graph-gophers/graphql-go/trace/opentracing"
200+
"github.com/graph-gophers/graphql-go/trace/tracer"
201+
)
202+
// ...
203+
_, err := graphql.ParseSchema(starwars.Schema, nil, graphql.Tracer(opentracing.Tracer{}))
204+
205+
// ...
206+
```
207+
208+
168209
### [Examples](https://github.com/graph-gophers/graphql-go/wiki/Examples)
169210

170211
### [Companies that use this library](https://github.com/graph-gophers/graphql-go/wiki/Users)

go.mod

+4-5
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
module github.com/graph-gophers/graphql-go
22

3+
go 1.13
4+
35
require (
4-
github.com/go-logr/stdr v1.2.2 // indirect
56
github.com/opentracing/opentracing-go v1.2.0
6-
go.opentelemetry.io/otel v1.3.0
7-
go.opentelemetry.io/otel/trace v1.3.0
7+
go.opentelemetry.io/otel v1.6.3
8+
go.opentelemetry.io/otel/trace v1.6.3
89
)
9-
10-
go 1.13

go.sum

+15-12
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,27 @@
1+
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
12
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
2-
github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
3-
github.com/go-logr/logr v1.2.1/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
4-
github.com/go-logr/logr v1.2.2 h1:ahHml/yUpnlb96Rp8HCvtYVPY8ZYpxq3g7UYchIYwbs=
53
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
6-
github.com/go-logr/stdr v1.2.0/go.mod h1:YkVgnZu1ZjjL7xTxrfm/LLZBfkhTqSR1ydtm6jTKKwI=
4+
github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0=
5+
github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
76
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
87
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
9-
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
10-
github.com/opentracing/opentracing-go v1.1.0 h1:pWlfV3Bxv7k65HYwkikxat0+s3pV4bsqf19k25Ur8rU=
11-
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
8+
github.com/google/go-cmp v0.5.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o=
9+
github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
1210
github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs=
1311
github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc=
12+
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
1413
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
1514
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
1615
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
17-
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
18-
go.opentelemetry.io/otel v1.3.0 h1:APxLf0eiBwLl+SOXiJJCVYzA1OOJNyAoV8C5RNRyy7Y=
19-
go.opentelemetry.io/otel v1.3.0/go.mod h1:PWIKzi6JCp7sM0k9yZ43VX+T345uNbAkDKwHVjb2PTs=
20-
go.opentelemetry.io/otel/trace v1.3.0 h1:doy8Hzb1RJ+I3yFhtDmwNc7tIyw1tNMOIsyPzp1NOGY=
21-
go.opentelemetry.io/otel/trace v1.3.0/go.mod h1:c/VDhno8888bvQYmbYLqe41/Ldmr/KKunbvWM4/fEjk=
16+
github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY=
17+
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
18+
go.opentelemetry.io/otel v1.6.3 h1:FLOfo8f9JzFVFVyU+MSRJc2HdEAXQgm7pIv2uFKRSZE=
19+
go.opentelemetry.io/otel v1.6.3/go.mod h1:7BgNga5fNlF/iZjG06hM3yofffp0ofKCDwSXx1GC4dI=
20+
go.opentelemetry.io/otel/trace v1.6.3 h1:IqN4L+5b0mPNjdXIiZ90Ni4Bl5BRkDQywePLWemd9bc=
21+
go.opentelemetry.io/otel/trace v1.6.3/go.mod h1:GNJQusJlUgZl9/TQBPKU/Y/ty+0iVB5fjhKeJGZPGFs=
22+
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
2223
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
24+
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
2325
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
26+
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
2427
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

graphql.go

+16-15
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@ import (
1616
"github.com/graph-gophers/graphql-go/internal/validation"
1717
"github.com/graph-gophers/graphql-go/introspection"
1818
"github.com/graph-gophers/graphql-go/log"
19-
"github.com/graph-gophers/graphql-go/trace"
19+
"github.com/graph-gophers/graphql-go/trace/noop"
20+
"github.com/graph-gophers/graphql-go/trace/tracer"
2021
"github.com/graph-gophers/graphql-go/types"
2122
)
2223

@@ -27,7 +28,7 @@ func ParseSchema(schemaString string, resolver interface{}, opts ...SchemaOpt) (
2728
s := &Schema{
2829
schema: schema.New(),
2930
maxParallelism: 10,
30-
tracer: trace.OpenTracingTracer{},
31+
tracer: noop.Tracer{},
3132
logger: &log.DefaultLogger{},
3233
panicHandler: &errors.DefaultPanicHandler{},
3334
}
@@ -36,10 +37,10 @@ func ParseSchema(schemaString string, resolver interface{}, opts ...SchemaOpt) (
3637
}
3738

3839
if s.validationTracer == nil {
39-
if tracer, ok := s.tracer.(trace.ValidationTracerContext); ok {
40-
s.validationTracer = tracer
40+
if t, ok := s.tracer.(tracer.ValidationTracer); ok {
41+
s.validationTracer = t
4142
} else {
42-
s.validationTracer = &validationBridgingTracer{tracer: trace.NoopValidationTracer{}}
43+
s.validationTracer = &validationBridgingTracer{tracer: tracer.LegacyNoopValidationTracer{}} //nolint:staticcheck
4344
}
4445
}
4546

@@ -75,8 +76,8 @@ type Schema struct {
7576

7677
maxDepth int
7778
maxParallelism int
78-
tracer trace.Tracer
79-
validationTracer trace.ValidationTracerContext
79+
tracer tracer.Tracer
80+
validationTracer tracer.ValidationTracer
8081
logger log.Logger
8182
panicHandler errors.PanicHandler
8283
useStringDescriptions bool
@@ -122,16 +123,16 @@ func MaxParallelism(n int) SchemaOpt {
122123
}
123124
}
124125

125-
// Tracer is used to trace queries and fields. It defaults to trace.OpenTracingTracer.
126-
func Tracer(tracer trace.Tracer) SchemaOpt {
126+
// Tracer is used to trace queries and fields. It defaults to tracer.Noop.
127+
func Tracer(t tracer.Tracer) SchemaOpt {
127128
return func(s *Schema) {
128-
s.tracer = tracer
129+
s.tracer = t
129130
}
130131
}
131132

132-
// ValidationTracer is used to trace validation errors. It defaults to trace.NoopValidationTracer.
133-
// Deprecated: context is needed to support tracing correctly. Use a Tracer which implements trace.ValidationTracerContext.
134-
func ValidationTracer(tracer trace.ValidationTracer) SchemaOpt { //nolint:staticcheck
133+
// ValidationTracer is used to trace validation errors. It defaults to tracer.LegacyNoopValidationTracer.
134+
// Deprecated: context is needed to support tracing correctly. Use a Tracer which implements tracer.ValidationTracer.
135+
func ValidationTracer(tracer tracer.LegacyValidationTracer) SchemaOpt { //nolint:staticcheck
135136
return func(s *Schema) {
136137
s.validationTracer = &validationBridgingTracer{tracer: tracer}
137138
}
@@ -296,10 +297,10 @@ func (s *Schema) validateSchema() error {
296297
}
297298

298299
type validationBridgingTracer struct {
299-
tracer trace.ValidationTracer //nolint:staticcheck
300+
tracer tracer.LegacyValidationTracer //nolint:staticcheck
300301
}
301302

302-
func (t *validationBridgingTracer) TraceValidation(context.Context) trace.TraceValidationFinishFunc {
303+
func (t *validationBridgingTracer) TraceValidation(context.Context) func([]*errors.QueryError) {
303304
return t.tracer.TraceValidation()
304305
}
305306

graphql_test.go

+12-12
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import (
1313
"github.com/graph-gophers/graphql-go/example/starwars"
1414
"github.com/graph-gophers/graphql-go/gqltesting"
1515
"github.com/graph-gophers/graphql-go/introspection"
16-
"github.com/graph-gophers/graphql-go/trace"
16+
"github.com/graph-gophers/graphql-go/trace/tracer"
1717
)
1818

1919
type helloWorldResolver1 struct{}
@@ -4053,7 +4053,7 @@ type queryTrace struct {
40534053
errors []*gqlerrors.QueryError
40544054
}
40554055

4056-
func (t *testTracer) TraceField(ctx context.Context, label, typeName, fieldName string, trivial bool, args map[string]interface{}) (context.Context, trace.TraceFieldFinishFunc) {
4056+
func (t *testTracer) TraceField(ctx context.Context, label, typeName, fieldName string, trivial bool, args map[string]interface{}) (context.Context, func(*gqlerrors.QueryError)) {
40574057
return ctx, func(qe *gqlerrors.QueryError) {
40584058
t.mu.Lock()
40594059
defer t.mu.Unlock()
@@ -4071,7 +4071,7 @@ func (t *testTracer) TraceField(ctx context.Context, label, typeName, fieldName
40714071
}
40724072
}
40734073

4074-
func (t *testTracer) TraceQuery(ctx context.Context, document string, opName string, vars map[string]interface{}, varTypes map[string]*introspection.Type) (context.Context, trace.TraceQueryFinishFunc) {
4074+
func (t *testTracer) TraceQuery(ctx context.Context, document string, opName string, vars map[string]interface{}, varTypes map[string]*introspection.Type) (context.Context, func([]*gqlerrors.QueryError)) {
40754075
return ctx, func(qe []*gqlerrors.QueryError) {
40764076
t.mu.Lock()
40774077
defer t.mu.Unlock()
@@ -4088,14 +4088,14 @@ func (t *testTracer) TraceQuery(ctx context.Context, document string, opName str
40884088
}
40894089
}
40904090

4091-
var _ trace.Tracer = (*testTracer)(nil)
4091+
var _ tracer.Tracer = (*testTracer)(nil)
40924092

40934093
func TestTracer(t *testing.T) {
40944094
t.Parallel()
40954095

4096-
tracer := &testTracer{mu: &sync.Mutex{}}
4096+
tt := &testTracer{mu: &sync.Mutex{}}
40974097

4098-
schema, err := graphql.ParseSchema(starwars.Schema, &starwars.Resolver{}, graphql.Tracer(tracer))
4098+
schema, err := graphql.ParseSchema(starwars.Schema, &starwars.Resolver{}, graphql.Tracer(tt))
40994099
if err != nil {
41004100
t.Fatalf("graphql.ParseSchema: %s", err)
41014101
}
@@ -4116,14 +4116,14 @@ func TestTracer(t *testing.T) {
41164116

41174117
_ = schema.Exec(ctx, doc, opName, variables)
41184118

4119-
tracer.mu.Lock()
4120-
defer tracer.mu.Unlock()
4119+
tt.mu.Lock()
4120+
defer tt.mu.Unlock()
41214121

4122-
if len(tracer.queries) != 1 {
4123-
t.Fatalf("expected one query trace, but got %d: %#v", len(tracer.queries), tracer.queries)
4122+
if len(tt.queries) != 1 {
4123+
t.Fatalf("expected one query trace, but got %d: %#v", len(tt.queries), tt.queries)
41244124
}
41254125

4126-
qt := tracer.queries[0]
4126+
qt := tt.queries[0]
41274127
if qt.document != doc {
41284128
t.Errorf("mismatched query trace document:\nwant: %q\ngot : %q", doc, qt.document)
41294129
}
@@ -4137,7 +4137,7 @@ func TestTracer(t *testing.T) {
41374137
{fieldName: "name", typeName: "Human"},
41384138
}
41394139

4140-
checkFieldTraces(t, expectedFieldTraces, tracer.fields)
4140+
checkFieldTraces(t, expectedFieldTraces, tt.fields)
41414141
}
41424142

41434143
func checkFieldTraces(t *testing.T, want, have []fieldTrace) {

internal/exec/exec.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,14 @@ import (
1414
"github.com/graph-gophers/graphql-go/internal/exec/selected"
1515
"github.com/graph-gophers/graphql-go/internal/query"
1616
"github.com/graph-gophers/graphql-go/log"
17-
"github.com/graph-gophers/graphql-go/trace"
17+
"github.com/graph-gophers/graphql-go/trace/tracer"
1818
"github.com/graph-gophers/graphql-go/types"
1919
)
2020

2121
type Request struct {
2222
selected.Request
2323
Limiter chan struct{}
24-
Tracer trace.Tracer
24+
Tracer tracer.Tracer
2525
Logger log.Logger
2626
PanicHandler errors.PanicHandler
2727
SubscribeResolverTimeout time.Duration

trace/noop/trace.go

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// Package noop defines a no-op tracer implementation.
2+
package noop
3+
4+
import (
5+
"context"
6+
7+
"github.com/graph-gophers/graphql-go/errors"
8+
"github.com/graph-gophers/graphql-go/introspection"
9+
)
10+
11+
// Tracer is a no-op tracer that does nothing.
12+
type Tracer struct{}
13+
14+
func (Tracer) TraceQuery(ctx context.Context, queryString string, operationName string, variables map[string]interface{}, varTypes map[string]*introspection.Type) (context.Context, func([]*errors.QueryError)) {
15+
return ctx, func(errs []*errors.QueryError) {}
16+
}
17+
18+
func (Tracer) TraceField(ctx context.Context, label, typeName, fieldName string, trivial bool, args map[string]interface{}) (context.Context, func(*errors.QueryError)) {
19+
return ctx, func(err *errors.QueryError) {}
20+
}
21+
22+
func (Tracer) TraceValidation(context.Context) func([]*errors.QueryError) {
23+
return func(errs []*errors.QueryError) {}
24+
}

trace/noop/trace_test.go

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package noop_test
2+
3+
import (
4+
"testing"
5+
6+
"github.com/graph-gophers/graphql-go"
7+
"github.com/graph-gophers/graphql-go/example/starwars"
8+
"github.com/graph-gophers/graphql-go/trace/noop"
9+
"github.com/graph-gophers/graphql-go/trace/tracer"
10+
)
11+
12+
func TestInterfaceImplementation(t *testing.T) {
13+
var _ tracer.ValidationTracer = &noop.Tracer{}
14+
var _ tracer.Tracer = &noop.Tracer{}
15+
}
16+
17+
func TestTracerOption(t *testing.T) {
18+
_, err := graphql.ParseSchema(starwars.Schema, nil, graphql.Tracer(noop.Tracer{}))
19+
if err != nil {
20+
t.Fatal(err)
21+
}
22+
}

0 commit comments

Comments
 (0)