Flutter Application Integration¶
Prerequisites¶
Note: If you have activated the RUM Headless service, the prerequisites have already been automatically configured for you, and you can directly integrate the application.
- Install DataKit;
- Configure the RUM Collector;
- Ensure DataKit is accessible via the public network and install the IP Geolocation Database.
Application Integration¶
Note
The current Flutter version only supports Android and iOS platforms.
- Navigate to User Access Monitoring > Create Application > Android/iOS;
- Create two applications for Flutter Android and Flutter iOS respectively to receive RUM data from the Android and iOS platforms;
- Enter the corresponding application name and application ID for each platform;
- Choose the application integration method:
- Public DataWay: Directly receive RUM data without installing the DataKit collector.
- Local Environment Deployment: Receive RUM data after meeting the prerequisites.
Installation¶
Pub.Dev: ft_mobile_agent_flutter
Source Code: https://github.com/GuanceCloud/datakit-flutter
Demo: https://github.com/GuanceCloud/datakit-flutter/example
In the project directory, run the following Flutter command in the terminal:
This will add the following line to the pubspec.yaml of the package (and run an implicit flutter pub get):
dependencies:
ft_mobile_agent_flutter: [lastest_version]
# For compatibility with Flutter 2.0, use the following reference method
ft_mobile_agent_flutter:
git:
url: https://github.com/GuanceCloud/datakit-flutter.git
ref: [github_legacy_lastest_tag]
Now in your Dart code, you can use:
Additional Configuration for Android Integration
- Configure the Gradle Plugin ft-plugin to collect App launch events and native Android events (page navigation, click events, native network requests, WebView data)
- Customize the
Application
and declare it inAndroidMainifest.xml
as follows.
import io.flutter.app.FlutterApplication
/**
* If you need to count [Launch Counts] and [Launch Time], you need to add a custom Application here
*/
class CustomApplication : FlutterApplication() {
}
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.cloudcare.ft.mobile.sdk.agent_example">
<application android:name=".CustomApplication">
//...
</application>
</manifest>
SDK Initialization¶
Basic Configuration¶
void main() async {
WidgetsFlutterBinding.ensureInitialized();
//Local environment deployment, Datakit deployment
await FTMobileFlutter.sdkConfig(
datakitUrl: datakitUrl
);
//Use public DataWay
await FTMobileFlutter.sdkConfig(
datawayUrl: datawayUrl,
cliToken: cliToken,
);
}
Field | Type | Required | Description |
---|---|---|---|
datakitUrl | String | Yes | The URL address for local environment deployment (Datakit) to report data, example: http://10.0.0.1:9529, the default port is 9529, the device installing the SDK needs to be able to access this address. Note: Only one of datakitUrl and datawayUrl needs to be configured |
datawayUrl | String | Yes | The URL address for public Dataway to report data, obtained from the [User Access Monitoring] application, example: https://open.dataway.url, the device installing the SDK needs to be able to access this address. Note: Only one of datakitUrl and datawayUrl needs to be configured |
cliToken | String | Yes | Authentication token, needs to be configured together with datawayUrl |
debug | bool | No | Set whether to allow logging, default false |
env | String | No | Environment configuration, default prod , any character, it is recommended to use a single word, such as test etc |
envType | enum EnvType | No | Environment configuration, default EnvType.prod . Note: Only one of env and envType needs to be configured |
autoSync | bool | No | Whether to automatically sync data to the server after collection, default true . When false , use FTMobileFlutter.flushSyncData() to manage data synchronization manually |
syncPageSize | enum | No | Set the number of entries for sync requests, SyncPageSize.mini 5 entries, SyncPageSize.medium 10 entries, SyncPageSize.large 50 entries, default SyncPageSize.medium |
customSyncPageSize | number | No | Set the number of entries for sync requests. Range [5,)Note: The larger the number of entries, the more computational resources the data synchronization will occupy |
syncSleepTime | number | No | Set the sync interval time. Range [0,5000], default not set |
globalContext | object | No | Add custom tags. For adding rules, please refer to here |
serviceName | String | No | Service name |
enableLimitWithDbSize | boolean | No | Enable using db to limit data size, default 100MB, unit Byte, the larger the database, the greater the disk pressure, default not enabled. Note: After enabling, the Log configuration logCacheLimitCount and RUM configurationrumCacheLimitCount will be invalid. SDK version 0.5.3-pre.2 and above supports this parameter |
dbCacheLimit | number | No | DB cache limit size. Range [30MB,), default 100MB, unit byte, SDK version 0.5.3-pre.2 and above supports this parameter |
dbCacheDiscard | string | No | Set the data discard rule in the database. Discard strategy: FTDBCacheDiscard.discard discard new data (default), FTDBCacheDiscard.discardOldest discard old data. SDK version 0.5.3-pre.2 and above supports this parameter |
compressIntakeRequests | boolean | No | Compress the uploaded sync data with deflate, supported in version 0.5.3-pre.2 and above, default off |
enableDataIntegerCompatible | boolean | No | Recommended to enable when coexisting with web data. This configuration is used to handle web data type storage compatibility issues. Default enabled in version 0.5.4-pre.1 and above |
dataModifier | Map |
No | Modify a single field. |
lineDataModifier | Map |
No | Modify a single piece of data. |
RUM Configuration¶
Field | Type | Required | Description |
---|---|---|---|
androidAppId | String | Yes | app_id, applied from the User Access Monitoring console |
iOSAppId | String | Yes | app_id, applied from the User Access Monitoring console |
sampleRate | double | No | Sampling rate, range [0,1], 0 means no collection, 1 means full collection, default value is 1. Scope is all View, Action, LongTask, Error data under the same session_id |
sessionOnErrorSampleRate | double | No | Set the error collection rate. When the session is not sampled by setSamplingRate, if an error occurs during the session, data within 1 minute before the error can be collected, range [0,1], 0 means no collection, 1 means full collection, default value is 0. Scope is all View, Action, LongTask, Error data under the same session_id |
enableUserResource | bool | No | Whether to enable automatic capture of http Resource data, default false , this is achieved by modifying HttpOverrides.global , if the project has customization needs in this area, inherit FTHttpOverrides . |
enableNativeUserAction | bool | No | Whether to track Native Action , native system Button click events, app launch events, default false |
enableNativeUserView | bool | No | Whether to enable automatic tracking of Native View , recommended to turn off for pure Flutter applications, default false |
enableNativeUserViewInFragment | bool | No | Whether to automatically track Native Fragment type page data, default false. Only supports Android |
enableNativeUserResource | bool | No | Whether to enable automatic tracking of Native Resource , recommended to turn off for pure Flutter applications, default false |
enableAppUIBlock | bool | No | Whether to enable automatic tracking of Native Freeze , default false |
nativeUiBlockDurationMS | int | No | Whether to set the time range for Native Freeze , range [100,), unit milliseconds. iOS default 250ms, Android default 1000ms |
enableTrackNativeAppANR | bool | No | Whether to enable Native ANR monitoring, default false |
enableTrackNativeCrash | bool | No | Whether to enable Android Java Crash and OC/C/C++ crash monitoring, default false |
errorMonitorType | enum ErrorMonitorType | No | Set auxiliary monitoring information, add additional monitoring data to RUM Error data, ErrorMonitorType.battery for battery level, ErrorMonitorType.memory for memory usage, ErrorMonitorType.cpu for CPU usage, default not enabled |
deviceMetricsMonitorType | enum DeviceMetricsMonitorType | No | Add monitoring data during the View cycle, DeviceMetricsMonitorType.battery (only Android) monitors the highest current output of the current page, DeviceMetricsMonitorType.memory monitors the memory usage of the current application, DeviceMetricsMonitorType.cpu monitors CPU jumps, DeviceMetricsMonitorType.fps monitors screen frame rate, default not enabled |
detectFrequency | enum DetectFrequency | No | View performance monitoring sampling period, default DetectFrequency.normal |
globalContext | Map | No | Custom global parameters |
rumCacheDiscard | enum | No | Discard strategy: FTRUMCacheDiscard.discard discard new data (default), FTRUMCacheDiscard.discardOldest discard old data |
rumCacheLimitCount | number | No | Local cache maximum RUM entry count limit [10_000,), default 100_000 |
isInTakeUrl | callBack | No | Set the conditions for filtering Resource, default no filtering |
Add Custom Tags¶
Static Usage¶
- Split the original main.dart into two parts, one part is main(), the other part is the App() MaterialApp component;
- Create entry files corresponding to various environments, such as: main_prod.dart, main_gray.dart, etc.;
- Configure custom tags in the corresponding environment file. For example:
///main_prod.dart
void main() async {
WidgetsFlutterBinding.ensureInitialized();
//Initialize SDK
await FTMobileFlutter.sdkConfig(
datakitUrl: serverUrl,
debug: true,
);
await FTRUMManager().setConfig(
androidAppId: appAndroidId,
iOSAppId: appIOSId,
globalContext: {CUSTOM_STATIC_TAG:"prod_static_tag"},
);
runApp(MyApp());
};
Dynamic Usage¶
- Through file type data storage, for example
shared_preferences
librarySharedPreferences
, configure the use ofSDK
, add code to obtain tag data at the configuration.
final prefs = await SharedPreferences.getInstance();
String customDynamicValue = prefs.getString("customDynamicValue")?? "not set";
await FTRUMManager().setConfig(
androidAppId: appAndroidId,
iOSAppId: appIOSId,
globalContext: {CUSTOM_DYNAMIC_TAG:customDynamicValue},
//… Add other configurations
);
- Add a method to change file data anywhere.
static Future<void> setDynamicParams(String value) async{
final prefs = await SharedPreferences.getInstance();
prefs.setString(CUSTOM_DYNAMIC_TAG, value);
}
- Finally, restart the application.
Note:
- Special key : track_id (used for tracking function) .
- When the user adds custom tags through globalContext that are the same as the SDK's own tags, the SDK's tags will overwrite the user's settings. It is recommended to add a project abbreviation prefix to the tag name, for example
df_tag_name
. Thekey
value used in the project can be queried from the source code.
Log Configuration¶
Field | Type | Required | Description |
---|---|---|---|
sampleRate | double | No | Sampling rate, range [0,1], 0 means no collection, 1 means full collection, default value is 1. |
enableLinkRumData | bool | No | Whether to associate with RUM |
enableCustomLog | bool | No | Whether to enable custom logs |
logLevelFilters | List |
No | Log level filtering |
logCacheLimitCount | int | No | Local cache maximum log entry count limit [1000,), the larger the log, the greater the disk cache pressure, default 5000 |
discardStrategy | enum FTLogCacheDiscard | No | Set the log discard rule after reaching the limit. DefaultFTLogCacheDiscard.discard , discard discard appended data, discardOldest discard old data |
Trace Configuration¶
Field | Type | Required | Description |
---|---|---|---|
sampleRate | double | No | Sampling rate, range [0,1], 0 means no collection, 1 means full collection, default value is 1. |
traceType | enum TraceType | No | Trace type, defaultTraceType.ddTrace . |
enableLinkRUMData | bool | No | Whether to associate with RUM data, defaultfalse . |
enableAutoTrace | bool | No | Whether to add Trace Header in http requests, defaultfalse , this is achieved by modifying HttpOverrides.global , if the project has customization needs in this area, inherit FTHttpOverrides |
enableNativeAutoTrace | bool | No | Whether to enable native network auto-tracing for iOS NSURLSession ,Android OKhttp , defaultfalse . |
RUM User Data Tracking¶
Action¶
Usage¶
/// Add action
/// [actionName] action name
/// [actionType] action type
/// [property] additional property parameters (optional)
Future<void> startAction(String actionName, String actionType,
{Map<String, String>? property})
Code Example¶
View¶
Auto Collection¶
- Method 1: Add
FTRouteObserver
toMaterialApp.navigatorObservers
, set the pages to be navigated inMaterialApp.routes
, thekey
inroutes
is the page name (view_name
).
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: HomeRoute(),
navigatorObservers: [
//RUM View: Monitor page lifecycle when using route navigation
FTRouteObserver(),
],
routes: <String, WidgetBuilder>{
//Set Route route navigation
'logging': (BuildContext context) => Logging(),
'rum': (BuildContext context) => RUM(),
'tracing_custom': (BuildContext context) => CustomTracing(),
'tracing_auto': (BuildContext context) => AutoTracing(),
},
);
}
}
//Use this method for page navigation, the page name here is logging
Navigator.pushNamed(context, "logging");
- Method 2: Add
FTRouteObserver
toMaterialApp.navigatorObservers
, use a customFTMaterialPageRoute
to parse the page name from the widget's runtimeType, where thewidget
class name is the page name (view_name
), see example here.
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: HomeRoute(),
navigatorObservers: [
//RUM View: Monitor page lifecycle when using route navigation
FTRouteObserver(),
],
);
}
}
//Here "page name" is NoRouteNamePage
Navigator.of(context).push(FTMaterialPageRoute(builder: (context) =>
new NoRouteNamePage()
- Method 3: Add
FTRouteObserver
toMaterialApp.navigatorObservers
, customize theRouteSettings.name
property inRoute
type pages,FTRouteObserver
collection logic will prioritize obtaining the value ofRouteSettings.name
, this method also applies to Dialog type pages, such asshowDialog()
,showTimePicker()
etc.
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: HomeRoute(),
navigatorObservers: [
//RUM View: Monitor page lifecycle when using route navigation
FTRouteObserver(),
],
);
}
}
//Here "page name" is "RouteSettingName"
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => new NoRouteNamePage(),
settings: RouteSettings(name: "RouteSettingName"))
-
The above three methods can be mixed in one project
-
Sleep and wake event collection For versions below 0.5.1-pre.1, if you need to collect application sleep and wake behavior, add the following code:
class _HomeState extends State<HomeRoute> {
@override
void initState(){
//Add application sleep and wake listener
FTLifeRecycleHandler().initObserver();
}
@override
void dispose(){
//Remove application sleep and wake listener
FTLifeRecycleHandler().removeObserver();
}
}
Auto Collection Filtering¶
Only supported in versions 0.5.0-pre.1 and above
FTRouteObserver
MaterialApp(
navigatorObservers: [
// RUM View: routeFilter filters pages that do not need to be monitored
FTRouteObserver(routeFilter: (Route? route, Route? previousRoute) {
if (filterConfig) {
//Do not collect
return true;
}
return false;
}),
])
Field | Type | Required | Description |
---|---|---|---|
routeFilter | RouteFilter | No | Page method callback, can judge based on the incoming and previous route specific situation, return true means filter data that meets the conditions, otherwise do not filter |
FTDialogRouteFilterObserver
Filter DialogRoute
type pages, such as showDialog()
,showTimePicker()
etc.
MaterialApp(
navigatorObservers: [
//RUM View filter DialogRoute type components
FTDialogRouteFilterObserver(filterOnlyNoSettingName: true)
])
// Here the Dialog will be collected if filterOnlyNoSettingName is true.
// view_name is "About"
showAboutDialog(
context: context, routeSettings: RouteSettings(name: "About"));
Field | Type | Required | Description |
---|---|---|---|
filterOnlyNoSettingName | bool | No | Only filter RouteSettings.name as null Route pages |
Custom View¶
Usage¶
/// view creation, this method needs to be called before [starView], currently flutter route does not have
/// [viewName] page name
/// [duration]
Future<void> createView(String viewName, int duration)
/// view start
/// [viewName] page name
/// [viewReferer] previous page name
/// [property] additional property parameters (optional)
Future<void> starView(String viewName, {Map<String, String>? property})
/// view end
/// [property] additional property parameters (optional)
Future<void> stopView({Map<String, String>? property})
Code Example¶
FTRUMManager().createView("Current Page Name", 100000000)
FTRUMManager().starView("Current Page Name");
FTRUMManager().stopView();
Error¶
Auto Collection¶
void main() async {
runZonedGuarded(() async {
WidgetsFlutterBinding.ensureInitialized();
await FTMobileFlutter.sdkConfig(
datakitUrl: serverUrl,
debug: true,
);
await FTRUMManager().setConfig(
androidAppId: appAndroidId,
iOSAppId: appIOSId,
);
// Flutter exception capture
FlutterError.onError = FTRUMManager().addFlutterError;
runApp(MyApp());
}, (Object error, StackTrace stack) {
//Add Error data
FTRUMManager().addError(error, stack);
});
Custom Error¶
Usage¶
///Add custom error
/// [stack] stack log
/// [message] error message
/// [appState] application state
/// [errorType] custom errorType
/// [property] additional property parameters (optional)
Future<void> addCustomError(String stack, String message,
{Map<String, String>? property, String? errorType})
Code Example¶
Resource¶
Auto Collection¶
Enable enableUserResource
through configuration FTRUMManager().setConfig
.
Custom Resource¶
Usage¶
///Start resource request
/// [key] unique id
/// [property] additional property parameters (optional)
Future<void> startResource(String key, {Map<String, String>? property})
///End resource request
/// [key] unique id
/// [property] additional property parameters (optional)
Future<void> stopResource(String key, {Map<String, String>? property})
/// Send resource data metrics
/// [key] unique id
/// [url] request URL
/// [httpMethod] request method
/// [requestHeader] request header parameters
/// [responseHeader] response header parameters
/// [responseBody] response content
/// [resourceStatus] response status code
Future<void> addResource(
{required String key,
required String url,
required String httpMethod,
required Map<String, dynamic> requestHeader,
Map<String, dynamic>? responseHeader,
String? responseBody = "",
int? resourceStatus})
Code Example¶
/// Use httpClient
void httpClientGetHttp(String url) async {
var httpClient = new HttpClient();
String key = Uuid().v4();
HttpClientResponse? response;
HttpClientRequest? request;
try {
request = await httpClient
.getUrl(Uri.parse(url))
.timeout(Duration(seconds: 10));
FTRUMManager().startResource(key);
response = await request.close();
} finally {
Map<String, dynamic> requestHeader = {};
Map<String, dynamic> responseHeader = {};
request!.headers.forEach((name, values) {
requestHeader[name] = values;
});
var responseBody = "";
if (response != null) {
response.headers.forEach((name, values) {
responseHeader[name] = values;
});
responseBody = await response.transform(Utf8Decoder()).join();
}
FTRUMManager().stopResource(key);
FTRUMManager().addResource(
key: key,
url: request.uri.toString(),
requestHeader: requestHeader,
httpMethod: request.method,
responseHeader: responseHeader,
resourceStatus: response?.statusCode,
responseBody: responseBody,
);
}
}
For http library and dio library usage, refer to example.
Logger Logging¶
Custom Log¶
Currently, the log content is limited to 30 KB, and the excess characters will be truncated
Usage¶
///Output log
///[content] log content
///[status] log status
///[property] additional property parameters (optional)
Future<void> logging(String content, FTLogStatus status, {Map<String, String>? property})
Code Example¶
Log Levels¶
Method Name | Meaning |
---|---|
FTLogStatus.info | Info |
FTLogStatus.warning | Warning |
FTLogStatus.error | Error |
FTLogStatus.critical | Critical |
FTLogStatus.ok | OK |
Tracer Network Trace¶
Auto Collection¶
Enable enableAutoTrace
through configuration FTTracer().setConfig
.
Custom Tracer¶
Usage¶
/// Get trace http request header data
/// [key] unique id
/// [url] request URL
///
Future<Map<String, String>> getTraceHeader(String url, {String? key})
Code Example¶
/// Use httpClient
void httpClientGetHttp() async {
var url = 'http://reqeust.url.cn';
var httpClient = new HttpClient();
String key = DateTime.now().millisecondsSinceEpoch.toString() + url;
var errorMessage = "";
HttpClientRequest request = await httpClient.getUrl(Uri.parse(url));
HttpClientResponse? response;
try {
final traceHeaders =
await FTTracer().getTraceHeader(key, request.uri.toString());
traceHeaders.forEach((key, value) {
request.headers.add(key, value);
});
response = await request.close();
} catch (exception) {
errorMessage = exception.toString();
} finally {
Map<String, dynamic> requestHeader = {};
Map<String, dynamic> responseHeader = {};
request.headers.forEach((name, values) {
requestHeader[name] = values;
});
if (response != null) {
response.headers.forEach((name, values) {
responseHeader[name] = values;
});
}
}
}
For http library and dio library usage, refer to example.
User Data Binding and Unbinding¶
FTMobileFlutter¶
Usage¶
///Bind user
///
///[userid] user id
///[userName] user name
///[userEmail] user email
///[userExt] extended data
static Future<void> bindRUMUserData(String userId,
{String? userName, String? userEmail, Map<String, String>? ext})
///Unbind user
static Future<void> unbindRUMUserData()
Code Example¶
Active Data Sync¶
FTMobileFlutter¶
WebView Data Monitoring¶
WebView data monitoring requires integrating Web Monitoring SDK in the WebView access page
Data Masking¶
If you want to fully mask fields, it is recommended to use dataModifier
, which performs better. If you need detailed rule replacement, it is recommended to use lineDataModifier
await FTMobileFlutter.sdkConfig(
dataModifier: {"device_uuid":"xxx"},
lineDataModifier: {"view":{"view_name":"xxx"}}
)
Native and Flutter Hybrid Development¶
If your project is native development, with some pages or business processes implemented using Flutter, the SDK installation and initialization configuration methods are as follows:
- Installation: The installation method remains the same
- Initialization: Refer to iOS SDK Initialization Configuration and Android SDK Initialization Configuration for initialization configuration in the native project
- Flutter Configuration:
- View, Resource, Error use the same configuration method as pure Flutter projects
- Flutter Resource and Trace auto collection use the following configuration method