gqlgen-contrib/extensions/sentry/tracer.go

96 lines
2.0 KiB
Go

package extensions
import (
"context"
"github.com/99designs/gqlgen/graphql"
"github.com/getsentry/sentry-go"
)
var (
sentrySdkIdentifier = "sentry.go.gqlgen"
)
var _ interface {
graphql.HandlerExtension
graphql.ResponseInterceptor
graphql.FieldInterceptor
} = Sentry{}
type Sentry struct{}
func (Sentry) ExtensionName() string {
return "Sentry"
}
func (Sentry) Validate(graphql.ExecutableSchema) error {
return nil
}
func (r Sentry) InterceptResponse(ctx context.Context, next graphql.ResponseHandler) *graphql.Response {
hub := sentry.GetHubFromContext(ctx)
if hub == nil {
hub = sentry.CurrentHub().Clone()
ctx = sentry.SetHubOnContext(ctx, hub)
}
if client := hub.Client(); client != nil {
client.SetSDKIdentifier(sentrySdkIdentifier)
}
rc := graphql.GetOperationContext(ctx)
span := sentry.StartTransaction(
ctx,
operationName(rc),
sentry.WithOpName("gql"),
sentry.ContinueFromHeaders(
rc.Headers.Get(sentry.SentryTraceHeader),
rc.Headers.Get(sentry.SentryBaggageHeader),
),
)
defer span.Finish()
span.SetData("request.query", rc.RawQuery)
res := next(span.Context())
if len(res.Errors) != 0 {
sentry.CaptureException(res.Errors)
}
return res
}
func (t Sentry) InterceptField(ctx context.Context, next graphql.Resolver) (interface{}, error) {
fc := graphql.GetFieldContext(ctx)
span := sentry.StartSpan(ctx, "resolver")
defer span.Finish()
if fc.Field.ObjectDefinition != nil {
span.Description = fc.Field.ObjectDefinition.Name + "." + fc.Field.Name
span.SetData("resolver.object", fc.Field.ObjectDefinition.Name)
}
span.SetData("resolver.path", fc.Path().String())
span.SetData("resolver.field", fc.Field.Name)
span.SetData("resolver.alias", fc.Field.Alias)
return next(span.Context())
}
func operationName(rc *graphql.OperationContext) string {
requestName := "nameless-operation"
if rc.Doc == nil || len(rc.Doc.Operations) == 0 {
return requestName
}
op := rc.Doc.Operations[0]
if op.Name != "" {
requestName = op.Name
}
return requestName
}