The Atatus Flutter SDK reports network requests as RUM resources, so you can see the URL, method, status code, and duration of every call your application makes. Each integration ships as its own package, so you add only the ones your application uses.
This page covers the four network integrations and how to choose between them.
Choose an integration
Pick the package that matches the client your application uses:
| Client | Package | How it attaches |
|---|---|---|
dart:io HttpClient, and the http package on top of it |
atatus_tracking_http_client |
Globally, with enableHttpTracking() |
The http package, wrapped explicitly |
atatus_tracking_http_client |
Per client, with AtatusClient |
| Dio | atatus_dio |
Per client, with addAtatusInterceptor() |
| gRPC | atatus_grpc_interceptor |
Per client, with AtatusGrpcInterceptor |
GraphQL (gql_link) |
atatus_gql_link |
In the link chain, with AtatusGqlLink |
All of them report resources on their own. To also connect those resources to your backend traces, list your API domains in firstPartyHosts. See Flutter Distributed Tracing.
Track HTTP requests
atatus_tracking_http_client tracks requests made with dart:io HttpClient. The http package uses HttpClient underneath on Android, so this one integration covers both.
Add the package to your pubspec.yaml:
dependencies:
atatus_flutter_plugin: ^1.0.4
atatus_tracking_http_client: ^1.0.4
Call enableHttpTracking() on your configuration before you initialize the SDK:
import 'package:atatus_tracking_http_client/atatus_tracking_http_client.dart';
final configuration = AtatusConfiguration(
licenseKey: '<LICENSE_KEY>',
env: '<ENV_NAME>',
appName: '<APP_NAME>',
firstPartyHosts: ['api.example.com'],
)..enableHttpTracking();
Every HttpClient created after initialization is then tracked, with no change to your request code.
enableHttpTracking() replaces HttpOverrides.global. If your application sets its own HttpOverrides, install yours before you initialize Atatus. The SDK reads HttpOverrides.current during initialization and wraps it, so your overrides keep working. Installing yours afterwards replaces the Atatus one and stops all network tracking.
Ignore specific URLs
Pass ignoreUrlPatterns to skip requests you do not want reported, such as health checks or an endpoint already tracked by another integration:
final configuration = AtatusConfiguration(
licenseKey: '<LICENSE_KEY>',
env: '<ENV_NAME>',
appName: '<APP_NAME>',
)..enableHttpTracking(
ignoreUrlPatterns: [
RegExp('example.com/graphql'),
RegExp('/health\$'),
],
);
Each pattern is matched against the full request URL.
Wrap a single http client
AtatusClient is a composable http.Client you create yourself. Use it instead of enableHttpTracking() when either of these is true:
- You use a native HTTP library such as
cronet_httporcupertino_http. Those do not go throughdart:ioHttpClient, so global tracking cannot see them. - You want to track only some requests rather than all of them.
Create the client and pass it wherever you make requests:
import 'package:atatus_tracking_http_client/atatus_tracking_http_client.dart';
import 'package:http/http.dart' as http;
final atatusClient = AtatusClient(
atatusSdk: AtatusSdk.instance,
innerClient: http.Client(),
);
final response = await atatusClient.get(Uri.parse('https://api.example.com/orders'));
innerClient is optional. Leave it out and AtatusClient creates a default http.Client for you. Because it wraps any http.BaseClient, you can compose it with other clients such as a retry client.
AtatusClient also accepts ignoreUrlPatterns, and an attributesProvider that lets you attach your own attributes to each resource:
final atatusClient = AtatusClient(
atatusSdk: AtatusSdk.instance,
ignoreUrlPatterns: [RegExp('/health\$')],
attributesProvider: (request, response, error) {
return {'tenant': tenantIdFor(request)};
},
);
Use one approach or the other. AtatusClient and enableHttpTracking() both track the same http package traffic, so using both reports each request twice. The exception is cronet_http and cupertino_http, which global tracking cannot see at all, so the two can safely run together in that case.
Track Dio requests
atatus_dio reports Dio requests as RUM resources. Add the package:
dependencies:
atatus_flutter_plugin: ^1.0.4
atatus_dio: ^1.0.4
Add the interceptor to your Dio instance:
import 'package:atatus_dio/atatus_dio.dart';
import 'package:dio/dio.dart';
final dio = Dio();
dio.addAtatusInterceptor(AtatusSdk.instance);
addAtatusInterceptor inserts the Atatus interceptor at the front of the list, so it sees every request even if a later interceptor stops the chain. Call it after the rest of your Dio setup so nothing displaces it.
Dio options
addAtatusInterceptor accepts four optional parameters:
| Parameter | Purpose |
|---|---|
ignoreUrlPatterns |
A list of RegExp matched against the request URL. Matching requests are not reported |
attributesProvider |
An AtatusDioAttributeProvider that adds your own attributes on request, response, and error |
parentSpanResolver |
Returns a parent span ID for a request, so the resource is attached to a span you already have |
kindResolver |
Returns a RumResourceType for a response, overriding the type derived from the content type |
To add your own attributes, implement AtatusDioAttributeProvider:
class TenantAttributes implements AtatusDioAttributeProvider {
@override
Map<String, Object?>? onRequest(RequestOptions request) =>
{'tenant': request.headers['x-tenant']};
@override
Map<String, Object?>? onResponse(Response<dynamic> response) => null;
@override
Map<String, Object?>? onError(DioException err) => {'retryable': err.type.name};
}
dio.addAtatusInterceptor(
AtatusSdk.instance,
attributesProvider: TenantAttributes(),
);
Attributes returned from onRequest land on the resource when it starts. Attributes from onResponse and onError land on it when it finishes.
Track gRPC requests
atatus_grpc_interceptor reports gRPC calls as RUM resources. Add the package:
dependencies:
atatus_flutter_plugin: ^1.0.4
atatus_grpc_interceptor: ^1.0.4
Create the interceptor with your channel, then pass it to your generated client:
import 'package:atatus_grpc_interceptor/atatus_grpc_interceptor.dart';
import 'package:grpc/grpc.dart';
final channel = ClientChannel(
'api.example.com',
port: 50051,
);
final atatusInterceptor = AtatusGrpcInterceptor(AtatusSdk.instance, channel);
final stub = GreeterClient(channel, interceptors: [atatusInterceptor]);
The interceptor needs the channel as well as the SDK, because it builds the request URL from the channel host and port so that first party host matching works.
Each call is reported with the gRPC method path in a grpc.method attribute.
The gRPC interceptor tracks unary calls only. Streaming calls are not intercepted and do not appear as resources.
Track GraphQL requests
atatus_gql_link reports GraphQL operations as RUM resources and records the operation name, the operation type, and the variables. Add the package:
dependencies:
atatus_flutter_plugin: ^1.0.4
atatus_gql_link: ^1.0.4
Add AtatusGqlLink above your terminating link:
import 'package:atatus_gql_link/atatus_gql_link.dart';
import 'package:gql_link/gql_link.dart';
import 'package:gql_http_link/gql_http_link.dart';
const graphQlUrl = 'https://api.example.com/graphql';
final link = Link.from([
AtatusGqlLink(AtatusSdk.instance, Uri.parse(graphQlUrl)),
HttpLink(graphQlUrl),
]);
AtatusGqlLink is not a terminating link, so it always needs a link such as HttpLink after it.
If you use atatus_gql_link together with atatus_tracking_http_client, tell the HTTP tracking to ignore your GraphQL endpoint. Otherwise every operation is reported twice, once by each integration, and the backend trace can break:
final configuration = AtatusConfiguration(
licenseKey: '<LICENSE_KEY>',
env: '<ENV_NAME>',
appName: '<APP_NAME>',
)..enableHttpTracking(
ignoreUrlPatterns: [RegExp('api.example.com/graphql')],
);
What is collected from a GraphQL operation
Each operation carries these attributes in addition to the usual resource fields:
| Attribute | Description |
|---|---|
_atatus.graphql.operation_name |
The operation name, taken from the request or read from the query document |
_atatus.graphql.operation_type |
query, mutation, or subscription |
_atatus.graphql.variables |
The variables sent with the operation, JSON encoded |
_atatus.graphql.errors |
Any errors returned in the GraphQL response |
Variables are recorded as sent. If your queries carry personal data, passwords, or tokens in their variables, redact them with a resourceEventMapper before the event leaves the device. See Modify or drop RUM events.
Track a custom resource
If none of the integrations covers a request, report it yourself with startResource and stopResource. See Manually track custom resources.
Next steps
- Connect resources to your backend with Flutter Distributed Tracing.
- Configure the rest of the agent in Flutter Agent Configuration Options.
- Record what your users saw with Flutter Session Replay.
+1-415-800-4104