FlutterLog - Flutter 日志存储与拉取工具
一套完整的 Flutter 日志管理解决方案,包含日志缓存工具和 ADB 日志拉取脚本。
📦 项目组成
1. log_storage_tool.dart - Flutter 日志存储工具
高性能的日志缓存和本地存储工具,支持自动节流、队列管理和文件大小控制。
2. pull_log.bat - ADB 日志拉取脚本
Windows 批处理脚本,通过 ADB 从 Android 设备拉取日志文件到本地。
🚀 快速开始
安装日志存储工具
-
将
log_storage_tool.dart复制到你的 Flutter 项目中 -
在
pubspec.yaml中添加依赖:
dependencies:
path_provider: ^2.0.0
- 运行安装:
flutter pub get
- 在代码中导入:
import 'log_storage_tool.dart';
📖 使用文档
一、日志存储工具 (log_storage_tool.dart)
基本用法
1. 简单打印日志
// 最简单的用法 - 打印到控制台并写入文件
println('应用启动');
println('用户登录成功');
2. 多参数拼接
// 支持最多 5 个参数自动拼接
println('用户ID:', userId, '操作:', action, '结果:', result);
// 输出: [2026-01-23 11:39:12] 用户ID:, 12345, 操作:, login, 结果:, success
3. 只写入文件,不打印到控制台
// 第二个参数传 "silent" 可以静默写入
println('敏感数据', 'silent');
// 支持传入 List,自动展开拼接
List<String> operations = ['步骤1完成', '步骤2完成', '步骤3完成'];
println(operations, 'silent');
// 输出: [2026-01-23 13:09:01] 步骤1完成, 步骤2完成, 步骤3完成
4. 打印对象
// 自动 JSON 序列化
Map<String, dynamic> data = {'name': 'test', 'value': 123};
println(data);
// 输出: [2026-01-23 11:39:12] {"name":"test","value":123}
高级功能
1. 配置开关
// 禁用文件写入(仅打印到控制台)
LogStorageTool.write = false;
// 重新启用
LogStorageTool.write = true;
2. 时间同步
// 注入服务器时间,用于多设备时间对齐
LogStorageTool.nowServerTime = DateTime.parse('2026-01-23 12:00:00');
println('此日志将使用服务器时间');
3. 读取日志
// 异步读取所有本地日志
String logs = await LogStorageTool.readLog();
print(logs);
4. 清空日志
// 清空内存队列和本地文件
await LogStorageTool.clear();
print('日志已清空');
核心特性
| 特性 | 说明 |
|---|---|
| 队列管理 | 内存中维护日志队列,批量写入,减少 I/O 操作 |
| 节流控制 | 两次写入间隔最少 3 秒,避免频繁写文件 |
| 写锁机制 | 使用 Completer 确保同一时刻只有一个写入任务 |
| 文件容量控制 | 文件超过 10MB 自动裁剪,保留后半部分 |
| 控制字符清理 | 自动过滤 \x00-\x1F 等不可见字符,防止写入失败 |
| 智能参数处理 | 支持 List 参数自动展开,灵活处理批量日志 |
| 跨平台支持 | Android 使用外部存储,iOS/其他平台使用应用目录 |
存储位置
- Android:
/storage/emulated/0/Android/data/<包名>/files/log.txt - iOS/其他平台: 应用沙盒的
ApplicationSupportDirectory/log.txt
文件裁剪逻辑
当日志文件超过 10MB 时:
- 读取整个文件到内存
- 从中间位置开始查找第一个换行符 (
\n) - 保留后半部分内容,覆盖原文件
// 相关常量配置
static const int maxLength = 10 * 1024 * 1024; // 10MB
二、日志拉取工具 (pull_log.bat)
基本用法
方式 1: 自动检测设备
pull_log.bat
- 自动连接第一个在线设备
- 拉取日志并保存
方式 2: 指定设备 ID
pull_log.bat -s <deviceID>
获取设备 ID
adb devices
输出示例:
List of devices attached
1234567890ABCDEF device
emulator-5554 device
使用指定设备:
pull_log.bat -s 1234567890ABCDEF
输出文件
日志文件以当前时间命名,格式为 Log_HHMMSS.txt:
| 时间 | 文件名 |
|---|---|
| 上午 10:15:30 | Log_101530.txt |
| 下午 14:25:45 | Log_142545.txt |
| 晚上 23:59:59 | Log_235959.txt |
工作流程
graph LR
A[运行脚本] --> B{检测设备}
B -->|未连接| C[❌ 错误退出]
B -->|已连接| D[执行 sync]
D --> E[拉取日志文件]
E --> F{检查结果}
F -->|成功且有内容| G[✅ 自动打开文件]
F -->|文件为空| H[⚠️ 警告提示]
F -->|拉取失败| I[❌ 错误提示]
配置说明
修改远程路径
脚本中的路径对应你的应用包名:
set "REMOTE_PATH=/storage/emulated/0/Android/data/com.xxx.xxx/files/log.txt"
如果你的包名不同,需要修改:
set "REMOTE_PATH=/storage/emulated/0/Android/data/<你的包名>/files/log.txt"
前置要求
-
ADB 已安装并配置环境变量
adb version -
设备已开启 USB 调试
- 进入
设置 -> 开发者选项 -> USB 调试
- 进入
-
设备已授权连接
- 首次连接时,设备会弹出授权提示
常见问题
Q: 提示 "adb 不是内部或外部命令"?
A: ADB 未添加到环境变量,需要配置 Android SDK 的 platform-tools 目录。
Q: 拉取文件为空 (0 字节)?
A:
- 检查应用是否写入过日志
- 检查
REMOTE_PATH路径是否正确 - 确认应用有存储权限
Q: 提示 "设备不在线"?
A:
- 运行
adb devices确认设备状态 - 尝试
adb kill-server和adb start-server重启 ADB
Q: 中文乱码?
A: 脚本已设置 UTF-8 编码 (chcp 65001),如果仍乱码,检查日志文件编码。
🔧 完整示例
Flutter 应用端
import 'package:flutter/material.dart';
import 'log_storage_tool.dart';
void main() {
// 可选:同步服务器时间
LogStorageTool.nowServerTime = DateTime.parse('2026-01-23 12:00:00');
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
println('应用启动', '版本:', '1.0.0');
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text('FlutterLog 示例')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
onPressed: () {
println('按钮点击', '时间:', DateTime.now().toString());
},
child: Text('记录日志'),
),
SizedBox(height: 20),
ElevatedButton(
onPressed: () async {
String logs = await LogStorageTool.readLog();
print('日志内容:\n$logs');
},
child: Text('读取日志'),
),
SizedBox(height: 20),
ElevatedButton(
onPressed: () async {
await LogStorageTool.clear();
println('日志已清空');
},
child: Text('清空日志'),
),
],
),
),
),
);
}
}
PC 端拉取日志
-
连接 Android 设备
-
运行脚本:
# 在脚本所在目录运行 pull_log.bat # 或指定设备 ID pull_log.bat -s 1234567890ABCDEF -
日志自动保存并打开
📊 性能特性
日志写入性能
| 场景 | 性能表现 |
|---|---|
| 单次日志 | ~0.1ms (内存队列) |
| 批量写入 | 3 秒节流,批量写入,减少 I/O |
| 文件裁剪 | 10MB 时触发,保留后 5MB |
资源占用
- 内存: 队列缓存 + 写入缓冲区,通常 < 1MB
- 存储: 最大 10MB 日志文件
- CPU: 异步写入,对主线程影响极小
🛡️ 安全与隐私
日志安全建议
-
敏感数据处理
// ❌ 不要直接记录密码 println('密码:', password); // ✅ 脱敏处理 println('密码:', password.replaceAll(RegExp(r'.'), '*')); -
生产环境控制
import 'package:flutter/foundation.dart'; // 生产环境禁用详细日志 if (kReleaseMode) { LogStorageTool.write = false; } -
定期清理
// 应用启动时检查日志大小 void checkLogSize() async { String logs = await LogStorageTool.readLog(); if (logs.length > 1024 * 1024) { // 超过 1MB await LogStorageTool.clear(); } }
📝 API 参考
全局函数
println()
打印日志到控制台并写入文件。
签名:
void println([
Object? v1 = _defaultCus,
Object? v2 = _defaultCus,
Object? v3 = _defaultCus,
Object? v4 = _defaultCus,
Object? v5 = _defaultCus,
])
参数:
v1-v5: 最多 5 个参数,自动拼接- 如果
v2 == "silent",则只写文件,不打印 - 当
v2 == "silent"且v1是 List 类型时,会自动展开 List 元素并拼接
示例:
println('Hello');
println('User:', 'Alice', 'Action:', 'Login');
println('Secret', 'silent'); // 静默写入
cleanBasicInvalidChars()
清除字符串中的控制字符。
签名:
String cleanBasicInvalidChars(String input)
参数:
input: 待清理的字符串
返回: 清理后的字符串
LogStorageTool 类
静态属性
| 属性 | 类型 | 说明 | 默认值 |
|---|---|---|---|
write | bool | 是否写入文件 | true |
maxLength | int | 文件最大大小(字节) | 10485760 (10MB) |
nowServerTime | DateTime? | 服务器时间(用于同步) | null |
静态方法
printAndWrite()
底层打印和写入方法。
签名:
static void printAndWrite(Object? info, [bool show = true])
参数:
info: 日志内容(字符串或对象)show: 是否打印到控制台
readLog()
读取所有本地日志。
签名:
static Future<String> readLog()
返回: 日志文本内容
示例:
String logs = await LogStorageTool.readLog();
print(logs);
clear()
清空内存队列和本地日志文件。
签名:
static Future<void> clear()
示例:
await LogStorageTool.clear();
🤝 贡献
欢迎提交 Issue 和 Pull Request!
📄 许可证
MIT License
🔗 相关资源
最后更新: 2026-01-23
版本: 1.0.0