log_manager_plugin
Flutter日志管理插件,支持控制台输出、文件存储、日志查看、压缩上报等功能。
目录
功能
- 控制台日志输出(基于logger)
- 文件日志存储,按天管理
- Debug/Release模式分别配置
- Dio网络请求日志拦截
- 日志文件自动分块(默认10MB)
- 自动清理过期日志(按天数)
- 日志文件压缩
- 内置日志查看器UI
- 日志上报(支持文件/字符串两种方式)
安装
方式1:从 pub.dev 安装(推荐)
dependencies:
log_manager_plugin: ^1.0.2
方式2:从 GitHub 安装
dependencies:
log_manager_plugin:
git:
url: https://github.com/xiaoli55979/log_manager_plugin.git
ref: main # 或指定 tag: v1.0.0
然后运行:
flutter pub get
快速开始
初始化
重要:在主应用的 main.dart 中初始化一次即可,所有插件共享同一个日志实例。
import 'package:log_manager_plugin/log_manager_plugin.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
// 只需初始化一次
await LogManager.instance.init(
const LogManagerConfig(
enabled: true,
enableConsoleInDebug: true,
enableConsoleInRelease: false,
enableFileLog: true,
maxFileSize: 10 * 1024 * 1024, // 10MB
maxRetentionDays: 7, // 保留7天
logLevel: Level.debug,
logDirectory: 'logs',
deleteAfterUpload: true, // 上报后删除压缩文件
maxBatchSize: 100 * 1024, // 字符串上报每批100KB
),
);
runApp(const MyApp());
}
日志输出
LogManager.v('verbose');
LogManager.d('debug');
LogManager.i('info');
LogManager.w('warning');
LogManager.e('error', error: e, stackTrace: stackTrace);
LogManager.f('fatal');
Dio拦截器
final dio = Dio();
dio.interceptors.add(
LogManagerInterceptor(
requestHeader: true, // 打印请求头
requestBody: true, // 打印请求体
responseHeader: true, // 打印响应头
responseBody: true, // 打印响应体
error: true, // 打印错误信息
compact: true, // 紧凑模式(超长内容截断)
maxWidth: 90, // 紧凑模式最大宽度
),
);
日志查看
按日期查看(推荐)
Navigator.push(
context,
MaterialPageRoute(builder: (context) => const LogViewerByDate()),
);
功能:
- 按日期分组
- 统计信息
- 多选操作
- 压缩/删除
- 查看内容
增强版查看器
Navigator.push(
context,
MaterialPageRoute(builder: (context) => const EnhancedLogViewer()),
);
功能:
- 日志级别着色
- 搜索过滤
- 级别过滤
- 复制内容
基础版查看器
Navigator.push(
context,
MaterialPageRoute(builder: (context) => const LogViewerPage()),
);
文件管理
基础操作
// 获取所有日志文件
final files = await LogManager.getAllLogFiles();
// 获取日志目录路径
final path = LogManager.logDirectoryPath;
// 清空所有日志
await LogManager.clearAllLogs();
按日期管理
// 按日期分组获取日志文件
final grouped = await LogFileManager.instance.getLogFilesByDate();
// 返回: {'20231128': [file1, file2], '20231127': [file3]}
// 删除指定日期的日志
await LogFileManager.instance.deleteLogsByDate('20231128');
日志压缩
// 压缩所有日志
final zipFile = await LogManager.compressLogs();
// 压缩指定日期的日志
final zipFile = await LogFileManager.instance.compressLogsByDate('20231128');
// 压缩指定文件
final files = [file1, file2];
final zipFile = await LogFileManager.instance.compressSpecificLogs(files);
日志统计
// 获取统计信息
final stats = await LogFileManager.instance.getStatistics();
print('总文件数: ${stats.totalFiles}');
print('总大小: ${stats.totalSize}');
print('日期数: ${stats.dateCount}');
print('最早日期: ${stats.earliestDate}');
print('最新日期: ${stats.latestDate}');
清理旧格式日志
如果从旧版本升级,可以清理旧格式的日志文件:
await LogFileManager.instance.cleanLegacyLogFiles();
高级用法
多插件项目使用
如果你的项目中有多个插件都依赖 log_manager_plugin:
// ✅ 正确做法:只在主应用中初始化一次
// main.dart
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await LogManager.instance.init(const LogManagerConfig(...));
runApp(const MyApp());
}
// plugin_a.dart - 插件A直接使用
class PluginA {
void doSomething() {
LogManager.d('插件A: 开始执行');
// ...
}
}
// plugin_b.dart - 插件B直接使用
class PluginB {
void doSomething() {
LogManager.i('插件B: 处理数据');
// ...
}
}
// ❌ 错误做法:不要在每个插件中都初始化
// 这会导致配置被覆盖,且浪费资源
说明:即使未初始化就调用日志方法,也不会报错,只是不会输出日志。
运行时更新配置
// 动态修改配置
await LogManager.instance.updateConfig(
LogManagerConfig(
enableConsoleInRelease: true, // 临时开启Release日志
logLevel: Level.trace, // 调整日志级别
),
);
获取当前配置
final config = LogManager.config;
print('当前日志级别: ${config.logLevel}');
print('文件日志: ${config.enableFileLog}');
日志上报
方式1:文件上传
适合支持 multipart/form-data 的接口。
// 设置回调
LogReporter.instance.setUploadCallback((zipFile) async {
try {
final dio = Dio();
final formData = FormData.fromMap({
'file': await MultipartFile.fromFile(zipFile.path, filename: 'logs.zip'),
'deviceId': 'xxx',
'timestamp': DateTime.now().toIso8601String(),
});
final response = await dio.post('https://your-api.com/upload', data: formData);
return response.statusCode == 200;
} catch (e) {
return false;
}
});
// 上报所有日志
await LogReporter.instance.uploadLogs();
// 上报指定日期
await LogReporter.instance.uploadLogsByDate('20231128');
// 自定义参数
await LogReporter.instance.uploadLogs(
deleteAfterUpload: false, // 保留压缩文件
);
方式2:字符串分批上传
适合只支持 JSON 格式的接口。
// 设置回调
LogReporter.instance.setUploadStringCallback((batches) async {
try {
final dio = Dio();
for (final batch in batches) {
await dio.post('https://your-api.com/upload-string', data: {
'batchIndex': batch.batchIndex,
'totalBatches': batch.totalBatches,
'content': batch.content,
'fileName': batch.fileName,
'date': batch.date,
});
}
return true;
} catch (e) {
return false;
}
});
// 上报所有日志
await LogReporter.instance.uploadLogsAsString();
// 上报指定日期
await LogReporter.instance.uploadLogsByDateAsString('20231128');
// 自定义批次大小
await LogReporter.instance.uploadLogsAsString(
maxBatchSize: 50 * 1024, // 每批50KB
);
配置优先级
上报参数的优先级:方法参数 > 配置项 > 默认值
// 在初始化时统一配置
await LogManager.instance.init(
const LogManagerConfig(
deleteAfterUpload: false, // 默认保留压缩文件
maxBatchSize: 200 * 1024, // 默认每批200KB
),
);
// 调用时可以临时覆盖
await LogReporter.instance.uploadLogs(
deleteAfterUpload: true, // 这次删除
);
配置参数
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| enabled | bool | true | 是否启用 |
| enableConsoleInDebug | bool | true | Debug模式控制台输出 |
| enableConsoleInRelease | bool | false | Release模式控制台输出 |
| enableFileLog | bool | kDebugMode | 文件日志(默认只在Debug模式) |
| maxFileSize | int | 10MB | 单文件最大大小 |
| maxRetentionDays | int | 7 | 保留天数 |
| logLevel | Level | debug | 日志级别 |
| logDirectory | String | 'logs' | 日志目录 |
| deleteAfterUpload | bool | true | 上报成功后删除压缩文件 |
| maxBatchSize | int | 100KB | 字符串上报每批大小 |
日志级别
日志级别从低到高:
Level.trace- 追踪(最详细)Level.debug- 调试Level.info- 信息Level.warning- 警告Level.error- 错误Level.fatal- 致命(最严重)
级别过滤规则
配置的 logLevel 会同时影响控制台和文件输出:
const LogManagerConfig(
logLevel: Level.warning, // 只输出 warning、error、fatal
)
// 这些日志会被过滤掉(不输出到控制台和文件)
LogManager.v('trace 日志'); // ❌ 被过滤
LogManager.d('debug 日志'); // ❌ 被过滤
LogManager.i('info 日志'); // ❌ 被过滤
// 这些日志会正常输出
LogManager.w('warning 日志'); // ✅ 输出
LogManager.e('error 日志'); // ✅ 输出
LogManager.f('fatal 日志'); // ✅ 输出
建议配置:
- 开发环境:
Level.debug或Level.trace- 查看详细信息 - 生产环境:
Level.info或Level.warning- 只记录重要信息,减少日志量
文件管理策略
- 按天创建日志文件:
log_20231128_001.txt - 单文件超过限制自动创建新文件:
log_20231128_002.txt - 自动删除超过保留天数的文件
- 应用启动和跨天时自动清理
完整使用示例
import 'package:flutter/material.dart';
import 'package:log_manager_plugin/log_manager_plugin.dart';
import 'package:dio/dio.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
// 1. 初始化日志系统
await LogManager.instance.init(
const LogManagerConfig(
enabled: true,
enableConsoleInDebug: true,
enableConsoleInRelease: false,
enableFileLog: true,
maxFileSize: 10 * 1024 * 1024,
maxRetentionDays: 7,
logLevel: Level.debug,
deleteAfterUpload: true,
maxBatchSize: 100 * 1024,
),
);
// 2. 配置Dio拦截器
final dio = Dio();
dio.interceptors.add(
LogManagerInterceptor(
requestHeader: true,
requestBody: true,
responseBody: true,
compact: true,
),
);
// 3. 配置日志上报
LogReporter.instance.setUploadCallback((zipFile) async {
try {
final formData = FormData.fromMap({
'file': await MultipartFile.fromFile(zipFile.path),
});
final response = await dio.post('https://api.example.com/logs', data: formData);
return response.statusCode == 200;
} catch (e) {
return false;
}
});
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: const Text('日志管理示例')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
onPressed: () {
// 输出各级别日志
LogManager.d('这是调试日志');
LogManager.i('这是信息日志');
LogManager.w('这是警告日志');
LogManager.e('这是错误日志');
},
child: const Text('输出日志'),
),
ElevatedButton(
onPressed: () {
// 打开日志查看器
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const LogViewerByDate(),
),
);
},
child: const Text('查看日志'),
),
ElevatedButton(
onPressed: () async {
// 上报日志
final success = await LogReporter.instance.uploadLogs();
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(success ? '上报成功' : '上报失败')),
);
},
child: const Text('上报日志'),
),
],
),
),
),
);
}
}
更多示例
查看 example 目录获取完整示例代码。
常见问题
1. 多个插件都使用这个日志插件,需要每个插件都初始化吗?
不需要! 只在主应用的 main.dart 中初始化一次即可。
// 主应用 main.dart
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await LogManager.instance.init(const LogManagerConfig(...)); // 只初始化一次
runApp(const MyApp());
}
// 插件A、插件B、插件C... 直接使用
LogManager.d('来自插件A的日志');
LogManager.i('来自插件B的日志');
所有插件共享同一个日志实例,日志会统一管理和存储。
2. 日志文件存储在哪里?
- iOS:
Application Support/logs/ - Android:
应用数据目录/logs/
可以通过 LogManager.logDirectoryPath 获取完整路径。
3. 如何在Release模式下查看日志?
await LogManager.instance.init(
const LogManagerConfig(
enableConsoleInRelease: true, // 开启Release控制台输出
),
);
4. 日志文件太大怎么办?
调整配置参数:
const LogManagerConfig(
maxFileSize: 5 * 1024 * 1024, // 减小单文件大小到5MB
maxRetentionDays: 3, // 只保留3天
)
5. 如何只记录/上报错误日志?
方法1:配置日志级别(推荐)
const LogManagerConfig(
logLevel: Level.error, // 只记录 error 和 fatal
)
这样只有 LogManager.e() 和 LogManager.f() 的日志会被写入文件。
方法2:运行时动态调整
// 正常情况记录所有日志
await LogManager.instance.init(const LogManagerConfig(logLevel: Level.debug));
// 生产环境只记录错误
await LogManager.instance.updateConfig(const LogManagerConfig(logLevel: Level.error));
6. 上报失败怎么办?
日志文件会保留在本地,可以稍后重试:
final success = await LogReporter.instance.uploadLogs(
deleteAfterUpload: false, // 失败时不删除
);
注意事项
- 初始化时机:必须在
WidgetsFlutterBinding.ensureInitialized()之后初始化 - 文件权限:iOS/Android 会自动处理,无需额外配置
- 性能影响:文件写入是异步的,不会阻塞主线程
- 日志级别:
logLevel配置会同时过滤控制台和文件输出,低于该级别的日志不会被记录 - 日志安全:上报前建议加密敏感信息
- 网络请求:Dio拦截器会记录完整请求响应,注意数据量
License
MIT