Committed by
GitHub
Merge pull request #2 from epoll-j/feat/request-listener
Feat/request listener
Showing
22 changed files
with
658 additions
and
137 deletions
| @@ -2,7 +2,7 @@ | @@ -2,7 +2,7 @@ | ||
| 2 | 2 | ||
| 3 | > Flutter全埋点插件,支持 Android 和 iOS | 3 | > Flutter全埋点插件,支持 Android 和 iOS |
| 4 | 4 | ||
| 5 | -低侵入全局自动埋点,自动记录页面进入、退出,点击、滑动等事件,并支持自定义事件。 | 5 | +低侵入全局自动埋点,自动记录页面进入、退出,点击、滑动、http请求等事件,并支持自定义事件。 |
| 6 | 6 | ||
| 7 | 7 | ||
| 8 | ## Getting Started 使用指南 | 8 | ## Getting Started 使用指南 |
| @@ -69,6 +69,7 @@ class _MyAppState extends State<MyApp> { | @@ -69,6 +69,7 @@ class _MyAppState extends State<MyApp> { | ||
| 69 | .enableClick() // 启用点击统计 | 69 | .enableClick() // 启用点击统计 |
| 70 | .enableDrag() // 启用滑动统计 | 70 | .enableDrag() // 启用滑动统计 |
| 71 | .enableIgnoreNullKey() // 忽略空的key,如果不忽略,没有配置key的页面或事件会基于一定的规则生成一个随机的key进行上报统计 | 71 | .enableIgnoreNullKey() // 忽略空的key,如果不忽略,没有配置key的页面或事件会基于一定的规则生成一个随机的key进行上报统计 |
| 72 | + .enableHttpRequest() // 启用http请求监听 | ||
| 72 | .enableLog(); // 启用日志,建议在debug模式下开启,会打印一些埋点相关的日志 | 73 | .enableLog(); // 启用日志,建议在debug模式下开启,会打印一些埋点相关的日志 |
| 73 | 74 | ||
| 74 | super.initState(); | 75 | super.initState(); |
| @@ -2,7 +2,7 @@ library autotrack; | @@ -2,7 +2,7 @@ library autotrack; | ||
| 2 | 2 | ||
| 3 | export './auto_track/index.dart'; | 3 | export './auto_track/index.dart'; |
| 4 | export './auto_track/config/config.dart'; | 4 | export './auto_track/config/config.dart'; |
| 5 | -export './auto_track/page_view/navigation_observer.dart'; | ||
| 6 | -export './auto_track/click/navigator_key.dart'; | ||
| 7 | -export './auto_track/click/element_key.dart'; | 5 | +export './auto_track/listener/page_view/navigation_observer.dart'; |
| 6 | +export './auto_track/listener/click/navigator_key.dart'; | ||
| 7 | +export './auto_track/listener/click/element_key.dart'; | ||
| 8 | export './auto_track/log/logger.dart'; | 8 | export './auto_track/log/logger.dart'; |
| @@ -29,9 +29,12 @@ class AutoTrackConfig { | @@ -29,9 +29,12 @@ class AutoTrackConfig { | ||
| 29 | this.enableClick = true, // 监听点击事件 | 29 | this.enableClick = true, // 监听点击事件 |
| 30 | this.enableDrag = false, // 监听拖拽事件 | 30 | this.enableDrag = false, // 监听拖拽事件 |
| 31 | this.enableIgnoreNullKey = false, // 忽略空key事件 | 31 | this.enableIgnoreNullKey = false, // 忽略空key事件 |
| 32 | + this.httpRequestConfig, | ||
| 32 | }) { | 33 | }) { |
| 33 | trackId ??= const Uuid().v4().replaceAll('-', ''); | 34 | trackId ??= const Uuid().v4().replaceAll('-', ''); |
| 34 | - signature ??= (t) => sha256.convert(utf8.encode('$appKey$t$appSecret')).toString(); | 35 | + signature ??= |
| 36 | + (t) => sha256.convert(utf8.encode('$appKey$t$appSecret')).toString(); | ||
| 37 | + httpRequestConfig ??= HttpRequestConfig(); | ||
| 35 | } | 38 | } |
| 36 | 39 | ||
| 37 | String? host; | 40 | String? host; |
| @@ -76,18 +79,68 @@ class AutoTrackConfig { | @@ -76,18 +79,68 @@ class AutoTrackConfig { | ||
| 76 | bool enableDrag; | 79 | bool enableDrag; |
| 77 | 80 | ||
| 78 | bool enableIgnoreNullKey; | 81 | bool enableIgnoreNullKey; |
| 82 | + | ||
| 83 | + HttpRequestConfig? httpRequestConfig; | ||
| 84 | + | ||
| 85 | + copyWith({ | ||
| 86 | + String? host, | ||
| 87 | + String? appKey, | ||
| 88 | + String? appSecret, | ||
| 89 | + String? trackId, | ||
| 90 | + String? userId, | ||
| 91 | + String? uniqueId, | ||
| 92 | + double? samplingRate, | ||
| 93 | + int? uploadInterval, | ||
| 94 | + Function? signature, | ||
| 95 | + EventHandlerFunc? eventHandler, | ||
| 96 | + List<AutoTrackPageConfig>? pageConfigs, | ||
| 97 | + bool? useCustomRoute, | ||
| 98 | + List<Key>? ignoreElementKeys, | ||
| 99 | + List<String>? ignoreElementStringKeys, | ||
| 100 | + bool? enablePageView, | ||
| 101 | + bool? enablePageLeave, | ||
| 102 | + bool? enableClick, | ||
| 103 | + bool? enableUpload, | ||
| 104 | + bool? enableDrag, | ||
| 105 | + bool? enableIgnoreNullKey, | ||
| 106 | + HttpRequestConfig? httpRequestConfig | ||
| 107 | + }) { | ||
| 108 | + return AutoTrackConfig( | ||
| 109 | + host: host ?? this.host, | ||
| 110 | + appKey: appKey ?? this.appKey, | ||
| 111 | + appSecret: appSecret ?? this.appSecret, | ||
| 112 | + trackId: trackId ?? this.trackId, | ||
| 113 | + userId: userId ?? this.userId, | ||
| 114 | + uniqueId: uniqueId ?? this.uniqueId, | ||
| 115 | + samplingRate: samplingRate ?? this.samplingRate, | ||
| 116 | + uploadInterval: uploadInterval ?? this.uploadInterval, | ||
| 117 | + signature: signature ?? this.signature, | ||
| 118 | + eventHandler: eventHandler ?? this.eventHandler, | ||
| 119 | + pageConfigs: pageConfigs ?? this.pageConfigs, | ||
| 120 | + useCustomRoute: useCustomRoute ?? this.useCustomRoute, | ||
| 121 | + ignoreElementKeys: ignoreElementKeys ?? this.ignoreElementKeys, | ||
| 122 | + ignoreElementStringKeys: | ||
| 123 | + ignoreElementStringKeys ?? this.ignoreElementStringKeys, | ||
| 124 | + enablePageView: enablePageView ?? this.enablePageView, | ||
| 125 | + enablePageLeave: enablePageLeave ?? this.enablePageLeave, | ||
| 126 | + enableClick: enableClick ?? this.enableClick, | ||
| 127 | + enableUpload: enableUpload ?? this.enableUpload, | ||
| 128 | + enableDrag: enableDrag ?? this.enableDrag, | ||
| 129 | + enableIgnoreNullKey: enableIgnoreNullKey ?? this.enableIgnoreNullKey, | ||
| 130 | + httpRequestConfig: httpRequestConfig ?? this.httpRequestConfig | ||
| 131 | + ); | ||
| 132 | + } | ||
| 79 | } | 133 | } |
| 80 | 134 | ||
| 81 | typedef PageWidgetFunc = bool Function(Widget); | 135 | typedef PageWidgetFunc = bool Function(Widget); |
| 82 | 136 | ||
| 83 | class AutoTrackPageConfig<T extends Widget> { | 137 | class AutoTrackPageConfig<T extends Widget> { |
| 84 | - AutoTrackPageConfig({ | ||
| 85 | - this.pageID, | ||
| 86 | - this.pagePath, | ||
| 87 | - this.ignore = false, | ||
| 88 | - this.pageTitle, | ||
| 89 | - this.isPageWidget | ||
| 90 | - }) { | 138 | + AutoTrackPageConfig( |
| 139 | + {this.pageID, | ||
| 140 | + this.pagePath, | ||
| 141 | + this.ignore = false, | ||
| 142 | + this.pageTitle, | ||
| 143 | + this.isPageWidget}) { | ||
| 91 | isPageWidget ??= (pageWidget) => pageWidget is T; | 144 | isPageWidget ??= (pageWidget) => pageWidget is T; |
| 92 | } | 145 | } |
| 93 | 146 | ||
| @@ -97,3 +150,13 @@ class AutoTrackPageConfig<T extends Widget> { | @@ -97,3 +150,13 @@ class AutoTrackPageConfig<T extends Widget> { | ||
| 97 | String? pageTitle; | 150 | String? pageTitle; |
| 98 | PageWidgetFunc? isPageWidget; | 151 | PageWidgetFunc? isPageWidget; |
| 99 | } | 152 | } |
| 153 | + | ||
| 154 | +class HttpRequestConfig { | ||
| 155 | + bool ignoreRequestHeader; | ||
| 156 | + bool ignoreResponseHeader; | ||
| 157 | + | ||
| 158 | + HttpRequestConfig({ | ||
| 159 | + this.ignoreRequestHeader = false, | ||
| 160 | + this.ignoreResponseHeader = false, | ||
| 161 | + }); | ||
| 162 | +} |
| @@ -8,6 +8,8 @@ import 'package:package_info_plus/package_info_plus.dart'; | @@ -8,6 +8,8 @@ import 'package:package_info_plus/package_info_plus.dart'; | ||
| 8 | 8 | ||
| 9 | import 'config.dart'; | 9 | import 'config.dart'; |
| 10 | 10 | ||
| 11 | +typedef UpdateConfigFunc = AutoTrackConfig Function(AutoTrackConfig); | ||
| 12 | + | ||
| 11 | class AutoTrackConfigManager { | 13 | class AutoTrackConfigManager { |
| 12 | static final AutoTrackConfigManager instance = AutoTrackConfigManager._(); | 14 | static final AutoTrackConfigManager instance = AutoTrackConfigManager._(); |
| 13 | 15 | ||
| @@ -36,10 +38,16 @@ class AutoTrackConfigManager { | @@ -36,10 +38,16 @@ class AutoTrackConfigManager { | ||
| 36 | bool _autoTrackEnable = false; | 38 | bool _autoTrackEnable = false; |
| 37 | bool get autoTrackEnable => _autoTrackEnable; | 39 | bool get autoTrackEnable => _autoTrackEnable; |
| 38 | 40 | ||
| 39 | - void updateConfig(AutoTrackConfig config) { | ||
| 40 | - _config = config; | 41 | + void setConfig(AutoTrackConfig config) { |
| 42 | + updateConfig((old) { | ||
| 43 | + return config; | ||
| 44 | + }); | ||
| 41 | _updateDeviceId(); | 45 | _updateDeviceId(); |
| 42 | - if (config.enableUpload) { | 46 | + } |
| 47 | + | ||
| 48 | + void updateConfig(UpdateConfigFunc func) { | ||
| 49 | + _config = func(_config); | ||
| 50 | + if (_config.enableUpload) { | ||
| 43 | AutoTrackQueue.instance.start(); | 51 | AutoTrackQueue.instance.start(); |
| 44 | } else { | 52 | } else { |
| 45 | AutoTrackQueue.instance.stop(); | 53 | AutoTrackQueue.instance.stop(); |
| @@ -58,59 +66,10 @@ class AutoTrackConfigManager { | @@ -58,59 +66,10 @@ class AutoTrackConfigManager { | ||
| 58 | } | 66 | } |
| 59 | } | 67 | } |
| 60 | 68 | ||
| 61 | - void updateUserId(String userId) { | ||
| 62 | - _config.userId = userId; | ||
| 63 | - } | ||
| 64 | - | ||
| 65 | - void updateSampleRate(double rate) { | ||
| 66 | - _config.samplingRate = rate; | ||
| 67 | - } | ||
| 68 | - | ||
| 69 | - void updatePageConfigs(List<AutoTrackPageConfig> pageConfigs) { | ||
| 70 | - _config.pageConfigs = pageConfigs; | ||
| 71 | - } | ||
| 72 | - | ||
| 73 | - void updateIgnoreElementKeys(List<Key> ignoreElementKeys) { | ||
| 74 | - _config.ignoreElementKeys = ignoreElementKeys; | ||
| 75 | - } | ||
| 76 | - | ||
| 77 | - void updateIgnoreElementStringKeys(List<String> ignoreElementStringKeys) { | ||
| 78 | - _config.ignoreElementStringKeys = ignoreElementStringKeys; | ||
| 79 | - } | ||
| 80 | - | ||
| 81 | - void enablePageView(bool enable) { | ||
| 82 | - _config.enablePageView = enable; | ||
| 83 | - } | ||
| 84 | - | ||
| 85 | - void enablePageLeave(bool enable) { | ||
| 86 | - _config.enablePageLeave = enable; | ||
| 87 | - } | ||
| 88 | - | ||
| 89 | - void enableClick(bool enable) { | ||
| 90 | - _config.enableClick = enable; | ||
| 91 | - } | ||
| 92 | - | ||
| 93 | - void enableDrag(bool enable) { | ||
| 94 | - _config.enableDrag = enable; | ||
| 95 | - } | ||
| 96 | - | ||
| 97 | void enableAutoTrack(bool enable) { | 69 | void enableAutoTrack(bool enable) { |
| 98 | _autoTrackEnable = enable; | 70 | _autoTrackEnable = enable; |
| 99 | } | 71 | } |
| 100 | 72 | ||
| 101 | - void enableUpload(bool enable) { | ||
| 102 | - _config.enableUpload = enable; | ||
| 103 | - if (enable) { | ||
| 104 | - AutoTrackQueue.instance.start(); | ||
| 105 | - } else { | ||
| 106 | - AutoTrackQueue.instance.stop(); | ||
| 107 | - } | ||
| 108 | - } | ||
| 109 | - | ||
| 110 | - void enableIgnoreNullKey(bool enable) { | ||
| 111 | - _config.enableIgnoreNullKey = enable; | ||
| 112 | - } | ||
| 113 | - | ||
| 114 | List<AutoTrackPageConfig> get pageConfigs => _config.pageConfigs; | 73 | List<AutoTrackPageConfig> get pageConfigs => _config.pageConfigs; |
| 115 | 74 | ||
| 116 | bool get useCustomRoute => _config.useCustomRoute; | 75 | bool get useCustomRoute => _config.useCustomRoute; |
| 1 | import 'dart:async'; | 1 | import 'dart:async'; |
| 2 | +import 'dart:convert'; | ||
| 3 | +import 'dart:io'; | ||
| 2 | import 'dart:math'; | 4 | import 'dart:math'; |
| 3 | 5 | ||
| 4 | import 'package:auto_track/auto_track/config/manager.dart'; | 6 | import 'package:auto_track/auto_track/config/manager.dart'; |
| 5 | import 'package:auto_track/auto_track/utils/track_model.dart'; | 7 | import 'package:auto_track/auto_track/utils/track_model.dart'; |
| 6 | -import 'package:dio/dio.dart'; | ||
| 7 | 8 | ||
| 8 | import '../log/logger.dart'; | 9 | import '../log/logger.dart'; |
| 9 | 10 | ||
| 10 | - | ||
| 11 | - | ||
| 12 | class AutoTrackQueue { | 11 | class AutoTrackQueue { |
| 13 | static final AutoTrackQueue instance = AutoTrackQueue._(); | 12 | static final AutoTrackQueue instance = AutoTrackQueue._(); |
| 14 | - AutoTrackQueue._(); | 13 | + AutoTrackQueue._() { |
| 14 | + httpClient.badCertificateCallback = | ||
| 15 | + (X509Certificate cert, String host, int port) => true; | ||
| 16 | + } | ||
| 15 | 17 | ||
| 16 | Timer? _timer; | 18 | Timer? _timer; |
| 17 | final List<TrackModel> _queue = []; | 19 | final List<TrackModel> _queue = []; |
| 18 | - final dio = Dio(); | 20 | + final httpClient = HttpClient(); |
| 19 | 21 | ||
| 20 | void appendQueue(TrackModel model) { | 22 | void appendQueue(TrackModel model) { |
| 21 | if (_timer == null) return; | 23 | if (_timer == null) return; |
| @@ -24,7 +26,10 @@ class AutoTrackQueue { | @@ -24,7 +26,10 @@ class AutoTrackQueue { | ||
| 24 | 26 | ||
| 25 | void start() { | 27 | void start() { |
| 26 | if (_timer != null) return; | 28 | if (_timer != null) return; |
| 27 | - _timer = Timer.periodic(Duration(seconds: AutoTrackConfigManager.instance.config.uploadInterval ?? 10), (timer) { | 29 | + _timer = Timer.periodic( |
| 30 | + Duration( | ||
| 31 | + seconds: AutoTrackConfigManager.instance.config.uploadInterval ?? | ||
| 32 | + 10), (timer) { | ||
| 28 | flush(); | 33 | flush(); |
| 29 | }); | 34 | }); |
| 30 | } | 35 | } |
| @@ -48,21 +53,29 @@ class AutoTrackQueue { | @@ -48,21 +53,29 @@ class AutoTrackQueue { | ||
| 48 | } | 53 | } |
| 49 | if (host != null) { | 54 | if (host != null) { |
| 50 | final t = DateTime.now().millisecondsSinceEpoch; | 55 | final t = DateTime.now().millisecondsSinceEpoch; |
| 51 | - dio.post(host, data: { | ||
| 52 | - 'app_key': config.appKey ?? '', | ||
| 53 | - 'signature': config.signature!(t), | ||
| 54 | - 't': t, | ||
| 55 | - 'user_id': config.userId ?? '', | ||
| 56 | - 'track_id': config.trackId ?? '', | ||
| 57 | - 'unique_id': config.uniqueId ?? AutoTrackConfigManager.instance.deviceId, | ||
| 58 | - 'device_id': AutoTrackConfigManager.instance.deviceId, | ||
| 59 | - 'data_list': uploadList.map((e) => e.toMap()).toList(), | ||
| 60 | - 'app_version': AutoTrackConfigManager.instance.appVersion, | ||
| 61 | - 'device_info': AutoTrackConfigManager.instance.deviceInfo | ||
| 62 | - }).onError((error, stackTrace) { | ||
| 63 | - AutoTrackLogger.getInstance().error(error!); | ||
| 64 | - return Future.value(Response(statusCode: 500, requestOptions: RequestOptions(path: host))); | 56 | + |
| 57 | + httpClient.postUrl(Uri.parse(host)).then((request) { | ||
| 58 | + request.headers.contentType = ContentType.json; | ||
| 59 | + request.write(json.encode({ | ||
| 60 | + 'app_key': config.appKey ?? '', | ||
| 61 | + 'signature': config.signature!(t), | ||
| 62 | + 't': t, | ||
| 63 | + 'user_id': config.userId ?? '', | ||
| 64 | + 'track_id': config.trackId ?? '', | ||
| 65 | + 'unique_id': | ||
| 66 | + config.uniqueId ?? AutoTrackConfigManager.instance.deviceId, | ||
| 67 | + 'device_id': AutoTrackConfigManager.instance.deviceId, | ||
| 68 | + 'data_list': uploadList.map((e) => e.toMap()).toList(), | ||
| 69 | + 'app_version': AutoTrackConfigManager.instance.appVersion, | ||
| 70 | + 'device_info': AutoTrackConfigManager.instance.deviceInfo | ||
| 71 | + })); | ||
| 72 | + return request.close(); | ||
| 73 | + }).then((response) { | ||
| 74 | + AutoTrackLogger.getInstance() | ||
| 75 | + .debug('upload status => ${response.statusCode}'); | ||
| 76 | + }).catchError((error) { | ||
| 77 | + AutoTrackLogger.getInstance().error(error); | ||
| 65 | }); | 78 | }); |
| 66 | } | 79 | } |
| 67 | } | 80 | } |
| 68 | -} | ||
| 81 | +} |
| 1 | -import 'package:auto_track/auto_track/drag/drag_pointer_event_listener.dart'; | 1 | +import 'dart:io'; |
| 2 | + | ||
| 2 | import 'package:flutter/foundation.dart'; | 3 | import 'package:flutter/foundation.dart'; |
| 3 | 4 | ||
| 4 | -import 'click/pointer_event_listener.dart'; | ||
| 5 | import 'config/config.dart'; | 5 | import 'config/config.dart'; |
| 6 | import 'config/manager.dart'; | 6 | import 'config/manager.dart'; |
| 7 | +import 'listener/click/pointer_event_listener.dart'; | ||
| 8 | +import 'listener/drag/drag_pointer_event_listener.dart'; | ||
| 9 | +import 'listener/request/request_listener.dart'; | ||
| 7 | import 'log/logger.dart'; | 10 | import 'log/logger.dart'; |
| 8 | 11 | ||
| 9 | class AutoTrack { | 12 | class AutoTrack { |
| @@ -16,98 +19,132 @@ class AutoTrack { | @@ -16,98 +19,132 @@ class AutoTrack { | ||
| 16 | } | 19 | } |
| 17 | 20 | ||
| 18 | void updateUserId(String id) { | 21 | void updateUserId(String id) { |
| 19 | - AutoTrackConfigManager.instance.updateUserId(id); | 22 | + AutoTrackConfigManager.instance.updateConfig((config) { |
| 23 | + return config.copyWith(userId: id); | ||
| 24 | + }); | ||
| 20 | } | 25 | } |
| 21 | 26 | ||
| 22 | void updateSampleRate(double rate) { | 27 | void updateSampleRate(double rate) { |
| 23 | - AutoTrackConfigManager.instance.updateSampleRate(rate); | 28 | + AutoTrackConfigManager.instance.updateConfig((config) { |
| 29 | + return config.copyWith(samplingRate: rate); | ||
| 30 | + }); | ||
| 24 | } | 31 | } |
| 25 | 32 | ||
| 26 | AutoTrack config(AutoTrackConfig? config) { | 33 | AutoTrack config(AutoTrackConfig? config) { |
| 27 | if (config != null) { | 34 | if (config != null) { |
| 28 | - AutoTrackConfigManager.instance.updateConfig(config); | 35 | + AutoTrackConfigManager.instance.setConfig(config); |
| 29 | } | 36 | } |
| 30 | return _instance; | 37 | return _instance; |
| 31 | } | 38 | } |
| 32 | 39 | ||
| 33 | AutoTrack pageConfigs(List<AutoTrackPageConfig>? pageConfigs) { | 40 | AutoTrack pageConfigs(List<AutoTrackPageConfig>? pageConfigs) { |
| 34 | if (pageConfigs != null) { | 41 | if (pageConfigs != null) { |
| 35 | - AutoTrackConfigManager.instance.updatePageConfigs(pageConfigs); | 42 | + AutoTrackConfigManager.instance.updateConfig((config) { |
| 43 | + return config.copyWith(pageConfigs: pageConfigs); | ||
| 44 | + }); | ||
| 36 | } | 45 | } |
| 37 | return _instance; | 46 | return _instance; |
| 38 | } | 47 | } |
| 39 | 48 | ||
| 40 | AutoTrack ignoreElementKeys(List<Key>? ignoreElementKeys) { | 49 | AutoTrack ignoreElementKeys(List<Key>? ignoreElementKeys) { |
| 41 | if (ignoreElementKeys != null) { | 50 | if (ignoreElementKeys != null) { |
| 42 | - AutoTrackConfigManager.instance.updateIgnoreElementKeys(ignoreElementKeys); | 51 | + AutoTrackConfigManager.instance.updateConfig((config) { |
| 52 | + return config.copyWith(ignoreElementKeys: ignoreElementKeys); | ||
| 53 | + }); | ||
| 43 | } | 54 | } |
| 44 | return _instance; | 55 | return _instance; |
| 45 | } | 56 | } |
| 46 | 57 | ||
| 47 | AutoTrack ignoreElementStringKeys(List<String>? ignoreElementStringKeys) { | 58 | AutoTrack ignoreElementStringKeys(List<String>? ignoreElementStringKeys) { |
| 48 | if (ignoreElementStringKeys != null) { | 59 | if (ignoreElementStringKeys != null) { |
| 49 | - AutoTrackConfigManager.instance.updateIgnoreElementStringKeys(ignoreElementStringKeys); | 60 | + AutoTrackConfigManager.instance.updateConfig((config) { |
| 61 | + return config.copyWith(ignoreElementStringKeys: ignoreElementStringKeys); | ||
| 62 | + }); | ||
| 50 | } | 63 | } |
| 51 | return _instance; | 64 | return _instance; |
| 52 | } | 65 | } |
| 53 | 66 | ||
| 54 | AutoTrack enablePageView() { | 67 | AutoTrack enablePageView() { |
| 55 | - AutoTrackConfigManager.instance.enablePageView(true); | 68 | + AutoTrackConfigManager.instance.updateConfig((config) { |
| 69 | + return config.copyWith(enablePageView: true); | ||
| 70 | + }); | ||
| 56 | return _instance; | 71 | return _instance; |
| 57 | } | 72 | } |
| 58 | 73 | ||
| 59 | AutoTrack disablePageView() { | 74 | AutoTrack disablePageView() { |
| 60 | - AutoTrackConfigManager.instance.enablePageView(false); | 75 | + AutoTrackConfigManager.instance.updateConfig((config) { |
| 76 | + return config.copyWith(enablePageView: false); | ||
| 77 | + }); | ||
| 61 | return _instance; | 78 | return _instance; |
| 62 | } | 79 | } |
| 63 | 80 | ||
| 64 | AutoTrack enablePageLeave() { | 81 | AutoTrack enablePageLeave() { |
| 65 | - AutoTrackConfigManager.instance.enablePageLeave(true); | 82 | + AutoTrackConfigManager.instance.updateConfig((config) { |
| 83 | + return config.copyWith(enablePageLeave: true); | ||
| 84 | + }); | ||
| 66 | return _instance; | 85 | return _instance; |
| 67 | } | 86 | } |
| 68 | 87 | ||
| 69 | AutoTrack disablePageLeave() { | 88 | AutoTrack disablePageLeave() { |
| 70 | - AutoTrackConfigManager.instance.enablePageLeave(false); | 89 | + AutoTrackConfigManager.instance.updateConfig((config) { |
| 90 | + return config.copyWith(enablePageLeave: false); | ||
| 91 | + }); | ||
| 71 | return _instance; | 92 | return _instance; |
| 72 | } | 93 | } |
| 73 | 94 | ||
| 74 | AutoTrack enableIgnoreNullKey() { | 95 | AutoTrack enableIgnoreNullKey() { |
| 75 | - AutoTrackConfigManager.instance.enableIgnoreNullKey(true); | 96 | + AutoTrackConfigManager.instance.updateConfig((config) { |
| 97 | + return config.copyWith(enableIgnoreNullKey: true); | ||
| 98 | + }); | ||
| 76 | return _instance; | 99 | return _instance; |
| 77 | } | 100 | } |
| 78 | 101 | ||
| 79 | AutoTrack disableIgnoreNullKey() { | 102 | AutoTrack disableIgnoreNullKey() { |
| 80 | - AutoTrackConfigManager.instance.enableIgnoreNullKey(false); | 103 | + AutoTrackConfigManager.instance.updateConfig((config) { |
| 104 | + return config.copyWith(enableIgnoreNullKey: false); | ||
| 105 | + }); | ||
| 81 | return _instance; | 106 | return _instance; |
| 82 | } | 107 | } |
| 83 | 108 | ||
| 84 | AutoTrack enableUpload() { | 109 | AutoTrack enableUpload() { |
| 85 | - AutoTrackConfigManager.instance.enableUpload(true); | 110 | + AutoTrackConfigManager.instance.updateConfig((config) { |
| 111 | + return config.copyWith(enableUpload: true); | ||
| 112 | + }); | ||
| 86 | return _instance; | 113 | return _instance; |
| 87 | } | 114 | } |
| 88 | 115 | ||
| 89 | AutoTrack disableUpload() { | 116 | AutoTrack disableUpload() { |
| 90 | - AutoTrackConfigManager.instance.enableUpload(false); | 117 | + AutoTrackConfigManager.instance.updateConfig((config) { |
| 118 | + return config.copyWith(enableUpload: false); | ||
| 119 | + }); | ||
| 91 | return _instance; | 120 | return _instance; |
| 92 | } | 121 | } |
| 93 | 122 | ||
| 94 | AutoTrack enableClick() { | 123 | AutoTrack enableClick() { |
| 95 | - AutoTrackConfigManager.instance.enableClick(true); | 124 | + AutoTrackConfigManager.instance.updateConfig((config) { |
| 125 | + return config.copyWith(enableClick: true); | ||
| 126 | + }); | ||
| 96 | return _instance; | 127 | return _instance; |
| 97 | } | 128 | } |
| 98 | 129 | ||
| 99 | - AutoTrack enableDrag() { | ||
| 100 | - AutoTrackConfigManager.instance.enableDrag(true); | 130 | + AutoTrack disableClick() { |
| 131 | + AutoTrackConfigManager.instance.updateConfig((config) { | ||
| 132 | + return config.copyWith(enableClick: false); | ||
| 133 | + }); | ||
| 101 | return _instance; | 134 | return _instance; |
| 102 | } | 135 | } |
| 103 | 136 | ||
| 104 | - AutoTrack disableDrag() { | ||
| 105 | - AutoTrackConfigManager.instance.enableDrag(true); | 137 | + AutoTrack enableDrag() { |
| 138 | + AutoTrackConfigManager.instance.updateConfig((config) { | ||
| 139 | + return config.copyWith(enableDrag: true); | ||
| 140 | + }); | ||
| 106 | return _instance; | 141 | return _instance; |
| 107 | } | 142 | } |
| 108 | 143 | ||
| 109 | - AutoTrack disableClick() { | ||
| 110 | - AutoTrackConfigManager.instance.enableClick(false); | 144 | + AutoTrack disableDrag() { |
| 145 | + AutoTrackConfigManager.instance.updateConfig((config) { | ||
| 146 | + return config.copyWith(enableDrag: false); | ||
| 147 | + }); | ||
| 111 | return _instance; | 148 | return _instance; |
| 112 | } | 149 | } |
| 113 | 150 | ||
| @@ -122,6 +159,7 @@ class AutoTrack { | @@ -122,6 +159,7 @@ class AutoTrack { | ||
| 122 | AutoTrackConfigManager.instance.enableAutoTrack(false); | 159 | AutoTrackConfigManager.instance.enableAutoTrack(false); |
| 123 | PointerEventListener.instance.stop(); | 160 | PointerEventListener.instance.stop(); |
| 124 | DragPointerEventListener.instance.stop(); | 161 | DragPointerEventListener.instance.stop(); |
| 162 | + disableHttpRequest(); | ||
| 125 | return _instance; | 163 | return _instance; |
| 126 | } | 164 | } |
| 127 | 165 | ||
| @@ -136,4 +174,14 @@ class AutoTrack { | @@ -136,4 +174,14 @@ class AutoTrack { | ||
| 136 | } | 174 | } |
| 137 | return _instance; | 175 | return _instance; |
| 138 | } | 176 | } |
| 177 | + | ||
| 178 | + AutoTrack enableHttpRequest() { | ||
| 179 | + HttpOverrides.global = AutoTrackHttpOverrides(HttpOverrides.current); | ||
| 180 | + return _instance; | ||
| 181 | + } | ||
| 182 | + | ||
| 183 | + AutoTrack disableHttpRequest() { | ||
| 184 | + HttpOverrides.global = null; | ||
| 185 | + return _instance; | ||
| 186 | + } | ||
| 139 | } | 187 | } |
| @@ -3,9 +3,9 @@ import 'dart:convert'; | @@ -3,9 +3,9 @@ import 'dart:convert'; | ||
| 3 | import 'package:crypto/crypto.dart'; | 3 | import 'package:crypto/crypto.dart'; |
| 4 | import 'package:flutter/widgets.dart'; | 4 | import 'package:flutter/widgets.dart'; |
| 5 | 5 | ||
| 6 | -import '../config/manager.dart'; | 6 | +import '../../config/manager.dart'; |
| 7 | import '../page_view/page_info.dart'; | 7 | import '../page_view/page_info.dart'; |
| 8 | -import '../utils/element_util.dart'; | 8 | +import '../../utils/element_util.dart'; |
| 9 | import 'element_key.dart'; | 9 | import 'element_key.dart'; |
| 10 | import 'xpath.dart'; | 10 | import 'xpath.dart'; |
| 11 | 11 |
| @@ -5,11 +5,11 @@ import 'package:flutter/rendering.dart'; | @@ -5,11 +5,11 @@ import 'package:flutter/rendering.dart'; | ||
| 5 | import 'package:flutter/widgets.dart'; | 5 | import 'package:flutter/widgets.dart'; |
| 6 | 6 | ||
| 7 | 7 | ||
| 8 | -import '../log/logger.dart'; | 8 | +import '../../log/logger.dart'; |
| 9 | import '../page_view/page_info.dart'; | 9 | import '../page_view/page_info.dart'; |
| 10 | import '../page_view/page_stack.dart'; | 10 | import '../page_view/page_stack.dart'; |
| 11 | -import '../track/track.dart'; | ||
| 12 | -import '../utils/element_util.dart'; | 11 | +import '../../track/track.dart'; |
| 12 | +import '../../utils/element_util.dart'; | ||
| 13 | import 'click_info.dart'; | 13 | import 'click_info.dart'; |
| 14 | 14 | ||
| 15 | class PointerEventListener { | 15 | class PointerEventListener { |
| 1 | -import 'package:auto_track/auto_track/drag/drag_info.dart'; | ||
| 2 | import 'package:auto_track/auto_track/track/track.dart'; | 1 | import 'package:auto_track/auto_track/track/track.dart'; |
| 3 | import 'package:flutter/gestures.dart'; | 2 | import 'package:flutter/gestures.dart'; |
| 4 | 3 | ||
| 5 | import '../page_view/page_stack.dart'; | 4 | import '../page_view/page_stack.dart'; |
| 5 | +import 'drag_info.dart'; | ||
| 6 | 6 | ||
| 7 | class DragPointerEventListener { | 7 | class DragPointerEventListener { |
| 8 | static final DragPointerEventListener instance = DragPointerEventListener._(); | 8 | static final DragPointerEventListener instance = DragPointerEventListener._(); |
| 1 | import 'package:flutter/material.dart'; | 1 | import 'package:flutter/material.dart'; |
| 2 | import 'package:flutter/scheduler.dart'; | 2 | import 'package:flutter/scheduler.dart'; |
| 3 | 3 | ||
| 4 | -import '../config/config.dart'; | ||
| 5 | -import '../config/manager.dart'; | ||
| 6 | -import '../log/logger.dart'; | ||
| 7 | -import '../utils/element_util.dart'; | 4 | +import '../../config/config.dart'; |
| 5 | +import '../../config/manager.dart'; | ||
| 6 | +import '../../log/logger.dart'; | ||
| 7 | +import '../../utils/element_util.dart'; | ||
| 8 | import 'page_stack.dart'; | 8 | import 'page_stack.dart'; |
| 9 | 9 | ||
| 10 | class AutoTrackNavigationObserver extends NavigatorObserver { | 10 | class AutoTrackNavigationObserver extends NavigatorObserver { |
| @@ -3,9 +3,9 @@ import 'dart:convert'; | @@ -3,9 +3,9 @@ import 'dart:convert'; | ||
| 3 | import 'package:crypto/crypto.dart'; | 3 | import 'package:crypto/crypto.dart'; |
| 4 | import 'package:flutter/material.dart'; | 4 | import 'package:flutter/material.dart'; |
| 5 | 5 | ||
| 6 | -import '../config/config.dart'; | ||
| 7 | -import '../config/manager.dart'; | ||
| 8 | -import '../utils/element_util.dart'; | 6 | +import '../../config/config.dart'; |
| 7 | +import '../../config/manager.dart'; | ||
| 8 | +import '../../utils/element_util.dart'; | ||
| 9 | 9 | ||
| 10 | class PageInfo { | 10 | class PageInfo { |
| 11 | PageInfo._(this.timer); | 11 | PageInfo._(this.timer); |
| @@ -3,7 +3,7 @@ import 'dart:core'; | @@ -3,7 +3,7 @@ import 'dart:core'; | ||
| 3 | 3 | ||
| 4 | import 'package:flutter/widgets.dart'; | 4 | import 'package:flutter/widgets.dart'; |
| 5 | 5 | ||
| 6 | -import '../track/track.dart'; | 6 | +import '../../track/track.dart'; |
| 7 | import 'page_info.dart'; | 7 | import 'page_info.dart'; |
| 8 | 8 | ||
| 9 | class PageStack with WidgetsBindingObserver { | 9 | class PageStack with WidgetsBindingObserver { |
| 1 | +import 'dart:async'; | ||
| 2 | +import 'dart:convert'; | ||
| 3 | +import 'dart:io'; | ||
| 4 | + | ||
| 5 | +import 'package:auto_track/auto_track/track/track.dart'; | ||
| 6 | + | ||
| 7 | +import '../../config/manager.dart'; | ||
| 8 | +import '../../utils/request_model.dart'; | ||
| 9 | +import '../page_view/page_stack.dart'; | ||
| 10 | + | ||
| 11 | +class HttpClientRequestWithChecker implements HttpClientRequest { | ||
| 12 | + final HttpClientRequest _realRequest; | ||
| 13 | + final Stopwatch _stopwatch; | ||
| 14 | + final Page? pageInfoData; | ||
| 15 | + | ||
| 16 | + HttpClientRequestWithChecker( | ||
| 17 | + this._realRequest, this._stopwatch, this.pageInfoData); | ||
| 18 | + | ||
| 19 | + @override | ||
| 20 | + bool get bufferOutput => _realRequest.bufferOutput; | ||
| 21 | + | ||
| 22 | + @override | ||
| 23 | + int get contentLength => _realRequest.contentLength; | ||
| 24 | + | ||
| 25 | + @override | ||
| 26 | + Encoding get encoding => _realRequest.encoding; | ||
| 27 | + | ||
| 28 | + @override | ||
| 29 | + bool get followRedirects => _realRequest.followRedirects; | ||
| 30 | + | ||
| 31 | + @override | ||
| 32 | + int get maxRedirects => _realRequest.maxRedirects; | ||
| 33 | + | ||
| 34 | + @override | ||
| 35 | + bool get persistentConnection => _realRequest.persistentConnection; | ||
| 36 | + | ||
| 37 | + @override | ||
| 38 | + void add(List<int> data) { | ||
| 39 | + _realRequest.add(data); | ||
| 40 | + } | ||
| 41 | + | ||
| 42 | + @override | ||
| 43 | + void addError(Object error, [StackTrace? stackTrace]) { | ||
| 44 | + _realRequest.addError(error, stackTrace); | ||
| 45 | + } | ||
| 46 | + | ||
| 47 | + @override | ||
| 48 | + Future addStream(Stream<List<int>> stream) { | ||
| 49 | + return _realRequest.addStream(stream); | ||
| 50 | + } | ||
| 51 | + | ||
| 52 | + @override | ||
| 53 | + Future<HttpClientResponse> close() async { | ||
| 54 | + return _realRequest.close().then((HttpClientResponse response) { | ||
| 55 | + _checkResponse(_realRequest, response); | ||
| 56 | + return response; | ||
| 57 | + }).catchError((dynamic error, dynamic stackTrace) {}, test: (error) { | ||
| 58 | + _stopwatch.stop(); | ||
| 59 | + String message; | ||
| 60 | + if (error is HttpException) { | ||
| 61 | + message = error.message; | ||
| 62 | + } else { | ||
| 63 | + message = error.toString(); | ||
| 64 | + } | ||
| 65 | + Track.instance.reportHttpRequest(RequestModel( | ||
| 66 | + uri: _realRequest.uri, | ||
| 67 | + method: method, | ||
| 68 | + pageId: pageInfoData?.pageInfo?.pageKey ?? "", | ||
| 69 | + requestHeaders: AutoTrackConfigManager | ||
| 70 | + .instance.config.httpRequestConfig!.ignoreRequestHeader | ||
| 71 | + ? null | ||
| 72 | + : _realRequest.headers, | ||
| 73 | + message: message, | ||
| 74 | + status: -1, | ||
| 75 | + spent: _stopwatch.elapsedMilliseconds)); | ||
| 76 | + return false; | ||
| 77 | + }); | ||
| 78 | + } | ||
| 79 | + | ||
| 80 | + @override | ||
| 81 | + HttpConnectionInfo? get connectionInfo => _realRequest.connectionInfo; | ||
| 82 | + | ||
| 83 | + @override | ||
| 84 | + List<Cookie> get cookies => _realRequest.cookies; | ||
| 85 | + | ||
| 86 | + @override | ||
| 87 | + Future<HttpClientResponse> get done async { | ||
| 88 | + return close(); | ||
| 89 | + } | ||
| 90 | + | ||
| 91 | + @override | ||
| 92 | + Future flush() { | ||
| 93 | + return _realRequest.flush(); | ||
| 94 | + } | ||
| 95 | + | ||
| 96 | + @override | ||
| 97 | + HttpHeaders get headers => _realRequest.headers; | ||
| 98 | + | ||
| 99 | + @override | ||
| 100 | + String get method => _realRequest.method; | ||
| 101 | + | ||
| 102 | + @override | ||
| 103 | + Uri get uri => _realRequest.uri; | ||
| 104 | + | ||
| 105 | + @override | ||
| 106 | + void write(Object? obj) { | ||
| 107 | + _realRequest.write(obj); | ||
| 108 | + } | ||
| 109 | + | ||
| 110 | + @override | ||
| 111 | + void writeAll(Iterable objects, [String separator = '']) { | ||
| 112 | + _realRequest.writeAll(objects, separator); | ||
| 113 | + } | ||
| 114 | + | ||
| 115 | + @override | ||
| 116 | + void writeCharCode(int charCode) { | ||
| 117 | + _realRequest.writeCharCode(charCode); | ||
| 118 | + } | ||
| 119 | + | ||
| 120 | + @override | ||
| 121 | + void writeln([Object? obj = '']) { | ||
| 122 | + _realRequest.writeln(obj); | ||
| 123 | + } | ||
| 124 | + | ||
| 125 | + void _checkResponse(HttpClientRequest request, HttpClientResponse response) { | ||
| 126 | + String message = 'status ${response.statusCode}'; | ||
| 127 | + message = '$message: ${response.reasonPhrase}'; | ||
| 128 | + | ||
| 129 | + _stopwatch.stop(); | ||
| 130 | + final config = AutoTrackConfigManager.instance.config.httpRequestConfig!; | ||
| 131 | + Track.instance.reportHttpRequest(RequestModel( | ||
| 132 | + uri: _realRequest.uri, | ||
| 133 | + method: method, | ||
| 134 | + pageId: pageInfoData?.pageInfo?.pageKey ?? "", | ||
| 135 | + requestHeaders: config.ignoreRequestHeader ? null : request.headers, | ||
| 136 | + responseHeaders: config.ignoreResponseHeader ? null : response.headers, | ||
| 137 | + message: message, | ||
| 138 | + status: response.statusCode, | ||
| 139 | + spent: _stopwatch.elapsedMilliseconds)); | ||
| 140 | + } | ||
| 141 | + | ||
| 142 | + @override | ||
| 143 | + set bufferOutput(bool bufferOutput) { | ||
| 144 | + _realRequest.bufferOutput = bufferOutput; | ||
| 145 | + } | ||
| 146 | + | ||
| 147 | + @override | ||
| 148 | + set contentLength(int contentLength) { | ||
| 149 | + _realRequest.contentLength = contentLength; | ||
| 150 | + } | ||
| 151 | + | ||
| 152 | + @override | ||
| 153 | + set encoding(Encoding encoding) { | ||
| 154 | + _realRequest.encoding = encoding; | ||
| 155 | + } | ||
| 156 | + | ||
| 157 | + @override | ||
| 158 | + set followRedirects(bool followRedirects) { | ||
| 159 | + _realRequest.followRedirects = followRedirects; | ||
| 160 | + } | ||
| 161 | + | ||
| 162 | + @override | ||
| 163 | + set maxRedirects(int maxRedirects) { | ||
| 164 | + _realRequest.maxRedirects = maxRedirects; | ||
| 165 | + } | ||
| 166 | + | ||
| 167 | + @override | ||
| 168 | + set persistentConnection(bool persistentConnection) { | ||
| 169 | + _realRequest.persistentConnection = persistentConnection; | ||
| 170 | + } | ||
| 171 | + | ||
| 172 | + @override | ||
| 173 | + void abort([Object? exception, StackTrace? stackTrace]) { | ||
| 174 | + _realRequest.abort(exception, stackTrace); | ||
| 175 | + } | ||
| 176 | +} | ||
| 177 | + | ||
| 178 | +class HttpClientWithChecker implements HttpClient { | ||
| 179 | + final HttpClient _realClient; | ||
| 180 | + | ||
| 181 | + Uri? url; | ||
| 182 | + String? method; | ||
| 183 | + | ||
| 184 | + HttpClientWithChecker(this._realClient); | ||
| 185 | + | ||
| 186 | + @override | ||
| 187 | + set connectionFactory( | ||
| 188 | + Future<ConnectionTask<Socket>> Function( | ||
| 189 | + Uri url, String? proxyHost, int? proxyPort)? | ||
| 190 | + f) { | ||
| 191 | + // TODO: add impl here | ||
| 192 | + assert(false); | ||
| 193 | + } | ||
| 194 | + | ||
| 195 | + @override | ||
| 196 | + set keyLog(Function(String line)? callback) { | ||
| 197 | + // TODO: add impl here | ||
| 198 | + assert(false); | ||
| 199 | + } | ||
| 200 | + | ||
| 201 | + @override | ||
| 202 | + bool get autoUncompress => _realClient.autoUncompress; | ||
| 203 | + | ||
| 204 | + @override | ||
| 205 | + set autoUncompress(bool value) => _realClient.autoUncompress = value; | ||
| 206 | + | ||
| 207 | + @override | ||
| 208 | + Duration? get connectionTimeout => _realClient.connectionTimeout; | ||
| 209 | + | ||
| 210 | + @override | ||
| 211 | + set connectionTimeout(Duration? value) => | ||
| 212 | + _realClient.connectionTimeout = value; | ||
| 213 | + | ||
| 214 | + @override | ||
| 215 | + Duration get idleTimeout => _realClient.idleTimeout; | ||
| 216 | + | ||
| 217 | + @override | ||
| 218 | + set idleTimeout(Duration value) => _realClient.idleTimeout = value; | ||
| 219 | + | ||
| 220 | + @override | ||
| 221 | + int? get maxConnectionsPerHost => _realClient.maxConnectionsPerHost; | ||
| 222 | + | ||
| 223 | + @override | ||
| 224 | + set maxConnectionsPerHost(int? value) => | ||
| 225 | + _realClient.maxConnectionsPerHost = value; | ||
| 226 | + | ||
| 227 | + @override | ||
| 228 | + String? get userAgent => _realClient.userAgent; | ||
| 229 | + | ||
| 230 | + @override | ||
| 231 | + set userAgent(String? value) => _realClient.userAgent = value; | ||
| 232 | + | ||
| 233 | + @override | ||
| 234 | + void addCredentials( | ||
| 235 | + Uri url, String realm, HttpClientCredentials credentials) => | ||
| 236 | + _realClient.addCredentials(url, realm, credentials); | ||
| 237 | + | ||
| 238 | + @override | ||
| 239 | + void addProxyCredentials(String host, int port, String realm, | ||
| 240 | + HttpClientCredentials credentials) => | ||
| 241 | + _realClient.addProxyCredentials(host, port, realm, credentials); | ||
| 242 | + | ||
| 243 | + @override | ||
| 244 | + set authenticate( | ||
| 245 | + Future<bool> Function(Uri url, String scheme, String? realm)? f) => | ||
| 246 | + _realClient.authenticate = f; | ||
| 247 | + | ||
| 248 | + @override | ||
| 249 | + set authenticateProxy( | ||
| 250 | + Future<bool> Function( | ||
| 251 | + String host, int port, String scheme, String? realm)? | ||
| 252 | + f) => | ||
| 253 | + _realClient.authenticateProxy = f; | ||
| 254 | + | ||
| 255 | + @override | ||
| 256 | + set badCertificateCallback( | ||
| 257 | + bool Function(X509Certificate cert, String host, int port)? | ||
| 258 | + callback) => | ||
| 259 | + _realClient.badCertificateCallback = callback; | ||
| 260 | + | ||
| 261 | + @override | ||
| 262 | + void close({bool force = false}) => _realClient.close(force: force); | ||
| 263 | + | ||
| 264 | + @override | ||
| 265 | + Future<HttpClientRequest> delete(String host, int port, String path) => | ||
| 266 | + _realClient.delete(host, port, path); | ||
| 267 | + | ||
| 268 | + @override | ||
| 269 | + Future<HttpClientRequest> deleteUrl(Uri url) => _realClient.deleteUrl(url); | ||
| 270 | + | ||
| 271 | + @override | ||
| 272 | + set findProxy(String Function(Uri url)? f) => _realClient.findProxy = f; | ||
| 273 | + | ||
| 274 | + @override | ||
| 275 | + Future<HttpClientRequest> get(String host, int port, String path) => | ||
| 276 | + _realClient.get(host, port, path); | ||
| 277 | + | ||
| 278 | + @override | ||
| 279 | + Future<HttpClientRequest> getUrl(Uri url) => | ||
| 280 | + _addCheck(_realClient.getUrl(url), 'get', url); | ||
| 281 | + | ||
| 282 | + @override | ||
| 283 | + Future<HttpClientRequest> head(String host, int port, String path) => | ||
| 284 | + _realClient.head(host, port, path); | ||
| 285 | + | ||
| 286 | + @override | ||
| 287 | + Future<HttpClientRequest> headUrl(Uri url) => | ||
| 288 | + _addCheck(_realClient.headUrl(url), 'head', url); | ||
| 289 | + | ||
| 290 | + @override | ||
| 291 | + Future<HttpClientRequest> patch(String host, int port, String path) => | ||
| 292 | + _realClient.patch(host, port, path); | ||
| 293 | + | ||
| 294 | + @override | ||
| 295 | + Future<HttpClientRequest> patchUrl(Uri url) => | ||
| 296 | + _addCheck(_realClient.patchUrl(url), 'patch', url); | ||
| 297 | + | ||
| 298 | + @override | ||
| 299 | + Future<HttpClientRequest> post(String host, int port, String path) => | ||
| 300 | + _realClient.post(host, port, path); | ||
| 301 | + | ||
| 302 | + @override | ||
| 303 | + Future<HttpClientRequest> postUrl(Uri url) => | ||
| 304 | + _addCheck(_realClient.postUrl(url), 'post', url); | ||
| 305 | + | ||
| 306 | + @override | ||
| 307 | + Future<HttpClientRequest> put(String host, int port, String path) => | ||
| 308 | + _realClient.put(host, port, path); | ||
| 309 | + | ||
| 310 | + @override | ||
| 311 | + Future<HttpClientRequest> putUrl(Uri url) => | ||
| 312 | + _addCheck(_realClient.putUrl(url), 'put', url); | ||
| 313 | + | ||
| 314 | + @override | ||
| 315 | + Future<HttpClientRequest> open( | ||
| 316 | + String method, String host, int port, String path) { | ||
| 317 | + const int hashMark = 0x23; | ||
| 318 | + const int questionMark = 0x3f; | ||
| 319 | + int fragmentStart = path.length; | ||
| 320 | + int queryStart = path.length; | ||
| 321 | + for (int i = path.length - 1; i >= 0; i--) { | ||
| 322 | + final char = path.codeUnitAt(i); | ||
| 323 | + if (char == hashMark) { | ||
| 324 | + fragmentStart = i; | ||
| 325 | + queryStart = i; | ||
| 326 | + } else if (char == questionMark) { | ||
| 327 | + queryStart = i; | ||
| 328 | + } | ||
| 329 | + } | ||
| 330 | + String? query; | ||
| 331 | + if (queryStart < fragmentStart) { | ||
| 332 | + query = path.substring(queryStart + 1, fragmentStart); | ||
| 333 | + path = path.substring(0, queryStart); | ||
| 334 | + } | ||
| 335 | + final Uri uri = | ||
| 336 | + Uri(scheme: 'http', host: host, port: port, path: path, query: query); | ||
| 337 | + return _addCheck(_realClient.open(method, host, port, path), method, uri); | ||
| 338 | + } | ||
| 339 | + | ||
| 340 | + @override | ||
| 341 | + Future<HttpClientRequest> openUrl(String method, Uri url) => | ||
| 342 | + _addCheck(_realClient.openUrl(method, url), method, url); | ||
| 343 | + | ||
| 344 | + Future<HttpClientRequest> _addCheck( | ||
| 345 | + Future<HttpClientRequest> request, String method, Uri url) { | ||
| 346 | + final host = AutoTrackConfigManager.instance.config.host; | ||
| 347 | + if (host != null) { | ||
| 348 | + final uploadUrl = Uri.parse(host); | ||
| 349 | + if (uploadUrl.host == url.host && uploadUrl.path == url.path) { | ||
| 350 | + return request; | ||
| 351 | + } | ||
| 352 | + } | ||
| 353 | + | ||
| 354 | + final Stopwatch stopwatch = Stopwatch()..start(); | ||
| 355 | + final Page? pageInfoData = PageStack.instance.getCurrentPage(); | ||
| 356 | + return request | ||
| 357 | + .then((HttpClientRequest request) => | ||
| 358 | + HttpClientRequestWithChecker(request, stopwatch, pageInfoData)) | ||
| 359 | + .catchError((dynamic error, dynamic stackTrace) {}, test: (error) { | ||
| 360 | + String message = error.toString(); | ||
| 361 | + if (error is SocketException) { | ||
| 362 | + message = error.message; | ||
| 363 | + } | ||
| 364 | + Track.instance.reportHttpRequest(RequestModel( | ||
| 365 | + uri: url, | ||
| 366 | + method: method, | ||
| 367 | + pageId: pageInfoData?.pageInfo?.pageKey ?? "", | ||
| 368 | + requestHeaders: null, | ||
| 369 | + message: message, | ||
| 370 | + status: -1, | ||
| 371 | + spent: stopwatch.elapsedMilliseconds)); | ||
| 372 | + return false; | ||
| 373 | + }); | ||
| 374 | + } | ||
| 375 | +} | ||
| 376 | + | ||
| 377 | +class _DefaultHttpOverrides extends HttpOverrides {} | ||
| 378 | + | ||
| 379 | +class AutoTrackHttpOverrides extends HttpOverrides { | ||
| 380 | + HttpOverrides? _currentOverrides; | ||
| 381 | + | ||
| 382 | + AutoTrackHttpOverrides(this._currentOverrides) : super() { | ||
| 383 | + _currentOverrides ??= _DefaultHttpOverrides(); | ||
| 384 | + } | ||
| 385 | + | ||
| 386 | + @override | ||
| 387 | + HttpClient createHttpClient(SecurityContext? context) { | ||
| 388 | + return HttpClientWithChecker(_currentOverrides!.createHttpClient(context)); | ||
| 389 | + } | ||
| 390 | + | ||
| 391 | + @override | ||
| 392 | + String findProxyFromEnvironment(Uri url, Map<String, String>? environment) { | ||
| 393 | + return _currentOverrides!.findProxyFromEnvironment(url, environment); | ||
| 394 | + } | ||
| 395 | +} |
| 1 | -import 'package:dio/dio.dart'; | 1 | +import 'dart:io'; |
| 2 | + | ||
| 2 | import 'package:flutter/widgets.dart'; | 3 | import 'package:flutter/widgets.dart'; |
| 3 | 4 | ||
| 4 | typedef AutoTrackLoggerHandler = void Function(AutoTrackLoggerLevel level, String message); | 5 | typedef AutoTrackLoggerHandler = void Function(AutoTrackLoggerLevel level, String message); |
| @@ -26,8 +27,8 @@ class AutoTrackLogger { | @@ -26,8 +27,8 @@ class AutoTrackLogger { | ||
| 26 | message = e.message; | 27 | message = e.message; |
| 27 | } else if (e is Error) { | 28 | } else if (e is Error) { |
| 28 | message = e.stackTrace.toString(); | 29 | message = e.stackTrace.toString(); |
| 29 | - } else if (e is DioException) { | ||
| 30 | - message = (e).message ?? 'dio exception with unknown message'; | 30 | + } else if (e is HttpException) { |
| 31 | + message = e.message; | ||
| 31 | } | 32 | } |
| 32 | _print(AutoTrackLoggerLevel.error, message); | 33 | _print(AutoTrackLoggerLevel.error, message); |
| 33 | } | 34 | } |
| 1 | import 'package:auto_track/auto_track/config/queue.dart'; | 1 | import 'package:auto_track/auto_track/config/queue.dart'; |
| 2 | -import 'package:auto_track/auto_track/drag/drag_info.dart'; | ||
| 3 | import 'package:auto_track/auto_track/utils/error_model.dart'; | 2 | import 'package:auto_track/auto_track/utils/error_model.dart'; |
| 3 | +import 'package:auto_track/auto_track/utils/request_model.dart'; | ||
| 4 | import 'package:auto_track/auto_track/utils/track_model.dart'; | 4 | import 'package:auto_track/auto_track/utils/track_model.dart'; |
| 5 | 5 | ||
| 6 | -import '../click/click_info.dart'; | ||
| 7 | import '../config/manager.dart'; | 6 | import '../config/manager.dart'; |
| 7 | +import '../listener/click/click_info.dart'; | ||
| 8 | +import '../listener/drag/drag_info.dart'; | ||
| 9 | +import '../listener/page_view/page_info.dart'; | ||
| 8 | import '../log/logger.dart'; | 10 | import '../log/logger.dart'; |
| 9 | -import '../page_view/page_info.dart'; | ||
| 10 | 11 | ||
| 11 | class Track { | 12 | class Track { |
| 12 | static final Track instance = Track._(); | 13 | static final Track instance = Track._(); |
| @@ -104,7 +105,14 @@ class Track { | @@ -104,7 +105,14 @@ class Track { | ||
| 104 | } | 105 | } |
| 105 | 106 | ||
| 106 | void reportError(Object error, StackTrace stack) { | 107 | void reportError(Object error, StackTrace stack) { |
| 107 | - _TrackPlugin.customEvent('error', ErrorModel(error: error, stack: stack).toMap()); | 108 | + final model = ErrorModel(error: error, stack: stack); |
| 109 | + _TrackPlugin.customEvent('error', model.toMap()); | ||
| 110 | + AutoTrackLogger.getInstance().debug('track error => ${model.toMap()}'); | ||
| 111 | + } | ||
| 112 | + | ||
| 113 | + void reportHttpRequest(RequestModel requestModel) { | ||
| 114 | + _TrackPlugin.customEvent('http', requestModel.toMap(), key: requestModel.uri.path); | ||
| 115 | + AutoTrackLogger.getInstance().debug('track request => ${requestModel.toMap()}'); | ||
| 108 | } | 116 | } |
| 109 | } | 117 | } |
| 110 | 118 | ||
| @@ -128,8 +136,8 @@ class _TrackPlugin { | @@ -128,8 +136,8 @@ class _TrackPlugin { | ||
| 128 | AutoTrackQueue.instance.appendQueue(model); | 136 | AutoTrackQueue.instance.appendQueue(model); |
| 129 | } | 137 | } |
| 130 | 138 | ||
| 131 | - static void customEvent(String type, Map<String, dynamic> params) { | ||
| 132 | - var model = TrackModel(type, DateTime.now().millisecondsSinceEpoch, params, params['key'] ?? type); | 139 | + static void customEvent(String type, Map<String, dynamic> params, { String? key }) { |
| 140 | + var model = TrackModel(type, DateTime.now().millisecondsSinceEpoch, params, params['key'] ?? key ?? type); | ||
| 133 | AutoTrackConfigManager.instance.config.eventHandler?.call(model); | 141 | AutoTrackConfigManager.instance.config.eventHandler?.call(model); |
| 134 | AutoTrackQueue.instance.appendQueue(model); | 142 | AutoTrackQueue.instance.appendQueue(model); |
| 135 | } | 143 | } |
lib/auto_track/utils/request_model.dart
0 → 100644
| 1 | +class RequestModel { | ||
| 2 | + RequestModel({ | ||
| 3 | + required this.uri, | ||
| 4 | + required this.method, | ||
| 5 | + required this.message, | ||
| 6 | + required this.status, | ||
| 7 | + required this.spent, | ||
| 8 | + required this.pageId, | ||
| 9 | + this.requestHeaders, | ||
| 10 | + this.responseHeaders, | ||
| 11 | + }); | ||
| 12 | + | ||
| 13 | + Uri uri; | ||
| 14 | + String method; | ||
| 15 | + String message; | ||
| 16 | + String pageId; | ||
| 17 | + int status; | ||
| 18 | + int spent; | ||
| 19 | + dynamic requestHeaders; | ||
| 20 | + dynamic responseHeaders; | ||
| 21 | + | ||
| 22 | + Map<String, dynamic> toMap() { | ||
| 23 | + return { | ||
| 24 | + 'uri': uri.toString(), | ||
| 25 | + 'method': method, | ||
| 26 | + 'message': message, | ||
| 27 | + 'pageId': pageId, | ||
| 28 | + 'status': status, | ||
| 29 | + 'spent': spent, | ||
| 30 | + 'requestHeaders': requestHeaders.toString(), | ||
| 31 | + 'responseHeaders': responseHeaders.toString(), | ||
| 32 | + }; | ||
| 33 | + } | ||
| 34 | +} |
| 1 | name: auto_track | 1 | name: auto_track |
| 2 | -description: "Auto Track Plugin" | ||
| 3 | -version: 0.0.6 | 2 | +description: "Low-intrusion global automatic embedding, automatically recording events such as page entry, exit, clicks, scrolling, and HTTP requests, and supporting custom events." |
| 3 | +version: 0.0.7 | ||
| 4 | homepage: https://github.com/epoll-j/auto_track_plugin | 4 | homepage: https://github.com/epoll-j/auto_track_plugin |
| 5 | 5 | ||
| 6 | environment: | 6 | environment: |
| @@ -10,7 +10,6 @@ environment: | @@ -10,7 +10,6 @@ environment: | ||
| 10 | dependencies: | 10 | dependencies: |
| 11 | crypto: ^3.0.3 | 11 | crypto: ^3.0.3 |
| 12 | device_info_plus: ^9.1.2 | 12 | device_info_plus: ^9.1.2 |
| 13 | - dio: ^5.4.1 | ||
| 14 | flutter: | 13 | flutter: |
| 15 | sdk: flutter | 14 | sdk: flutter |
| 16 | package_info_plus: ^8.0.1 | 15 | package_info_plus: ^8.0.1 |
-
Please register or login to post a comment