Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add basic support for OpenCensus tracing (resolvers only) #70

Merged
merged 2 commits into from
Jul 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
}