日志不再裸奔:pd_log 让打印有型、写盘有序
一句话简介:
pd_log是一个轻量、跨平台的 Flutter 日志插件,用纯 Dart 统一缓冲写入与文件管理,平台侧提供控制台输出与目录事件监听,既好用又省心。
- 插件主页:
https://pub.dev/packages/pd_log - 核心亮点:
- 级别日志:
v/d/i/w/e五级输出,语义清晰。 - 统一缓冲写盘:Dart 写线程+定时/阈值刷新,稳健不阻塞。
 - 文件查询与滚动清理:按年/月/日组织,列出/删除/分页检索。
 - 元数据账本:自动维护 NDJSON 事件账本与 JSON 快照。
 - ANSI 样式:控制台彩色输出可开关;文件始终纯文本,安全可靠。
 - 跨平台一致:Web 输出到控制台(不写文件),接口保持一致。
 
 - 级别日志:
 
安装与初始化
- 在 
pubspec.yaml添加依赖: 
            
            
              yaml
              
              
            
          
          dependencies:
  pd_log: ^0.7.3
        - 引入与配置:
 
            
            
              dart
              
              
            
          
          import 'package:pd_log/pd_log.dart';
void main() {
  PDLog.configure(const PDLogConfig(
    minLevel: LogLevel.debug, // 最低输出级别
    ansiEnabled: true,        // 控制台彩色输出(文件始终纯文本)
    nativeFileLoggingEnabled: true, // 开启本地文件写入(Web 端无效)
  ));
  PDLog.i('pd_log is ready');
}
        - Apple 控制台不想要彩色?
 
            
            
              dart
              
              
            
          
          import 'dart:io';
PDLog.updateConfigure(ansiEnabled: !(Platform.isIOS || Platform.isMacOS));
        会用就会爱:核心能力一览
级别日志与标签
            
            
              dart
              
              
            
          
          PDLog.v('Verbose 细节');
PDLog.d('Debug 调试');
PDLog.i('Info 信息');
PDLog.w('Warning 警告');
PDLog.e('Error 错误', tag: 'Network');
        - 建议:按模块使用 
tag,定位更精准。 
缓冲与刷盘
            
            
              dart
              
              
            
          
          // 通知写线程立即刷盘(平台侧为占位,语义一致)
await PDLog.flushNativeLogs();
        - 适合在应用切后台、退出前调用,确保关键日志落盘。
 
文件查询与读取
            
            
              dart
              
              
            
          
          // 列出最近的日志文件(支持排序/分页的 ListOptions)
final files = await PDLog.listLogFiles(limit: 20);
// 按年/月列出
final filesByYear = await PDLog.listLogFilesByYear(2025);
final filesByMonth = await PDLog.listLogFilesByYearMonth(2025, 10);
// 读取内容
final path = PDLog.logFilePathIfExists(DateTime.now());
if (path != null) {
  final text = await PDLog.readLogFileContent(path);
  PDLog.d('今日日志长度: ${text.length}');
}
        元数据账本与摘要
            
            
              dart
              
              
            
          
          final ledger = await PDLog.metaLedgerContent();   // NDJSON 事件账本
final summary = await PDLog.metaSummaryContent(); // JSON 快照摘要
PDLog.i('Ledger ${ledger.length}, Summary ${summary.length}');
        - 适合做审计与统计,快速把握日志健康度与占用情况。
 
目录结构自检(可视化助理)
            
            
              dart
              
              
            
          
          final tree = await PDLog.fileTreeString(maxDepth: 4);
print(tree); // 一键打印目录树,排查文件组织是否合理
        日志文件存储结构说明
- 根目录:位于应用可写的本地存储(移动端/桌面端);Web 不写文件,仅控制台输出。
 - 分层组织:通常按"年/月"(可选"日")分层,便于分页与滚动清理。
 - 文件命名:包含日期时间戳与可选模块标识,纯文本 UTF-8,适合备份与检索。
 - 元数据:通过 
