Flutter日志分析与记录插件analytics_logger_gen的使用
Flutter日志分析与记录插件analytics_logger_gen的使用
analytics_logger_gen
是一个代码生成器,用于为工具(如 Firebase Analytics)生成分析事件。它可以从 Google 表格、远程仓库或本地 CSV 文件中导入事件。
运行生成器
要运行生成器,请执行以下命令:
dart pub run build_runner build
其他有用的命令:
dart pub run build_runner build --delete-conflicting-outputs
# 如果你想在构建之前删除生成的文件
dart pub run build_runner clean
# 如果修改了 CSV 文件后生成的文件未更新
调用生成的代码
你可以在项目中的任何地方调用生成的代码,这些方法返回 Future
类型。
await EventProvider.setup();
EventProvider.appStarted(title: 'Hello', message: 'world');
基于注解的代码生成
生成器会在找到带有 [@AnalyticsLogger](/user/AnalyticsLogger)
注解的成员时生成代码。
import 'package:analytics_logger_gen/analytics_logger_gen.dart';
// 生成对应的 part 'analytics_logger.g.dart'
part 'analytics_logger.g.dart';
[@AnalyticsLogger](/user/AnalyticsLogger)(
// 例如:(从项目根目录)assets/logger_gen_example_sheet.csv
localCsvPath: '<PATH-TO-CSV-FILE>',
// 当声明了 localCsvPath 时,remoteCsvUrl 将被忽略。
remoteCsvUrl: '<URL-TO-CSV-FILE>',
loggers: <Type, String>{
// Map 的键是实现 [EventLogger] 接口的类的类型。
//
// 在 [@AnalyticsLogger](/user/AnalyticsLogger) 中匹配 <CSV-COLUMN-NAME> 与 CSV 列以确定生成的事件应调用哪个分析工具。
FirebaseAnalyticsLogger: '<CSV-COLUMN-NAME>',
})
// 类应声明为私有 '_' 以避免与生成的类冲突
// ignore: unused_element
class _EventLoggerContainer {}
// 你可以声明任意数量的第三方分析工具的日志记录器。
class FirebaseAnalyticsLogger extends EventLogger {
FirebaseAnalyticsLogger();
final FirebaseAnalytics _analytics = FirebaseAnalytics.instance;
[@override](/user/override)
Future<void> setup() async {
await Firebase.initializeApp();
super.setup();
}
[@override](/user/override)
Future<void> logEvent(String event,
{required Map<String, dynamic> attributes}) async {
switch (EventType.fromName(event)) {
case EventType.setUserId:
await _analytics.setUserId(
id: attributes.values.first?.value.toString());
break;
case EventType.setUserInfo:
for (final entry in attributes.entries) {
await _analytics.setUserProperty(
name: entry.key,
value: entry.value,
);
}
break;
default:
await _analytics.logEvent(name: event, parameters: attributes);
}
}
}
示例
调用生成的代码
EventProvider.appStarted();
EventProvider.buttonClicked(abTestCase: 'A');
EventProvider.purchase(productId: 'product-id', price: 100, currency: 'USD', quantity: 1);
CSV 文件
event_name
和arguments
列的索引不能修改;它们分别固定在第 0 和第 1 列。(你可以更改它们的名称,但不能更改索引。)- 你可以在
arguments
列的右侧添加任意数量的列。 - 在
[@AnalyticsLogger](/user/AnalyticsLogger)
的loggers
属性值中匹配的列将确定生成的事件应调用哪个分析工具。 - 使用
[TRUE]/[1]
启用日志记录器,使用[FALSE]/[0]/[ ]
禁用。
event_name | arguments | isFirebaseEnabled | isAppsFlyerEnabled | isAmplitudeEnabled | isMixpanelEnabled | isSingularEnabled | isDatadogEnabled | description |
---|---|---|---|---|---|---|---|---|
app_started | title, message | TRUE | TRUE | TRUE | TRUE | TRUE | TRUE | |
home_page_entered | ab_test_case | TRUE | TRUE | TRUE | TRUE | TRUE | ||
app_ended | TRUE | TRUE | TRUE | TRUE | TRUE | |||
button_clicked | ab_test_case | TRUE | TRUE | TRUE | TRUE | TRUE | TRUE | |
select_contents | content_type, item_id | TRUE | ||||||
send_message | title, message | TRUE | TRUE | TRUE | TRUE | |||
banner_clicked | TRUE | TRUE | TRUE | TRUE | TRUE | TRUE | ||
set_user_id | id | TRUE | ||||||
set_user_info | age, gender | TRUE | ||||||
purchase | productId, price, currency, quantity | TRUE |
生成代码的前提条件
本地 CSV 文件
import 'package:analytics_logger_gen/analytics_logger_gen.dart';
import '../event_logger_impls/event_loggers.dart';
part 'logger_from_local_file.g.dart';
[@AnalyticsLogger](/user/AnalyticsLogger)(
localCsvPath: 'assets/logger_gen_example_v3.csv',
loggers: {
FirebaseAnalyticsLogger: 'isFirebaseEnabled',
AppsFlyerLogger: 'isAppsFlyerEnabled',
AmplitudeLogger: 'isAmplitudeEnabled',
MixpanelLogger: 'isMixpanelEnabled',
SingularLogger: 'isSingularEnabled',
DatadogDebugLogger: 'isDatadogEnabled',
},
providerName: 'EventProvider',
eventTypeName: 'EventType')
// ignore: unused_element
class _EventLoggerContainer {}
远程 CSV 文件
import 'package:analytics_logger_gen/analytics_logger_gen.dart';
import '../event_logger_impls/event_loggers.dart';
part 'logger_from_google_spread_sheet.g.dart';
[@AnalyticsLogger](/user/AnalyticsLogger)(
remoteCsvUrl:
'https://docs.google.com/spreadsheets/d/e/2PACX-1vSziB4YXy4C777tDDHlW96iT-640jWsfpc0cJLCc114DWxNusSQs61EkrY5lhLcp0T1Wkj1IJWijQ-j/pub?gid=0&single=true&output=csv',
loggers: {
FirebaseAnalyticsLogger: 'isFirebaseEnabled',
AppsFlyerLogger: 'isAppsFlyerEnabled',
AmplitudeLogger: 'isAmplitudeEnabled',
MixpanelLogger: 'isMixpanelEnabled',
SingularLogger: 'isSingularEnabled',
DatadogDebugLogger: 'isDatadogEnabled',
},
providerName: 'EventProvider',
eventTypeName: 'EventType')
// ignore: unused_element
class _EventLoggerContainer {}
class FirebaseAnalyticsLogger extends EventLogger {
FirebaseAnalyticsLogger();
[@override](/user/override)
Future<void> logEvent(String event,
{required Map<String, dynamic> attributes}) async {
// Do something with the event and attributes
}
}
class AppsFlyerLogger extends EventLogger {
AppsFlyerLogger();
[@override](/user/override)
Future<void> logEvent(String event,
{required Map<String, dynamic> attributes}) async {
// Do something with the event and attributes
}
}
class AmplitudeLogger extends EventLogger {
AmplitudeLogger();
[@override](/user/override)
Future<void> logEvent(String event,
{required Map<String, dynamic> attributes}) {
// Do something with the event and attributes
}
}
class MixpanelLogger extends EventLogger {
MixpanelLogger();
[@override](/user/override)
Future<void> logEvent(String event,
{required Map<String, dynamic> attributes}) async {
// Do something with the event and attributes
}
}
class SingularLogger extends EventLogger {
SingularLogger();
[@override](/user/override)
Future<void> logEvent(String event,
{required Map<String, dynamic> attributes}) async {
// Do something with the event and attributes
}
}
class DatadogDebugLogger extends EventLogger {
DatadogDebugLogger();
[@override](/user/override)
Future<void> logEvent(String event,
{required Map<String, dynamic> attributes}) async {
// Do something with the event and attributes
}
}
生成的代码
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'main.dart';
// **************************************************************************
// AnalyticsLoggerGenerator
// **************************************************************************
enum EventType {
appStarted('app_started', true, true, true, true, true, true),
homePageEntered('home_page_entered', true, true, true, true, false, true),
appEnded('app_ended', true, true, true, true, true, false),
buttonClicked('button_clicked', true, true, true, true, true, true),
selectContents('select_contents', true, false, false, false, false, false),
sendMessage('send_message', false, true, true, true, false, true),
countIncreased('countIncreased', true, true, true, true, true, true),
bannerClicked('banner_clicked', true, true, true, true, true, true),
setUserId('set_user_id', true, false, false, false, false, false),
setUserInfo('set_user_info', true, false, false, false, false, false),
purchase('purchase', true, false, false, false, false, false);
const EventType(
this.name,
this.isFirebaseEnabled,
this.isAppsFlyerEnabled,
this.isAmplitudeEnabled,
this.isMixpanelEnabled,
this.isSingularEnabled,
this.isDatadogEnabled);
final String name;
final bool isFirebaseEnabled;
final bool isAppsFlyerEnabled;
final bool isAmplitudeEnabled;
final bool isMixpanelEnabled;
final bool isSingularEnabled;
final bool isDatadogEnabled;
static EventType fromName(String name) {
switch (name) {
case 'app_started':
return EventType.appStarted;
case 'home_page_entered':
return EventType.homePageEntered;
case 'app_ended':
return EventType.appEnded;
case 'button_clicked':
return EventType.buttonClicked;
case 'select_contents':
return EventType.selectContents;
case 'send_message':
return EventType.sendMessage;
case 'countIncreased':
return EventType.countIncreased;
case 'banner_clicked':
return EventType.bannerClicked;
case 'set_user_id':
return EventType.setUserId;
case 'set_user_info':
return EventType.setUserInfo;
case 'purchase':
return EventType.purchase;
default:
throw ArgumentError('Invalid name: $name');
}
}
}
class EventProvider {
EventProvider._();
static Future<void> appStarted({dynamic title, dynamic message}) async {
Map<String, dynamic> attributes = <String, dynamic>{
'title': title,
'message': message,
};
await EventLoggerContainer.logEvent(EventType.appStarted, attributes);
}
static Future<void> homePageEntered({dynamic abTestCase}) async {
Map<String, dynamic> attributes = <String, dynamic>{
'abTestCase': abTestCase,
};
await EventLoggerContainer.logEvent(EventType.homePageEntered, attributes);
}
static Future<void> appEnded() async {
Map<String, dynamic> attributes = <String, dynamic>{};
await EventLoggerContainer.logEvent(EventType.appEnded, attributes);
}
static Future<void> buttonClicked({dynamic abTestCase}) async {
Map<String, dynamic> attributes = <String, dynamic>{
'abTestCase': abTestCase,
};
await EventLoggerContainer.logEvent(EventType.buttonClicked, attributes);
}
static Future<void> selectContents(
{dynamic contentType, dynamic itemId}) async {
Map<String, dynamic> attributes = <String, dynamic>{
'contentType': contentType,
'itemId': itemId,
};
await EventLoggerContainer.logEvent(EventType.selectContents, attributes);
}
static Future<void> sendMessage({dynamic title, dynamic message}) async {
Map<String, dynamic> attributes = <String, dynamic>{
'title': title,
'message': message,
};
await EventLoggerContainer.logEvent(EventType.sendMessage, attributes);
}
static Future<void> countIncreased({dynamic count}) async {
Map<String, dynamic> attributes = <String, dynamic>{
'count': count,
};
await EventLoggerContainer.logEvent(EventType.countIncreased, attributes);
}
static Future<void> bannerClicked() async {
Map<String, dynamic> attributes = <String, dynamic>{};
await EventLoggerContainer.logEvent(EventType.bannerClicked, attributes);
}
static Future<void> setUserId({dynamic id}) async {
Map<String, dynamic> attributes = <String, dynamic>{
'id': id,
};
await EventLoggerContainer.logEvent(EventType.setUserId, attributes);
}
static Future<void> setUserInfo({dynamic age, dynamic gender}) async {
Map<String, dynamic> attributes = <String, dynamic>{
'age': age,
'gender': gender,
};
await EventLoggerContainer.logEvent(EventType.setUserInfo, attributes);
}
static Future<void> purchase(
{dynamic productId,
dynamic price,
dynamic currency,
dynamic quantity}) async {
Map<String, dynamic> attributes = <String, dynamic>{
'productId': productId,
'price': price,
'currency': currency,
'quantity': quantity,
};
await EventLoggerContainer.logEvent(EventType.purchase, attributes);
}
}
class EventLoggerContainer {
EventLoggerContainer._();
static FirebaseAnalyticsLogger firebaseAnalyticsLogger =
FirebaseAnalyticsLogger();
static AppsFlyerLogger appsFlyerLogger = AppsFlyerLogger();
static AmplitudeLogger amplitudeLogger = AmplitudeLogger();
static MixpanelLogger mixpanelLogger = MixpanelLogger();
static SingularLogger singularLogger = SingularLogger();
static DatadogDebugLogger datadogDebugLogger = DatadogDebugLogger();
static Future<void> setup() async {
await firebaseAnalyticsLogger.setup();
await appsFlyerLogger.setup();
await amplitudeLogger.setup();
await mixpanelLogger.setup();
await singularLogger.setup();
await datadogDebugLogger.setup();
}
static Future<void> logEvent(
EventType event, Map<String, dynamic> attributes) async {
if (event.isFirebaseEnabled) {
await firebaseAnalyticsLogger.logEvent(event.name,
attributes: attributes);
}
if (event.isAppsFlyerEnabled) {
await appsFlyerLogger.logEvent(event.name, attributes: attributes);
}
if (event.isAmplitudeEnabled) {
await amplitudeLogger.logEvent(event.name, attributes: attributes);
}
if (event.isMixpanelEnabled) {
await mixpanelLogger.logEvent(event.name, attributes: attributes);
}
if (event.isSingularEnabled) {
await singularLogger.logEvent(event.name, attributes: attributes);
}
if (event.isDatadogEnabled) {
await datadogDebugLogger.logEvent(event.name, attributes: attributes);
}
}
}
完整示例 Demo
以下是一个完整的示例应用程序,展示了如何使用 analytics_logger_gen
插件来记录和分析日志。
// ignore: depend_on_referenced_packages
import 'package:analytics_logger_gen/analytics_logger_gen.dart';
// ignore: depend_on_referenced_packages
import 'package:flutter/material.dart';
import '../event_logger_impls/event_loggers.dart';
part 'main.g.dart';
[@AnalyticsLogger](/user/AnalyticsLogger)(
localCsvPath: 'assets/logger_gen_example_v3.csv',
loggers: <Type, String>{
FirebaseAnalyticsLogger: 'isFirebaseEnabled',
AppsFlyerLogger: 'isAppsFlyerEnabled',
AmplitudeLogger: 'isAmplitudeEnabled',
MixpanelLogger: 'isMixpanelEnabled',
SingularLogger: 'isSingularEnabled',
DatadogDebugLogger: 'isDatadogEnabled',
},
providerName: 'EventProvider',
eventTypeName: 'EventType')
// ignore: unused_element
class _EventLoggerContainer {}
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
[@override](/user/override)
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, required this.title});
final String title;
[@override](/user/override)
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
EventProvider.countIncreased(count: _counter);
}
[@override](/user/override)
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
_asyncMethod();
});
}
_asyncMethod() async {
// 初始化所有事件记录器
await EventLoggerContainer.setup();
// 或者单独初始化每个事件记录器
await EventLoggerContainer.firebaseAnalyticsLogger.setup();
EventProvider.appStarted();
}
[@override](/user/override)
void dispose() {
EventProvider.appEnded();
super.dispose();
}
[@override](/user/override)
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.headlineMedium,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: const Icon(Icons.add),
),
);
}
}
更多关于Flutter日志分析与记录插件analytics_logger_gen的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter日志分析与记录插件analytics_logger_gen的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
当然,以下是如何在Flutter项目中使用analytics_logger_gen
插件进行日志分析与记录的代码示例。analytics_logger_gen
是一个假设的插件名称,用于说明目的,实际使用中请确保插件名称和用法符合真实插件的文档。
1. 添加依赖
首先,在你的pubspec.yaml
文件中添加analytics_logger_gen
依赖:
dependencies:
flutter:
sdk: flutter
analytics_logger_gen: ^x.y.z # 替换为实际版本号
然后运行flutter pub get
来获取依赖。
2. 初始化插件
在你的应用入口文件(通常是main.dart
)中初始化插件:
import 'package:flutter/material.dart';
import 'package:analytics_logger_gen/analytics_logger_gen.dart';
void main() {
// 初始化AnalyticsLogger
final AnalyticsLogger logger = AnalyticsLogger(
// 配置日志级别等参数
level: LogLevel.verbose,
output: Output.console, // 输出到控制台,实际使用中可能输出到文件或远程服务器
);
runApp(MyApp(logger: logger));
}
class MyApp extends StatelessWidget {
final AnalyticsLogger logger;
MyApp({required this.logger});
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('Flutter Analytics Logger Demo'),
),
body: Center(
child: LogExample(logger: logger),
),
),
);
}
}
3. 使用插件记录日志
在你的应用组件中使用插件记录不同类型的日志:
import 'package:flutter/material.dart';
import 'package:analytics_logger_gen/analytics_logger_gen.dart';
class LogExample extends StatelessWidget {
final AnalyticsLogger logger;
LogExample({required this.logger});
@override
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
ElevatedButton(
onPressed: () {
logger.verbose('This is a verbose log message.');
logger.debug('This is a debug log message.');
logger.info('This is an info log message.');
logger.warning('This is a warning log message.');
logger.error('This is an error log message.');
},
child: Text('Log Messages'),
),
ElevatedButton(
onPressed: () {
// 假设有一个自定义的日志事件
logger.logCustomEvent(
event: 'user_action',
parameters: {
'action': 'button_clicked',
'timestamp': DateTime.now().toIso8601String(),
},
);
},
child: Text('Log Custom Event'),
),
],
);
}
}
4. 自定义日志输出(可选)
如果你需要将日志输出到文件或远程服务器,可以自定义输出方式。例如,输出到文件:
import 'dart:io';
import 'package:path_provider/path_provider.dart';
import 'package:analytics_logger_gen/analytics_logger_gen.dart';
class FileLoggerOutput implements LogOutput {
final File _logFile;
FileLoggerOutput() : _logFile = File('${(getApplicationDocumentsDirectory() as Future<Directory>).sync().path}/app.log');
@override
Future<void> writeLog(LogLevel level, String message) async {
final String logEntry = '[${DateTime.now().toIso8601String()}] [$level] $message\n';
await _logFile.writeAsString(logEntry, mode: FileMode.append);
}
}
void main() async {
final Directory appDocDir = await getApplicationDocumentsDirectory();
final AnalyticsLogger logger = AnalyticsLogger(
level: LogLevel.verbose,
output: FileLoggerOutput(), // 使用自定义的文件输出
);
runApp(MyApp(logger: logger));
}
请注意,上述代码示例中的analytics_logger_gen
插件及其API是假设的。在实际使用中,请查阅具体插件的官方文档以获取准确的API和用法。同时,确保处理任何潜在的异步操作(如文件IO)时,使用适当的错误处理和资源管理策略。