sentry_link 0.5.2 sentry_link: ^0.5.2 copied to clipboard
Automatic capture of exceptions and GraphQL errors for the gql eco-system, like graphql and ferry
Sentry Link (GraphQL) #
Compatibility list #
This integration is compatible with the following packages. It's also compatible with other packages which are build on gql
suite of packages.
package | stats |
---|---|
gql_link |
|
graphql |
|
ferry |
|
artemis |
Usage #
Just add SentryGql.link()
to your links.
It will add error reporting and performance monitoring to your GraphQL operations.
final link = Link.from([
AuthLink(getToken: () async => 'Bearer $personalAccessToken'),
// SentryLink records exceptions
SentryGql.link(
shouldStartTransaction: true,
graphQlErrorsMarkTransactionAsFailed: true,
),
HttpLink('https://api.github.com/graphql'),
]);
A GraphQL errors will be reported as seen in the example below:
Given the following query with an error
query LoadPosts($id: ID!) {
post(id: $id) {
id
# This word is intentionally misspelled to trigger a GraphQL error
titl
body
}
}
it will be represented in Sentry as seen in the image
Improve exception reports for LinkException
s #
LinkException
s and it subclasses can be arbitrary deeply nested. By adding an exception extractor for it, Sentry can create significantly improved exception reports.
Sentry.init((options) {
options.addGqlExtractors();
});
Performance traces for serialization and parsing #
The SentryResponseParser
and SentryRequestSerializer
classes can be used to trace the de/serialization process.
Both classes work with the HttpLink
and the DioLink
.
When using the HttpLink
, you can additionally use the sentryResponseDecoder
function as explained further down below.
Example for HttpLink
#
This example uses the http
integration in addition to this gql integration.
import 'package:sentry/sentry.dart';
import 'package:sentry_link/sentry_link.dart';
final link = Link.from([
AuthLink(getToken: () async => 'Bearer $personalAccessToken'),
SentryGql.link(
shouldStartTransaction: true,
graphQlErrorsMarkTransactionAsFailed: true,
),
HttpLink(
'https://api.github.com/graphql',
httpClient: SentryHttpClient(),
serializer: SentryRequestSerializer(),
parser: SentryResponseParser(),
),
]);
Example for DioLink
#
This example uses the sentry_dio
integration in addition to this gql integration.
import 'package:sentry_link/sentry_link.dart';
import 'package:sentry_dio/sentry_dio.dart';
final link = Link.from([
AuthLink(getToken: () async => 'Bearer $personalAccessToken'),
SentryGql.link(
shouldStartTransaction: true,
graphQlErrorsMarkTransactionAsFailed: true,
),
DioLink(
'https://api.github.com/graphql',
client: Dio()..addSentry(),
serializer: SentryRequestSerializer(),
parser: SentryResponseParser(),
),
]);
HttpLink
Bonus HttpLink
tracing #
import 'dart:async';
import 'dart:convert';
import 'package:sentry/sentry.dart';
import 'package:http/http.dart' as http;
import 'package:sentry_link/sentry_link.dart';
final link = Link.from([
AuthLink(getToken: () async => 'Bearer $personalAccessToken'),
SentryGql.link(
shouldStartTransaction: true,
graphQlErrorsMarkTransactionAsFailed: true,
),
HttpLink(
'https://api.github.com/graphql',
httpClient: SentryHttpClient(networkTracing: true),
serializer: SentryRequestSerializer(),
parser: SentryResponseParser(),
httpResponseDecoder: sentryResponseDecoder,
),
]);
Map<String, dynamic>? sentryResponseDecoder(
http.Response response, {
Hub? hub,
}) {
final currentHub = hub ?? HubAdapter();
final span = currentHub.getSpan()?.startChild(
'serialize.http.client',
description: 'http response deserialization',
);
Map<String, dynamic>? result;
try {
result = _defaultHttpResponseDecoder(response);
span?.status = const SpanStatus.ok();
} catch (e) {
span?.status = const SpanStatus.unknownError();
span?.throwable = e;
rethrow;
} finally {
unawaited(span?.finish());
}
return result;
}
Map<String, dynamic>? _defaultHttpResponseDecoder(http.Response httpResponse) {
return json.decode(utf8.decode(httpResponse.bodyBytes))
as Map<String, dynamic>?;
}
Filter redundant HTTP breadcrumbs #
If you use the sentry_dio
or http
you will have breadcrumbs attached for every HTTP request. In order to not have duplicated breadcrumbs from the HTTP integrations and this GraphQL integration,
you should filter those breadcrumbs.
That can be achieved in two ways:
- Disable all HTTP breadcrumbs.
- Use
beforeBreadcrumb
.
return Sentry.init(
(options) {
options.beforeBreadcrumb = graphQlFilter();
// or
options.beforeBreadcrumb = graphQlFilter((breadcrumb, hint) {
// custom filter
return breadcrumb;
});
},
);
Additional graphql
usage hints #
Additional hints for usage with graphql
graphql
import 'package:sentry/sentry.dart';
import 'package:sentry_link/sentry_link.dart';
import 'package:graphql/graphql.dart';
Sentry.init((options) {
options.addExceptionCauseExtractor(UnknownExceptionExtractor());
options.addExceptionCauseExtractor(NetworkExceptionExtractor());
options.addExceptionCauseExtractor(CacheMissExceptionExtractor());
options.addExceptionCauseExtractor(OperationExceptionExtractor());
options.addExceptionCauseExtractor(CacheMisconfigurationExceptionExtractor());
options.addExceptionCauseExtractor(MismatchedDataStructureExceptionExtractor());
options.addExceptionCauseExtractor(UnexpectedResponseStructureExceptionExtractor());
});
class UnknownExceptionExtractor
extends LinkExceptionExtractor<UnknownException> {}
class NetworkExceptionExtractor
extends LinkExceptionExtractor<NetworkException> {}
class CacheMissExceptionExtractor
extends LinkExceptionExtractor<CacheMissException> {}
class CacheMisconfigurationExceptionExtractor
extends LinkExceptionExtractor<CacheMisconfigurationException> {}
class MismatchedDataStructureExceptionExtractor
extends LinkExceptionExtractor<MismatchedDataStructureException> {}
class UnexpectedResponseStructureExceptionExtractor
extends LinkExceptionExtractor<UnexpectedResponseStructureException> {}
class OperationExceptionExtractor extends ExceptionCauseExtractor<T> {
@override
ExceptionCause? cause(T error) {
return ExceptionCause(error.linkException, error.originalStackTrace);
}
}