metaLedgerContent()(NDJSON 账本)与metaSummaryContent()(JSON 摘要)访问,无需直接关心文件名。 - 目录巡检:用 
fileTreeString(maxDepth: ...)一键查看树状结构。 
示例:
            
            
              dart
              
              
            
          
          final text = await PDLog.fileTreeString(maxDepth: 6);
PDLog.v(text);
        输出结果
            
            
              text
              
              
            
          
          /data/user/0/com.example.pd_log_example/files/pd_log
├── .pd_log_meta/
├── pd_log_ledger.jsonl (5322 B)
└── pd_log_summary.json (2582 B)
└── 2025/
    ├── 10/
    │   ├── 15.log (369 B)
    │   ├── 16.log (267 B)
    │   ├── 20.log (16266 B)
    │   └── 28.log (5451 B)
    └── 11/
        └── 03.log (80 B)
        - 保留周期建议:保留最近 90 天,结合 
listLogFilesByYear/Month做滚动清理。 - 合规建议:敏感信息脱敏;为日志目录设置合理权限与备份策略。
 
典型场景配方(打工人的快乐来自少走弯路)
- 接入即稳:统一在 
main()配置PDLogConfig,确保所有模块都有一致行为。 - 网络层:请求成功用 
i,失败用w/e搭配tag: 'HTTP';重要参数用 JSON 美化打印。 - 任务执行:阶段打点用 
d/i,异常用e;必要时flushNativeLogs()保证落盘。 - 日志清理:结合 
listLogFilesByYear/Month做滚动清理(如保留最近 N 月),避免空间被"日志怪兽"吃掉。 - Web 平台:只输出到控制台,不执行文件写入;用统一接口写更少条件判断。
 - 彩色语义:本地开发开 
ansiEnabled,CI 或 Apple 控制台关彩色,读取更清晰。 
风格与礼仪(让日志说人话)
- 级别分工:
v细枝末节(大量输出、仅开发环境);d调试信息(模块内部行为);i业务关键路径(成功、状态变更);w非致命异常(降级、重试);e致命错误(影响功能、需告警)。
 - JSON 美化:序列化对象时尽量结构化输出,避免"猜谜题"。
 - 上下文带上 
tag:组件、页面、模块名,让检索更快乐。 - 慎重输出敏感信息:对账户/隐私数据做脱敏或摘要,符合合规要求。
 
与其它工具配合更香
- 与崩溃上报(如 Sentry)组合:
e级别触发上报,普通i/d留在本地做排障。 - 与限流防抖(如 
pd_cooldown)组合:把重复点击与日志洪水一起拦住,后端同事给你点赞。 
常见问答(FAQ)
- Q:Web 会写文件吗?
- A:不会。Web 端只输出到控制台,但接口一致,逻辑更简单。
 
 - Q:ANSI 彩色会影响文件内容吗?
- A:不会。文件写入始终是纯文本;彩色只作用于控制台输出。
 
 - Q:缓冲写盘是不是"拖延症"?
- A:是"有计划的高效"。写线程+定时/阈值刷新,在性能与可靠性之间拿捏到位。
 
 - Q:如何做滚动清理?
- A:用 
listLogFilesByYear/Month拿到文件集合,结合时间策略批量删除即可。 
 - A:用 
 - Q:我想看目录结构全貌?
- A:
fileTreeString(maxDepth: ...)一键打印目录树,直观又省事。 
 - A:
 
小结与彩蛋
- 总结:
pd_log把日志这件事做得"稳、准、轻",让你专注写功能,不再与日志系统"搏斗"。 - 彩蛋:把 ANSI 彩色合理开关、文件滚动清理配置好------你的磁盘和眼睛都会感谢你。
 
实战故事集(让日志"说书")
- 场景一:用户说"卡了",开发说"我没改",日志说"你们都别吵"。
- 入口层 
i打点(页面进入/按钮点击),服务层d打点(请求开始/结束),异常用w/e,一条时间线串到底------谁"卡",一目了然。 
 - 入口层 
 - 场景二:凌晨两点的报警电话。
