Skip to content

Commit

Permalink
Implement options to skip tracing for IntrospectionQuery and fields w…
Browse files Browse the repository at this point in the history
…ithout methods, addressing issues DataDog#2610 and DataDog#2684
  • Loading branch information
devin-ai-integration[bot] committed May 3, 2024
1 parent 6e59915 commit 6aff485
Show file tree
Hide file tree
Showing 2 changed files with 57 additions and 33 deletions.
24 changes: 21 additions & 3 deletions contrib/99designs/gqlgen/option.go
Expand Up @@ -15,9 +15,11 @@ import (
const defaultServiceName = "graphql"

type config struct {
serviceName string
analyticsRate float64
tags map[string]interface{}
serviceName string
analyticsRate float64
tags map[string]interface{}
skipFieldsWithoutMethods bool // New field to determine if fields without methods should be skipped
skipIntrospectionQuery bool // New field to determine if "IntrospectionQuery" should be skipped during tracing
}

// An Option configures the gqlgen integration.
Expand All @@ -27,6 +29,8 @@ func defaults(cfg *config) {
cfg.serviceName = namingschema.ServiceNameOverrideV0(defaultServiceName, defaultServiceName)
cfg.analyticsRate = globalconfig.AnalyticsRate()
cfg.tags = make(map[string]interface{})
cfg.skipFieldsWithoutMethods = false // Default value is false, meaning by default it will not skip fields without methods
cfg.skipIntrospectionQuery = false // Default value is false, meaning by default it will not skip "IntrospectionQuery"
}

// WithAnalytics enables or disables Trace Analytics for all started spans.
Expand Down Expand Up @@ -60,3 +64,17 @@ func WithCustomTag(key string, value interface{}) Option {
cfg.tags[key] = value
}
}

// WithSkipFieldsWithoutMethods sets the skipFieldsWithoutMethods option to true.
func WithSkipFieldsWithoutMethods() Option {
return func(cfg *config) {
cfg.skipFieldsWithoutMethods = true
}
}

// WithSkipIntrospectionQuery sets the skipIntrospectionQuery option to true.
func WithSkipIntrospectionQuery() Option {
return func(cfg *config) {
cfg.skipIntrospectionQuery = true
}
}
66 changes: 36 additions & 30 deletions contrib/99designs/gqlgen/tracer.go
Expand Up @@ -81,6 +81,8 @@ type gqlTracer struct {
cfg *config
}

// Removed lines related to config struct redeclaration and WithSkipFieldsWithoutMethods function

// NewTracer creates a graphql.HandlerExtension instance that can be used with
// a graphql.handler.Server.
// Options can be passed in for further configuration.
Expand Down Expand Up @@ -137,36 +139,40 @@ func (t *gqlTracer) InterceptOperation(ctx context.Context, next graphql.Operati
}

func (t *gqlTracer) InterceptField(ctx context.Context, next graphql.Resolver) (res any, err error) {
opCtx := graphql.GetOperationContext(ctx)
if opCtx.OperationName == "IntrospectionQuery" {
return next(ctx)
}
fieldCtx := graphql.GetFieldContext(ctx)
opts := make([]tracer.StartSpanOption, 0, 6+len(t.cfg.tags))
for k, v := range t.cfg.tags {
opts = append(opts, tracer.Tag(k, v))
}
opts = append(opts,
tracer.Tag(tagGraphqlField, fieldCtx.Field.Name),
tracer.Tag(tagGraphqlOperationType, opCtx.Operation.Operation),
tracer.Tag(ext.Component, componentName),
tracer.ResourceName(fmt.Sprintf("%s.%s", fieldCtx.Object, fieldCtx.Field.Name)),
tracer.Measured(),
)
if !math.IsNaN(t.cfg.analyticsRate) {
opts = append(opts, tracer.Tag(ext.EventSampleRate, t.cfg.analyticsRate))
}
span, ctx := tracer.StartSpanFromContext(ctx, fieldOp, opts...)
defer func() { span.Finish(tracer.WithError(err)) }()
ctx, op := graphqlsec.StartResolveOperation(ctx, graphqlsec.FromContext[*types.ExecutionOperation](ctx), span, types.ResolveOperationArgs{
Arguments: fieldCtx.Args,
TypeName: fieldCtx.Object,
FieldName: fieldCtx.Field.Name,
Trivial: !(fieldCtx.IsMethod || fieldCtx.IsResolver), // TODO: Is this accurate?
})
defer func() { op.Finish(types.ResolveOperationRes{Data: res, Error: err}) }()
res, err = next(ctx)
return
opCtx := graphql.GetOperationContext(ctx)
if t.cfg.skipIntrospectionQuery && opCtx.OperationName == "IntrospectionQuery" {
return next(ctx)
}
fieldCtx := graphql.GetFieldContext(ctx)
// Skip creating a span if WithSkipFieldsWithoutMethods is set and the field is not a method
if t.cfg.skipFieldsWithoutMethods && !fieldCtx.IsMethod {
return next(ctx)
}
opts := make([]tracer.StartSpanOption, 0, 6+len(t.cfg.tags))
for k, v := range t.cfg.tags {
opts = append(opts, tracer.Tag(k, v))
}
opts = append(opts,
tracer.Tag(tagGraphqlField, fieldCtx.Field.Name),
tracer.Tag(tagGraphqlOperationType, opCtx.Operation.Operation),
tracer.Tag(ext.Component, componentName),
tracer.ResourceName(fmt.Sprintf("%s.%s", fieldCtx.Object, fieldCtx.Field.Name)),
tracer.Measured(),
)
if !math.IsNaN(t.cfg.analyticsRate) {
opts = append(opts, tracer.Tag(ext.EventSampleRate, t.cfg.analyticsRate))
}
span, ctx := tracer.StartSpanFromContext(ctx, fieldOp, opts...)
defer func() { span.Finish(tracer.WithError(err)) }()
ctx, op := graphqlsec.StartResolveOperation(ctx, graphqlsec.FromContext[*types.ExecutionOperation](ctx), span, types.ResolveOperationArgs{
Arguments: fieldCtx.Args,
TypeName: fieldCtx.Object,
FieldName: fieldCtx.Field.Name,
Trivial: !(fieldCtx.IsMethod || fieldCtx.IsResolver), // TODO: Is this accurate?
})
defer func() { op.Finish(types.ResolveOperationRes{Data: res, Error: err}) }()
res, err = next(ctx)
return
}

func (*gqlTracer) InterceptResponse(ctx context.Context, next graphql.ResponseHandler) *graphql.Response {
Expand Down

0 comments on commit 6aff485

Please sign in to comment.