一套完整的 Flutter 日志管理方案,主要为日志本地化缓存。提供了 ADB 日志拉取脚本。

54 阅读7分钟

FlutterLog - Flutter 日志存储与拉取工具

一套完整的 Flutter 日志管理解决方案,包含日志缓存工具和 ADB 日志拉取脚本。

代码地址gitee.com/dxm_code/lo…


📦 项目组成

1. log_storage_tool.dart - Flutter 日志存储工具

高性能的日志缓存和本地存储工具,支持自动节流、队列管理和文件大小控制。

2. pull_log.bat - ADB 日志拉取脚本

Windows 批处理脚本,通过 ADB 从 Android 设备拉取日志文件到本地。


🚀 快速开始

安装日志存储工具

  1. log_storage_tool.dart 复制到你的 Flutter 项目中

  2. pubspec.yaml 中添加依赖:

dependencies:
  path_provider: ^2.0.0
  1. 运行安装:
flutter pub get
  1. 在代码中导入:
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 时:

  1. 读取整个文件到内存
  2. 从中间位置开始查找第一个换行符 (\n)
  3. 保留后半部分内容,覆盖原文件
// 相关常量配置
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:30Log_101530.txt
下午 14:25:45Log_142545.txt
晚上 23:59:59Log_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"
前置要求
  1. ADB 已安装并配置环境变量

    adb version
    
  2. 设备已开启 USB 调试

    • 进入 设置 -> 开发者选项 -> USB 调试
  3. 设备已授权连接

    • 首次连接时,设备会弹出授权提示
常见问题

Q: 提示 "adb 不是内部或外部命令"?
A: ADB 未添加到环境变量,需要配置 Android SDK 的 platform-tools 目录。

Q: 拉取文件为空 (0 字节)?
A:

  • 检查应用是否写入过日志
  • 检查 REMOTE_PATH 路径是否正确
  • 确认应用有存储权限

Q: 提示 "设备不在线"?
A:

  • 运行 adb devices 确认设备状态
  • 尝试 adb kill-serveradb 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 端拉取日志

  1. 连接 Android 设备

  2. 运行脚本:

    # 在脚本所在目录运行
    pull_log.bat
    
    # 或指定设备 ID
    pull_log.bat -s 1234567890ABCDEF
    
  3. 日志自动保存并打开


📊 性能特性

日志写入性能

场景性能表现
单次日志~0.1ms (内存队列)
批量写入3 秒节流,批量写入,减少 I/O
文件裁剪10MB 时触发,保留后 5MB

资源占用

  • 内存: 队列缓存 + 写入缓冲区,通常 < 1MB
  • 存储: 最大 10MB 日志文件
  • CPU: 异步写入,对主线程影响极小

🛡️ 安全与隐私

日志安全建议

  1. 敏感数据处理

    // ❌ 不要直接记录密码
    println('密码:', password);
    
    // ✅ 脱敏处理
    println('密码:', password.replaceAll(RegExp(r'.'), '*'));
    
  2. 生产环境控制

    import 'package:flutter/foundation.dart';
    
    // 生产环境禁用详细日志
    if (kReleaseMode) {
      LogStorageTool.write = false;
    }
    
  3. 定期清理

    // 应用启动时检查日志大小
    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 类

静态属性
属性类型说明默认值
writebool是否写入文件true
maxLengthint文件最大大小(字节)10485760 (10MB)
nowServerTimeDateTime?服务器时间(用于同步)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