- 快速打开最近日志文件,定位 
e级别条目,结合tag: 'HTTP'或模块名,十分钟搞定根因,回去继续睡。 
 - 快速打开最近日志文件,定位 
 - 场景三:新同事刚上手,觉得项目像"迷宫"。
- 给他看 
fileTreeString(maxDepth: 4)的目录树,以及metaSummaryContent()的摘要:从"迷宫"到"景区地图"。 
 - 给他看 
 
进阶姿势(把日志练到"内功心法")
- 
结构化优先:直接打印对象(如
Map/toJson()),pd_log 会优雅美化,别用拼字符串让自己"猜谜题"。- 
示例:
dartfinal user = {'id': 42, 'name': 'Ada', 'vip': true}; PDLog.i(user); // 结构化更友好,后续检索也更舒服 
 - 
 - 
合理采样:高频事件只记录"每 N 次一次",既保留趋势又不灌爆磁盘。
- 
示例:
dartint _counter = 0; void logSampled(dynamic message) { _counter++; if (_counter % 10 == 0) PDLog.d('[sampled x10] $message'); } 
 - 
 - 
账本与摘要:
- 用 
metaLedgerContent()看"流水账",用metaSummaryContent()看"年度总结"。两者配合,既能查细节,也能看全局健康度。 
 - 用 
 - 
刷盘策略:
- 切后台、崩溃前、重要里程碑后,调用 
flushNativeLogs(),关键时刻不丢证据。 
 - 切后台、崩溃前、重要里程碑后,调用 
 - 
滚动清理:
- 每月或每周定期清理旧日志。先用 
listLogFilesByYear/Month找到目标,再按时间策略删除(避免成为"日志收藏家")。 
 - 每月或每周定期清理旧日志。先用 
 
常见故障排查(五分钟速通)
- 看不到文件?
- 检查 
nativeFileLoggingEnabled是否开启;Web 端本来就只输出控制台(不写文件)。 
 - 检查 
 - 控制台"彩虹"太炫影响阅读?
- 关闭 
ansiEnabled或在 Apple 平台按需关闭。 
 - 关闭 
 - 日志"突然不见"?
- 检查 
minLevel,可能被过滤了;或进程退出过快,补一个flushNativeLogs()。 
 - 检查 
 - 内容太碎,检索费劲?
- 统一 
tag约定:模块名/页面名/服务名,形成检索维度。 
 - 统一 
 - 文件太大,磁盘喊疼?
- 做滚动清理,配合采样与级别过滤。
 
 
团队约定清单(可打印贴墙)
- 级别使用:
v(细节,仅开发)、d(调试)、i(业务关键)、w(降级/重试)、e(致命错误)。 - 结构化输出:优先 
toJson()/Map,少拼接字符串。 - Tag 规则:
<层级>/<模块>/<动作>(例如:UI/Profile/Save)。 - 敏感信息:统一脱敏,不打印原文(账号、手机号、Token 等)。
 - 刷盘节点:切后台、退出、重要节点后主动刷盘。
 - 清理频率:每月一次,保留最近 90 天。
 - Web 差异:只在控制台看日志,不追文件。
 
术语小抄(跟 PM 也能聊明白)
- 缓冲写盘:不是"拖延",是"有序"。降低频繁 IO 的性能损耗。
 - 账本与摘要:账本讲细节,摘要讲趋势------像记账和年终复盘。
 - ANSI 彩色:给眼睛减压的调味料,文件仍是朴素纯文本。
 
彩蛋续集(加点"梗"更耐看)
- 把日志级别比作辣度:
v微辣、d中辣、i重口、w冒汗、e冒烟。该出手时才出手,别一把辣椒糊脸。 - 目录树像森林地图:
fileTreeString就是无人机航拍,一眼看穿小路与主干。 - 账本是流水席,摘要是菜单推荐:到底吃了啥,一清二楚。
 
参考资料
- pd_log | Flutter package:pub.dev/packages/pd...