-
Notifications
You must be signed in to change notification settings - Fork 213
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
Add support for GraphQL #749
Comments
@IGassmann If you are using gqlgen today and want to get started quickly while this is being implemented you can use this package that I found yesterday and add a small error handler. Solved almost all of my needs. gqlHandler.SetErrorPresenter(func(ctx context.Context, err error) *gqlerror.Error {
hub := sentry.GetHubFromContext(ctx)
if hub == nil {
hub = sentry.CurrentHub()
}
hub.CaptureException(err)
return graphql.DefaultErrorPresenter(ctx, err)
}) |
For those interested, here is how I instrumented gqlHandler.AroundOperations(func(ctx context.Context, next graphql.OperationHandler) graphql.ResponseHandler {
oc := graphql.GetOperationContext(ctx)
operationType := string(oc.Operation.Operation)
hub := sentry.GetHubFromContext(ctx)
if hub == nil {
hub = sentry.CurrentHub().Clone()
ctx = sentry.SetHubOnContext(ctx, hub)
}
// See https://docs.sentry.io/platforms/go/guides/http/enriching-events/scopes/
hub.ConfigureScope(func(scope *sentry.Scope) {
scope.SetContext("graphql", map[string]interface{}{
"document": oc.RawQuery,
"variables": oc.Variables,
})
scope.SetTag("graphql.operation.name", oc.OperationName)
scope.SetTag("graphql.operation.type", operationType)
user, err := authn.User(ctx)
if err == nil && user != nil {
scope.SetUser(sentry.User{
ID: user.ID.String(),
Email: user.Email,
})
}
})
// See https://docs.sentry.io/platforms/go/guides/http/performance/instrumentation/custom-instrumentation/
span := sentry.StartSpan(ctx, fmt.Sprintf("graphql.%s", operationType))
span.Description = fmt.Sprintf("%s %s", operationType, oc.OperationName)
// Before the operation
handler := next(ctx)
return func(ctx context.Context) *graphql.Response {
response := handler(ctx)
// After the operation
span.Finish()
return response
}
})
gqlHandler.AroundFields(func(ctx context.Context, next graphql.Resolver) (res interface{}, err error) {
fc := graphql.GetFieldContext(ctx)
// Skip fields that don't have a resolver.
if !fc.IsResolver {
return next(ctx)
}
hub := sentry.GetHubFromContext(ctx)
if hub == nil {
hub = sentry.CurrentHub().Clone()
ctx = sentry.SetHubOnContext(ctx, hub)
}
// See https://docs.sentry.io/platforms/go/guides/http/performance/instrumentation/custom-instrumentation/
fieldPath := fmt.Sprintf("%s.%s", fc.Object, fc.Field.Name)
span := sentry.StartSpan(ctx, "graphql.resolve")
span.Description = fmt.Sprintf("resolving %s", fieldPath)
span.SetData("graphql.field_name", fc.Field.Name)
span.SetData("graphql.field_path", fieldPath)
span.SetData("graphql.path", fc.Path().String())
res, err = next(ctx)
span.Finish()
return res, err
})
gqlHandler.SetRecoverFunc(func(ctx context.Context, err interface{}) error {
hub := sentry.GetHubFromContext(ctx)
if hub == nil {
hub = sentry.CurrentHub().Clone()
}
hub.RecoverWithContext(ctx, err)
// Return a generic error to the user
return &gqlerror.Error{
Path: graphql.GetPath(ctx),
Message: "Internal system error.",
Extensions: map[string]interface{}{
"code": "INTERNAL_SYSTEM_ERROR",
},
}
})
gqlHandler.SetErrorPresenter(func(ctx context.Context, err error) *gqlerror.Error {
var gqlErr *gqlerror.Error
if errors.As(err, &gqlErr) {
if errCode, ok := gqlErr.Extensions["code"].(string); ok {
// We don't want to log GraphQL built-in validation and parsing errors
if errCode == errcode.ValidationFailed || errCode == errcode.ParseFailed {
return gqlErr
}
// We already log internal system errors in the recover function
if errCode == "INTERNAL_SYSTEM_ERROR" {
return gqlErr
}
}
}
hub := sentry.GetHubFromContext(ctx)
if hub == nil {
hub = sentry.CurrentHub().Clone()
ctx = sentry.SetHubOnContext(ctx, hub)
}
hub.CaptureException(err)
return graphql.DefaultErrorPresenter(ctx, err)
}) I inspired myself by Sentry's Strawberry integration, but I, unfortunately, wasn't able to get syntax highlighting working like on the Sentry blog post. Any improvement suggestions are welcomed :) |
@IGassmann Nice! I will definitely change our implementation to something more like your solution |
Summary
Sentry recently released improved GraphQL support. However, there isn't yet support for Go's GraphQL libraries.
It would be great to see support for Go's most popular GraphQL libraries, such as gqlgen, in the same way support was added for other languages like Python's Strawberry integration.
Motivation
This would provide a better error-reporting experience for GraphQL APIs written in Go.
The text was updated successfully, but these errors were encountered: