Skip to content

Commit

Permalink
feat: Add basic support for OpenCensus tracing (resolvers only) (#70)
Browse files Browse the repository at this point in the history
* feat: Add basic support for OpenCensus tracing (resolvers only)

* chore: add dep to go.mod
  • Loading branch information
carstendietrich authored Jul 11, 2023
1 parent 7d7c6df commit a2dabe9
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 9 deletions.
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ require (
github.com/99designs/gqlgen v0.17.34
github.com/spf13/cobra v1.7.0
github.com/vektah/gqlparser/v2 v2.5.6
go.opencensus.io v0.24.0
)

require (
Expand Down Expand Up @@ -45,7 +46,6 @@ require (
github.com/spf13/pflag v1.0.5 // indirect
github.com/uber/jaeger-client-go v2.25.0+incompatible // indirect
github.com/zemirco/memorystore v0.0.0-20160308183530-ecd57e5134f6 // indirect
go.opencensus.io v0.24.0 // indirect
go.uber.org/atomic v1.7.0 // indirect
go.uber.org/automaxprocs v1.5.1 // indirect
go.uber.org/multierr v1.6.0 // indirect
Expand Down
26 changes: 18 additions & 8 deletions module.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,21 +37,25 @@ func (*Module) Configure(injector *dingo.Injector) {
type routes struct {
exec graphql.ExecutableSchema
reverseRouter web.ReverseRouter
origins flamingoConfig.Slice
introspectionEnabled bool
uploadMaxSize int64
operationMiddlewares []graphql.OperationMiddleware

// configs
origins flamingoConfig.Slice
openCensusTracingEnabled bool
introspectionEnabled bool
uploadMaxSize int64
}

// Inject executable schema
func (r *routes) Inject(
reverseRouter web.ReverseRouter,
config *struct {
Exec graphql.ExecutableSchema `inject:",optional"`
OperationMiddlewares []graphql.OperationMiddleware `inject:",optional"`
Origins flamingoConfig.Slice `inject:"config:graphql.cors.origins"`
IntrospectionEnabled bool `inject:"config:graphql.introspectionEnabled,optional"`
UploadMaxSize int64 `inject:"config:graphql.multipartForm.uploadMaxSize,optional"`
Exec graphql.ExecutableSchema `inject:",optional"`
OperationMiddlewares []graphql.OperationMiddleware `inject:",optional"`
Origins flamingoConfig.Slice `inject:"config:graphql.cors.origins"`
IntrospectionEnabled bool `inject:"config:graphql.introspectionEnabled,optional"`
OpenCensusTracingEnabled bool `inject:"config:graphql.openCensusTracingEnabled,optional"`
UploadMaxSize int64 `inject:"config:graphql.multipartForm.uploadMaxSize,optional"`
},
) {
r.reverseRouter = reverseRouter
Expand All @@ -61,6 +65,7 @@ func (r *routes) Inject(
r.introspectionEnabled = config.IntrospectionEnabled
r.uploadMaxSize = config.UploadMaxSize
r.operationMiddlewares = config.OperationMiddlewares
r.openCensusTracingEnabled = config.OpenCensusTracingEnabled
}
}

Expand Down Expand Up @@ -96,6 +101,10 @@ func (r *routes) Routes(registry *web.RouterRegistry) {
Cache: lru.New(100),
})

if r.openCensusTracingEnabled {
srv.Use(&OpenCensusTracingExtension{})
}

return srv
}(r.exec)

Expand All @@ -122,6 +131,7 @@ func (m *Module) CueConfig() string {
return `
graphql: {
introspectionEnabled: bool | *false
openCensusTracingEnabled: bool | *true
multipartForm: {
uploadMaxSize: (int | *1.5M) & > 0
}
Expand Down
52 changes: 52 additions & 0 deletions opencensus_tracing.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package graphql

import (
"context"
"fmt"

"github.com/99designs/gqlgen/graphql"
"go.opencensus.io/trace"
)

var _ graphql.HandlerExtension = &OpenCensusTracingExtension{}
var _ graphql.ResponseInterceptor = &OpenCensusTracingExtension{}
var _ graphql.FieldInterceptor = &OpenCensusTracingExtension{}
var _ graphql.OperationInterceptor = &OpenCensusTracingExtension{}

type OpenCensusTracingExtension struct{}

func (o *OpenCensusTracingExtension) ExtensionName() string {
return "OpenCensusTracing"
}

func (o *OpenCensusTracingExtension) InterceptField(ctx context.Context, next graphql.Resolver) (res interface{}, err error) {
fieldContext := graphql.GetFieldContext(ctx)

// for now, we only trace resolvers
if !fieldContext.IsResolver {
return next(ctx)
}

spanName := fmt.Sprintf("graphql/resolver/%s", fieldContext.Field.Name)
if fieldContext.Object != "Query" && fieldContext.Object != "Mutation" {
// enrich information if the resolver is registered on a graphql subtype, e.g. resolver that resolves `sortedList` on type `Users_Result`
spanName = fmt.Sprintf("graphql/resolver/%s/%s", fieldContext.Object, fieldContext.Field.Name)
}

ctx, span := trace.StartSpan(ctx, spanName)
defer span.End()

return next(ctx)
}

func (o *OpenCensusTracingExtension) InterceptOperation(ctx context.Context, next graphql.OperationHandler) graphql.ResponseHandler {
return next(ctx)
}

func (o *OpenCensusTracingExtension) InterceptResponse(ctx context.Context, next graphql.ResponseHandler) *graphql.Response {
return next(ctx)
}

func (o *OpenCensusTracingExtension) Validate(_ graphql.ExecutableSchema) error {
return nil
}

0 comments on commit a2dabe9

Please sign in to comment.