Flutter 应用接入¶
前置条件¶
注意:若您开通了 RUM Headless 服务,前置条件已自动帮您配置完成,直接接入应用即可。
- 安装 DataKit;
- 配置 RUM 采集器;
- DataKit 配置为公网可访问,并且安装 IP 地理信息库。
应用接入¶
当前 Flutter 版本暂只支持 Android 和 iOS 平台。登录观测云控制台,进入用户访问监测页面,点击左上角 新建应用,即可开始创建一个新的应用。
安装¶
Pub.Dev: ft_mobile_agent_flutter
源码地址:https://github.com/GuanceCloud/datakit-flutter
Demo 地址:https://github.com/GuanceCloud/datakit-flutter/example
在项目路径下,终端运行 Flutter 命令:
这将在包的 pubspec.yaml 中添加这样的一行(并运行一个隐式 flutter pub get ):
dependencies:
ft_mobile_agent_flutter: [lastest_version]
# flutter 2.0 兼容版本使用下面的引用方式
ft_mobile_agent_flutter:
git:
url: https://github.com/GuanceCloud/datakit-flutter.git
ref: [github_legacy_lastest_tag]
现在在您的 Dart 代码中,您可以使用:
Android 集成额外配置
- 配置 Gradle Plugin ft-plugin,采集 App 启动事件,以及 Android Native 原生相关事件(页面跳转、点击事件、Native 网络网络请求、WebView 数据)
- 自定义
Application
,并AndroidMainifest.xml
中声明使用,代码如下.
import io.flutter.app.FlutterApplication
/**
* 如果需要统计【启动次数】和【启动时间】需要在此处添加自定义 Application
*/
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 初始化¶
基础配置¶
void main() async {
WidgetsFlutterBinding.ensureInitialized();
//本地环境部署、Datakit 部署
await FTMobileFlutter.sdkConfig(
datakitUrl: datakitUrl
);
//使用公网 DataWay
await FTMobileFlutter.sdkConfig(
datawayUrl: datawayUrl,
cliToken: cliToken,
);
}
字段 | 类型 | 必须 | 说明 |
---|---|---|---|
datakitUrl | String | 是 | datakit 访问 URL 地址,例子:http://10.0.0.1:9529,端口默认 9529,安装 SDK 设备需能访问该地址。注意:datakit 和 dataway 配置两者二选一 |
datawayUrl | String | 是 | dataway 访问 URL 地址,例子:http://10.0.0.1:9528,端口默认 9528,安装 SDK 设备需能访问这地址。注意:datakit 和 dataway 配置两者二选一 |
cliToken | String | 是 | 认证 token, 需要与 datawayUrl 同时配置 |
debug | bool | 否 | 设置是否允许打印日志,默认 false |
env | String | 否 | 环境配置,默认 prod ,任意字符,建议使用单个单词,例如 test 等 |
envType | enum EnvType | 否 | 环境配置,默认 EnvType.prod 。注:env 与 envType 只需配置一个 |
serviceName | String | 否 | 服务名 |
RUM 配置¶
字段 | 类型 | 必须 | 说明 |
---|---|---|---|
androidAppId | String | 是 | appId,监测中申请 |
iOSAppId | String | 是 | appId,监测中申请 |
sampleRate | double | 否 | 采样率,取值范围 [0,1],0 表示不采集,1 表示全采集,默认值为 1。作用域为同一 session_id 下所有 View,Action,LongTask,Error 数据 |
enableUserResource | bool | 否 | 是否开启 http Resource 数据自动抓取,默认为 false ,这个是通过修改 HttpOverrides.global 来实现,如果项目有这方面需求需要继承 FTHttpOverrides ,并设置 enableAutoTrace 为 false |
enableNativeUserAction | bool | 否 | 是否进行 Native Action 追踪,原生系统 Button 点击事件,应用启动事件,默认为 false |
enableNativeUserView | bool | 否 | 是否进行 Native View 自动追踪,纯 Flutter 应用建议关闭,,默认为 false |
enableNativeUserResource | bool | 否 | 是否进行 Native Resource 自动追踪,纯 Flutter 应用建议关闭,默认为 false |
errorMonitorType | enum ErrorMonitorType | 否 | 设置辅助监控信息,添加附加监控数据到 RUM Error 数据中,ErrorMonitorType.battery 为电池余量,ErrorMonitorType.memory 为内存用量,ErrorMonitorType.cpu 为 CPU 占有率 |
deviceMetricsMonitorType | enum DeviceMetricsMonitorType | 否 | 在 View 周期中,添加监控数据,DeviceMetricsMonitorType.battery 监控当前页的最高输出电流输出情况,DeviceMetricsMonitorType.memory 监控当前应用使用内存情况,DeviceMetricsMonitorType.cpu 监控 CPU 跳动次数 ,DeviceMetricsMonitorType.fps 监控屏幕帧率 |
globalContext | Map | 否 | 自定义全局参数 |
添加自定义标签¶
静态使用¶
- 拆分原有的 main.dart 为 2 个部分,一部分为 main(),一部分为 App() MaterialApp 组件;
- 建立对应各个环境的入口文件,如:main_prod.dart、main_gray.dart 等;
- 在对应的环境文件中进行自定义标签配置。例如:
///main_prod.dart
void main() async {
WidgetsFlutterBinding.ensureInitialized();
//初始化 SDK
await FTMobileFlutter.sdkConfig(
datakitUrl: serverUrl,
debug: true,
);
await FTRUMManager().setConfig(
androidAppId: appAndroidId,
iOSAppId: appIOSId,
globalContext: {CUSTOM_STATIC_TAG:"prod_static_tag"},
);
runApp(MyApp());
};
动态使用¶
- 通过存文件类型数据,例如
shared_preferences
库SharedPreferences
,配置使用SDK
,在配置处添加获取标签数据的代码。
final prefs = await SharedPreferences.getInstance();
String customDynamicValue = prefs.getString("customDynamicValue")?? "not set";
await FTRUMManager().setConfig(
androidAppId: appAndroidId,
iOSAppId: appIOSId,
globalContext: {CUSTOM_DYNAMIC_TAG:customDynamicValue},
//… 添加其他配置
);
- 在任意处添加改变文件数据的方法。
static Future<void> setDynamicParams(String value) async{
final prefs = await SharedPreferences.getInstance();
prefs.setString(CUSTOM_DYNAMIC_TAG, value);
}
- 最后重启应用。
注意:
- 特殊 key : track_id (用于追踪功能) 。
- 当用户通过 globalContext 添加自定义标签与 SDK 自有标签相同时,SDK 的标签会覆盖用户设置的,建议标签命名添加项目缩写的前缀,例如
df_tag_name
。项目中使用key
值可查询源码。
Log 配置¶
字段 | 类型 | 必须 | 说明 |
---|---|---|---|
sampleRate | double | 否 | 采样率,取值范围 [0,1],0 表示不采集,1 表示全采集,默认值为 1。 |
enableLinkRumData | bool | 否 | 是否与 RUM 关联 |
enableCustomLog | bool | 否 | 是否开启自定义日志 |
discardStrategy | enum FTLogCacheDiscard | 否 | 日志丢弃策略,默认FTLogCacheDiscard.discard |
logLevelFilters | List |
否 | 日志等级过滤 |
Trace 配置¶
await FTTracer().setConfig(
enableLinkRUMData: true,
enableAutoTrace:false,
enableNativeAutoTrace: false
);
字段 | 类型 | 必须 | 说明 |
---|---|---|---|
sampleRate | double | 否 | 采样率,取值范围 [0,1],0 表示不采集,1 表示全采集,默认值为 1。 |
traceType | enum TraceType | 否 | 链路类型,默认TraceType.ddTrace 。 |
enableLinkRUMData | bool | 否 | 是否与 RUM 数据关联,默认false 。 |
enableAutoTrace | bool | 否 | 是否 http 请求中添加 Trace Header ,默认false ,这个是通过修改 HttpOverrides.global 来实现,如果项目有这方面需求需要继承 FTHttpOverrides ,并设置 enableAutoTrace 为 false 。 |
enableNativeAutoTrace | bool | 否 | 是否开启原生网络自动追踪 iOS NSURLSession ,Android OKhttp ,默认false 。 |
RUM 用户数据追踪¶
Action¶
使用方法¶
/// 添加 action
/// [actionName] action 名称
/// [actionType] action 类型
/// [property] 附加属性参数(可选)
Future<void> startAction(String actionName, String actionType,
{Map<String, String>? property})
代码示例¶
View¶
自动采集¶
- 方法 1:
MaterialApp.navigatorObservers
添加FTRouteObserver
,设置MaterialApp.routes
需要跳转的页面,routes
中key
即为页面名称(view_name
)。
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: HomeRoute(),
navigatorObservers: [
//RUM View: 使用路由跳转时,监控页面生命周期
FTRouteObserver(),
],
routes: <String, WidgetBuilder>{
//set Route 路由跳转
'logging': (BuildContext context) => Logging(),
'rum': (BuildContext context) => RUM(),
'tracing_custom': (BuildContext context) => CustomTracing(),
'tracing_auto': (BuildContext context) => AutoTracing(),
},
);
}
}
//通过这种方式进行页面跳转,此处页面名称为 logging
Navigator.pushNamed(context, "logging");
- 方法 2:
MaterialApp.navigatorObservers
添加FTRouteObserver
,通过FTMaterialPageRoute
配合使用生成,其中widget
类名称即为页面名称(view_name
)。
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: HomeRoute(),
navigatorObservers: [
//RUM View: 使用路由跳转时,监控页面生命周期
FTRouteObserver(),
],
);
}
}
//此处“页面名称”为 NoRouteNamePage
Navigator.of(context).push(FTMaterialPageRoute(builder: (context) =>
new NoRouteNamePage()
- 方法 3:
MaterialApp.navigatorObservers
添加FTRouteObserver
,在Route
类型页面中自定义RouteSettings.name
属性,FTRouteObserver
的采集逻辑会优先获取RouteSettings.name
的赋值,这个方法同样适用 Dialog 类型页面,例如showDialog()
,showTimePicker()
等。
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: HomeRoute(),
navigatorObservers: [
//RUM View: 使用路由跳转时,监控页面生命周期
FTRouteObserver(),
],
);
}
}
//此处“页面名称”为 "RouteSettingName"
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => new NoRouteNamePage(),
settings: RouteSettings(name: "RouteSettingName"))
-
以上三种方法同时在一个项目中混合使用
-
休眠和唤醒事件采集 低于 0.5.1-pre.1 版本,如果需要采集应用休眠和唤醒行为需要添加如下代码:
class _HomeState extends State<HomeRoute> {
@override
void initState(){
//添加应用休眠和唤醒监听
FTLifeRecycleHandler().initObserver();
}
@override
void dispose(){
//移除应用休眠和唤醒监听
FTLifeRecycleHandler().removeObserver();
}
}
自动采集过滤¶
仅支持 0.5.0-pre.1 以上的版本
FTRouteObserver
MaterialApp(
navigatorObservers: [
// RUM View: routeFilter 过滤不需要参与监听的页面
FTRouteObserver(routeFilter: (Route? route, Route? previousRoute) {
if (filterConfig) {
//不采集
return true;
}
return false;
}),
])
字段 | 类型 | 必须 | 说明 |
---|---|---|---|
routeFilter | RouteFilter | 否 | 页面方法回调,可以根据进入和上一个 route 具体情况进行判断,返回 true 代表过滤符合条件的数据 ,反之则不过滤 |
FTDialogRouteFilterObserver
针对 DialogRoute
类型页面进行过滤,例如 showDialog()
,showTimePicker()
等。
MaterialApp(
navigatorObservers: [
//RUM View 过滤 DialogRoute 类型的组件
FTDialogRouteFilterObserver(filterOnlyNoSettingName: true)
])
// 这里的 Dialog 在 filterOnlyNoSettingName 为 true 的前提下会被采集。
// view_name 为 “About”
showAboutDialog(
context: context, routeSettings: RouteSettings(name: "About"));
字段 | 类型 | 必须 | 说明 |
---|---|---|---|
filterOnlyNoSettingName | bool | 否 | 仅过滤 RouteSettings.name 为 null 的 Route 页面 |
自定义 View¶
使用方法¶
/// view 创建,这个方法需要在 [starView] 之前被调用,目前 flutter route 中未有
/// [viewName] 界面名称
/// [duration]
Future<void> createView(String viewName, int duration)
/// view 开始
/// [viewName] 界面名称
/// [viewReferer] 前一个界面名称
/// [property] 附加属性参数(可选)
Future<void> starView(String viewName, {Map<String, String>? property})
/// view 结束
/// [property] 附加属性参数(可选)
Future<void> stopView({Map<String, String>? property})
代码示例¶
FTRUMManager().createView("Current Page Name", 100000000)
FTRUMManager().starView("Current Page Name");
FTRUMManager().stopView();
Error¶
自动采集¶
void main() async {
runZonedGuarded(() async {
WidgetsFlutterBinding.ensureInitialized();
await FTMobileFlutter.sdkConfig(
datakitUrl: serverUrl,
debug: true,
);
await FTRUMManager().setConfig(
androidAppId: appAndroidId,
iOSAppId: appIOSId,
);
// Flutter 异常捕获
FlutterError.onError = FTRUMManager().addFlutterError;
runApp(MyApp());
}, (Object error, StackTrace stack) {
//添加 Error 数据
FTRUMManager().addError(error, stack);
});
自定义 Error¶
使用方法¶
///添加自定义错误
/// [stack] 堆栈日志
/// [message] 错误信息
/// [appState] 应用状态
/// [errorType] 自定义 errorType
/// [property] 附加属性参数(可选)
Future<void> addCustomError(String stack, String message,
{Map<String, String>? property, String? errorType})
代码示例¶
Resource¶
自动采集¶
通过配置 FTRUMManager().setConfig
开启 enableUserResource
来实现。
自定义 Resource¶
使用方法¶
///开始资源请求
/// [key] 唯一 id
/// [property] 附加属性参数(可选)
Future<void> startResource(String key, {Map<String, String>? property})
///结束资源请求
/// [key] 唯一 id
/// [property] 附加属性参数(可选)
Future<void> stopResource(String key, {Map<String, String>? property})
/// 发送资源数据指标
/// [key] 唯一 id
/// [url] 请求地址
/// [httpMethod] 请求方法
/// [requestHeader] 请求头参数
/// [responseHeader] 返回头参数
/// [responseBody] 返回内容
/// [resourceStatus] 返回状态码
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})
代码示例¶
/// 使用 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,
);
}
}
使用 http 库与 dio 库,可参考 example。
Logger 日志打印¶
自定义日志¶
目前日志内容限制为 30 KB,字符超出部分会进行截断处理
使用方法¶
///输出日志
///[content] 日志内容
///[status] 日志状态
///[property] 附加属性参数(可选)
Future<void> logging(String content, FTLogStatus status, {Map<String, String>? property})
代码示例¶
日志等级¶
方法名 | 含义 |
---|---|
FTLogStatus.info | 提示 |
FTLogStatus.warning | 警告 |
FTLogStatus.error | 错误 |
FTLogStatus.critical | 严重 |
FTLogStatus.ok | 恢复 |
Tracer 网络链路追踪¶
自动采集¶
通过配置 FTTracer().setConfig
开启 enableAutoTrace
来实现。
自定义 Tracer¶
使用方法¶
/// 获取 trace http 请求头数据
/// [key] 唯一 id
/// [url] 请求地址
///
Future<Map<String, String>> getTraceHeader(String url, {String? key})
代码示例¶
/// 使用 httpClient
void httpClientGetHttp() async {
var url = 'http://www.google.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;
});
}
}
}
使用 http 库与 dio 库,可参考 example。
用户信息绑定与解绑¶
FTMobileFlutter¶
使用方法¶
///绑定用户
///
///[userid] 用户 id
///[userName] 用户名
///[userEmail] 用户邮箱
///[userExt] 扩展数据
static Future<void> bindRUMUserData(String userId,
{String? userName, String? userEmail, Map<String, String>? ext})
///解绑用户
static Future<void> unbindRUMUserData()
代码示例¶
WebView 数据监测¶
WebView 数据监测,需要在 WebView 访问页面集成Web 监测 SDK