LogPilot
](https://pub.dev/packages/log_pilot)   
第一个为AI代理构建的Flutter日志包。
LogPilot为AI编码代理(Cursor、Claude Code、Windsurf、Copilot、, Gemini CLI)通过 MCP服务器 和 DevTools扩展 --没有手动复制粘贴,没有过时的终端输出。 代理可以在运行时查询、过滤、监视和更改日志级别,而您 代码。
它还可以完成您对记录器的所有期望——非常漂亮的控制台输出, 结构化元数据、接收器、文件日志记录、面包屑——但 差异化因素是 代理优先架构:您的AI助手会看到 您的应用程序实时看到的内容。
void main() {
LogPilot.init(child: const MyApp());
}
// Anywhere in your app:
LogPilot.info('User signed in', metadata: {'userId': '123'});
LogPilot.error('Payment failed', error: e, stackTrace: st);______________________________________________________________________
对于AI代理
如果您是AI编码代理,请阅读此自述文件,本节给出 你需要的一切。README的其余部分是为人类开发人员准备的。
设置
该应用程序已经依赖于 log_pilot.使用 LogPilot 对于所有日志记录,从不 原始 print() 或 debugPrint().
import 'package:log_pilot/log_pilot.dart';日志API
LogPilot.verbose('Starting sync...');
LogPilot.debug('Cache key: user_42');
LogPilot.info('Order placed', metadata: {'orderId': 'ORD-456', 'total': 29.99});
LogPilot.warning('Retry attempt 3/5');
LogPilot.error('Checkout failed', error: e, stackTrace: st);
LogPilot.fatal('Database corrupted');每种方法都接受可选 error, stackTrace, metadata (地图),以及 tag (字符串)。总是通过 error + stackTrace 一起出错/致命。
范围记录仪
class AuthService {
static const _log = LogPilotLogger('AuthService');
Future signIn(String email) async {
_log.info('Attempting sign in', metadata: {'email': email});
try {
await _auth.signIn(email);
_log.info('Sign in successful');
} catch (e, st) {
_log.error('Sign in failed', error: e, stackTrace: st);
}
}
}查询日志(进程中)
LogPilot.history; // all records
LogPilot.historyWhere(level: LogLevel.error); // filter by level
LogPilot.historyWhere(tag: 'http', hasError: true); // filter by tag + error
LogPilot.export(format: ExportFormat.json); // NDJSON export
LogPilot.exportForLLM(tokenBudget: 2000); // compressed for LLM context
LogPilot.snapshot(); // structured diagnostic summary
LogPilot.setLogLevel(LogLevel.verbose); // increase verbosity at runtime查询日志(通过MCP)
如果配置了LogPilot MCP服务器,您可以直接调用这些工具:
| MCP工具 | 它的作用 |
|---|---|
get_snapshot | 会话ID、配置、错误计数、最近日志、活动计时器 |
query_logs | 按级别、标签、消息、跟踪ID、错误存在、元数据键进行筛选 |
export_logs | 完整历史记录为文本或NDJSON |
export_for_llm | 针对您的上下文窗口优化的压缩摘要 |
set_log_level | 在运行时更改冗长程度,而无需重新启动 |
get_log_level | 读取当前最低电平 |
clear_logs | 清除内存历史记录 |
watch_logs | 将新条目作为推送通知流式传输(可过滤) |
stop_watch | 停止活动观察者 |
设置MCP服务器
您必须完成以下所有步骤。 MCP服务器将不会出现在 直到开发人员完成步骤4。不要跳过前面-- 在完全完成设置之前,您无法使用LogPilot MCP工具。
先决条件: 应用程序必须调用 LogPilot.init() 或 LogPilot.configure() --两者都在MCP中注册服务扩展 服务器需要。如果使用 configure() (例如,用于Firebase/异步启动), MCP的工作方式完全相同。
步骤1-3是您(代理)执行的操作。步骤4需要 开发者——你必须问他们并等待。
- 安装MCP服务器。 两个选项——选择一个:
选项1——全局激活(推荐,适用于所有地方):
dart pub global activate log_pilot_mcp选项2——开发人员依赖关系(仅限于您的应用程序):
dart pub add --dev log_pilot_mcp- 确保Flutter应用程序在调试模式下运行。 自动发现
要求应用程序正在运行,以便它可以写入VM服务URI。 如果应用程序未运行,请让开发人员运行 flutter run 第一。 开 安卓/iOS,无法从设备自动发现-- 使用 --project-root 在MCP配置中或要求开发人员运行 log_pilot_mcp write-uri 从主机。
> 代理:更喜欢全局激活。 它避免了工作目录 > 问题-- dart run log_pilot_mcp 仅在目录中工作 > 里面有包裹 pubspec.yaml,但IDE可能会启动MCP > 来自不同目录的服务器。
- 在调试模式下运行Flutter应用程序 (如果尚未运行):
flutter run应用程序必须正在运行,以便LogPilot可以写入VM服务URI。 对于 Flutter Web,注意 ws://... 调试中打印的URI 控制台——您将在步骤3中需要它。
- 编写MCP配置文件。
检测IDE并写入正确的位置:
| IDE | 配置文件 |
|---|---|
| 光标 | .cursor/mcp.json 在工作区根目录中 |
| VS代码(副本) | .vscode/mcp.json 在工作区根目录中 |
| 风帆冲浪 | ~/.codeium/windsurf/mcp_config.json |
| 克劳德代码 | ~/.claude/mcp.json |
| Gemini CLI | ~/.gemini/settings.json |
使用以下逻辑构建JSON:
命令: 关于macOS/Linux的使用 "log_pilot_mcp"。在Windows上使用 绝对路径 "C:\\Users\\\\AppData\\Local\\Pub\\Cache\\bin\\log_pilot_mcp.bat". 如果您使用开发依赖项而不是全局激活,请使用 "dart" 和 ["run", "log_pilot_mcp"] 作为args。
Args——只添加需要的内容:
- 窗户: 添加 "--project-root=" - Flutter Web: 添加 "--vm-service-uri=" (每次重新启动时都会更改) - 开发人员依赖关系不在工作区根目录: 添加 "cwd": " " 在服务器级别
典型结果(全局激活,Windows):
{
"mcpServers": {
"LogPilot": {
"command": "C:\\Users\\you\\AppData\\Local\\Pub\\Cache\\bin\\log_pilot_mcp.bat",
"args": ["--project-root=C:/Users/you/my-flutter-app"]
}
}
}典型结果(全局激活,macOS/Linux):
{
"mcpServers": {
"LogPilot": {
"command": "log_pilot_mcp",
"args": []
}
}
}如果配置文件已存在,请合并到现有 mcpServers --做 不 覆盖其他服务器。
- 要求开发人员重新加载IDE窗口并启用LogPilot。
IDE在重新加载之前不会发现新的MCP服务器。请问 开发人员重新加载窗口,在MCP设置中打开LogPilot,如果 需要,并确认绿色/连接指示器。然后打电话 get_snapshot 以验证连接。
代理人规则
- 总是通过
error+stackTrace错误/致命呼叫。 - 使用
tag:对于每个域(Auth,Cart,API,DB,Nav等等)。 - 更喜欢
metadata:映射字符串插值。 - 使用
const LogPilotLogger('Tag')在自动标记的课堂上。 - 永远不要用空的catch块来消除错误——记录它们。
将此块复制到项目的代理指令文件中(例如。 AGENTS.md, GEMINI.md, CLAUDE.md,或IDE的规则目录) 为了正确的代理行为。
______________________________________________________________________
目录
- 对于AI代理
- MCP服务器
- DevTools扩展
- 应用内日志查看器
- LLM出口
- 快速开始
- 控制台输出
- 日志消息
- JSON漂亮打印
- 作用域实例记录器
- 网络日志记录
- 原木水槽
- 文件记录
- 配置预设
- 标记日志和聚焦模式
- 速率限制/重复数据删除
- 日志历史记录/环形缓冲区
- 会话和跟踪ID
- 导航日志
- BLoC观察员
- 性能计时
- 面包屑错误
- 错误ID
- 敏感场遮蔽
- 错误静音
- 运行时日志级别覆盖
- 延迟消息评估
- 仪器助手
- 自我诊断
- Crash Reporter集成
- 诊断快照
- 网络平台
- 测试
- 配置参考
- 包装进口
- 示例应用程序
- 一览特性
- 贡献
- 许可证
- 从plog迁移
______________________________________________________________________
MCP服务器
这 log_pilot_mcp 该软件包是一个独立的MCP服务器,为AI编码代理提供实时服务, 双向访问正在运行的Flutter应用程序的日志。无终端 抓取——代理通过模型上下文协议调用结构化工具。 全局安装(dart pub global activate log_pilot_mcp)或作为a dev依赖性(dart pub add --dev log_pilot_mcp).
运作原理
+----------------+ +------------------+
| Flutter App | -- VM Service ---> | log_pilot_mcp |
| (debug mode) | +------------------+- 当你的Flutter应用程序在调试模式下启动时,
LogPilot.init()或
LogPilot.configure() 寄存器 ext.LogPilot.* 服务扩展 在Dart VM上,并将VM服务URI写入 .dart_tool/log_pilot_vm_service_uri.
- MCP服务器监视该文件,自动发现URI并连接。
- AI代理调用MCP工具(
get_snapshot,query_logs等等)
服务器转换为正在运行的应用程序上的服务扩展调用。
- 在热重启时,VM扩展重新注册,服务器
自动重新连接。完全重新启动时,URI文件会更新,服务器也会更新 几秒钟内重新连接,无需手动操作。
设置
- 安装:
dart pub global activate log_pilot_mcp(或dart pub add --dev log_pilot_mcp) - 运行您的应用程序:
flutter run - 将LogPilot添加到IDE的MCP配置中
- 重新加载IDE窗口并打开LogPilot
对于 根据IDE说明进行详细说明 (光标、VS代码、风帆、克劳德代码、, 反重力,Gemini CLI),请参阅 log_pilot_mcp 自述文件.
自动发现
LogPilot将Dart VM WebSocket URI写入 .dart_tool/log_pilot_vm_service_uri 每次你的应用程序在调试中启动时 模式。MCP服务器:
- 启动时读取文件
- 监视更改(文件系统监视器)
- 如果文件还不存在(服务器在应用程序之前启动),则等待它
出现
- 热重启时:隔离回收、扩展重新注册、服务器检测
隔离事件,并在下次工具调用时重新解析
- 完全重启时:URI更改、文件更新,服务器检测到更改
在观察间隔内重新连接
不需要手动复制URI 在正常的桌面工作流程中。
安卓/iOS: 该应用程序在设备上运行,无法写入主机的 .dart_tool 目录。在主机上使用以下方法之一:
write-uri命令 --复制ws://...来自调试控制台的URI
并运行: log_pilot_mcp write-uri ws://127.0.0.1:PORT/TOKEN=/ws (添加 --project-root= 如果需要)。MCP服务器的文件 监视器检测到更改并自动重新连接。
--project-root--添加--project-root=
MCP服务器args,以便它知道在哪里监视URI文件。
Flutter Web: 自动发现在web上不起作用(否 dart:io).你 必须手动传递VM服务URI——它在每次应用程序重启时都会发生变化。 最简单的方法是复制 ws://... Flutter调试中的URI 控制台并更新 --vm-service-uri MCP配置中的参数。
这 log_pilot_mcp 仓库 提供了可选的帮助脚本(bash+PowerShell),可以自动执行此操作 解析捕获 flutter run 输出。 注: 这些脚本取决于 Flutter控制台输出的确切格式,可能需要调整 跨Flutter SDK版本。
如果桌面上的自动发现失败 (例如。, .dart_tool 不在 预期位置,或应用程序的工作目录与项目不同 Windows上的root),您有两个回退选项:
- 传递项目根 --添加
--project-root= 到 args 数组因此 服务器知道在哪里可以找到 .dart_tool/log_pilot_vm_service_uri.
- 手动传递URI --复制
ws://...Flutter的URI
调试控制台并添加 --vm-service-uri=ws://127.0.0.1:PORT/TOKEN=/ws 到 args 数组in mcp.json.
代理人可以做什么
| 工具 | 它做什么 |
|---|---|
get_snapshot | 结构化摘要:会话ID、配置、历史计数、最近错误、活动计时器。支持 group_by_tag 用于每个标签的细分。 |
query_logs | 按级别、标记、消息文本、跟踪ID、错误存在、元数据键进行筛选。 deduplicate: true 折叠重复的条目,同时保留不同的呼叫站点。 |
export_logs | 完整历史记录为人类可读文本或NDJSON。 |
export_for_llm | 针对LLM上下文窗口优化的压缩摘要--对错误进行优先级排序、重复数据消除、截断冗长条目。 |
set_log_level / get_log_level | 在运行时更改或读取详细信息。曲柄转动至 verbose 对于调试,请返回 warning 当完成时。 |
clear_logs | 擦除内存历史记录。 |
watch_logs | 将新条目作为MCP推送通知进行流式传输。按标签和级别过滤。 |
stop_watch | 停止观察并获取交付摘要。 |
| 资源 | 内容 |
|---|---|
LogPilot://config | 当前LogPilotConfig为JSON格式 |
LogPilot://session | 会话ID和活动跟踪ID |
LogPilot://tail | 来自活动观察者的最新批次(可订阅) |
代理调试工作流
get_snapshot--查看发生了什么(错误、配置、计时器)set_log_level(level: "verbose")--增加细节- *重现该问题*
query_logs(level: "error", deduplicate: true)--找出根本原因export_for_llm(token_budget: 2000)--获取压缩上下文进行分析set_log_level(level: "warning")--恢复安静模式
克劳德代码/终端使用
Flutter Web 没有 dart:io,所以自动发现不可用——你 必须通过 --vm-service-uri 手动。请参阅 log_pilot_mcp Flutter Web文档 有关详细信息和辅助脚本。
如果自动发现在本机上失败 (例如Windows cwd不匹配),添加 --project-root= 到MCP服务器args,或传递 --vm-service-uri 直接。
| 问题 | 解决方案 |
|---|---|
| 服务器在IDE的MCP设置中显示“已禁用” | 切换开关 开 手动。大多数IDE默认禁用新服务器。 |
| 服务器未出现在MCP设置中 | 创建/编辑MCP配置文件后重新加载IDE窗口。 |
Could not find package "log_pilot_mcp" | 快跑 dart pub add --dev log_pilot_mcp 首先在应用程序的目录中。 |
Failed to connect to VM service | 应用程序未在调试模式下运行,或者URI已过时。先启动应用程序。 |
| 未创建自动发现文件 | 在Windows上,应用程序的工作目录可能与项目根目录不匹配。在Android/iOS上,设备无法写入主机。通过 --project-root=,使用 log_pilot_mcp write-uri ,或 --vm-service-uri 手动。 |
| 热重启后工具失败 | 下次调用时自动恢复。如果这种情况持续存在,则VM端口发生了变化(完全重新启动)——URI文件监视器会处理这一点。 |
| 服务器已连接,但工具返回错误 | 应用程序必须 import 'package:log_pilot/log_pilot.dart' 因此,库已加载。 |
请参阅 log_pilot_mcp 自述文件 完整的MCP工具参考、参数表、调试工作流程, 以及故障排除指南。
______________________________________________________________________
DevTools扩展
零配置 --添加 log_pilot 作为依赖和a LogPilot 标签 自动出现在Dart DevTools中。
- 带有颜色编码级别徽章、标签、时间戳和呼叫者位置的实时日志表
- 液位过滤器 下拉菜单+ 标签过滤器 下拉菜单+自由文本搜索
- 自动滚动,手动超控
- 工具栏:刷新、清除、设置日志级别、导出(文本/JSON)、快照
- 详细视图:完整消息、元数据JSON树、错误+堆栈跟踪、错误ID、面包屑时间线,并复制到剪贴板
- 适用于所有平台,包括Flutter Web(使用VM服务扩展,而不是表达式求值)
|按级别+标签筛选|包含元数据和面包屑的详细信息| |:-:|:-:| | DevTools Table | DevTools Detail |
______________________________________________________________________
应用内日志查看器
MaterialApp(
builder: (context, child) => LogPilotOverlay(child: child!),
home: const MyHome(),
)A. 可拖动、可调整大小的底板 具有完整的调试功能:
- 在25%、50%、75%和全屏时捕捉点
- 液位过滤器芯片 (全部、详细、调试、信息、警告、错误、致命)
- 标签过滤器芯片 --从记录的记录中动态生成
- 跨消息、标签和级别的文本搜索
- 记录详细视图 --点击任何条目以获取完整元数据、错误、堆栈跟踪,
面包屑、调用者、会话/跟踪/错误ID,并复制到剪贴板
- 自动滚动切换
- 将完整历史记录复制为文本或NDJSON
- 清除历史记录按钮
汽车正在生产中。覆盖 LogPilotOverlay(enabled: true). 控制FAB位置: LogPilotOverlay(entryButtonAlignment: Alignment.bottomLeft).
|叠加列表视图|记录详细信息视图| |:-:|:-:| | Overlay List | Overlay Detail |
______________________________________________________________________
LLM出口
压缩日志历史记录以适应LLM的上下文窗口:
final summary = LogPilot.exportForLLM(tokenBudget: 2000);该算法对错误进行优先级排序,对连续的相同错误进行重复数据消除 消息,截断详细条目,并用以下内容填充剩余预算 最近的记录。默认预算为4000个令牌(约16k个字符)。
也可通过MCP获得: export_for_llm 工具接受 token_budget 参数,并将压缩的摘要直接返回给代理。
______________________________________________________________________
快速开始
安装
dependencies:
log_pilot: ^1.1.0-beta.1选择您的设置级别
LogPilot提供四种设置选项。 选择一个:
| 设置 | 功能 | 您调用 runApp()? | MCP/DevTools? |
|---|---|---|---|
选项A: LogPilot.init() | 完整设置——错误区域、错误捕获、日志记录。 替换 runApp(). | 没有-- init() 称之为 | 是 |
选项B: LogPilot.configure() | 配置+服务扩展。 没有错误区域,没有自动错误捕获。 | 是 | 是 |
| 选项C: Firebase/异步启动 | configure() 在你自己的 runZonedGuarded最适合之前使用异步初始化的应用程序 runApp()。 | 是 | 是 |
| 选项D: Zero setup | No init--在调试模式下使用默认值。 | 是 | 否 |
init() 对比 configure()
| 能力 | init() | configure() |
|---|---|---|
| 控制台日志记录 | 是 | 是 |
配置预设(.debug(), .production()等) | 是 | 是 |
| 日志历史记录/环形缓冲区 | 是 | 是 |
| 服务扩展(MCP+DevTools) | 是 | 是 |
| 用于自动发现的VM URI文件 | 是 | 是 |
| 日志汇 | 是 | 是 |
| 面包屑 | 是 | 是 |
FlutterError.onError 处理程序 | 是 | 不 --你管理它 |
PlatformDispatcher.onError 处理程序 | 是 | 不 --你管理它 |
runZonedGuarded (未捕获的异步错误) | 是 | 不 --你管理它 |
| 错误级联抑制 | 是 | 不 |
onError 回调(适用于Crashlytics/Sentry) | 是 | 不 --使用水槽或自己的区域 |
选项D(零点设置) 仅为您提供默认的控制台日志记录-- 没有服务扩展、历史记录、接收器或面包屑。
______________________________________________________________________
选项A: LogPilot.init() --完整设置 *(建议用于简单应用程序)*
init()电话runApp()内部。 做 非 也称为runApp()--这样做会导致双重初始化错误。
import 'package:flutter/material.dart';
import 'package:log_pilot/log_pilot.dart';
void main() {
LogPilot.init(child: const MyApp());
}此自动捕获每个Flutter错误、平台错误和未捕获区域 例外情况。它还为DevTools和MCP注册服务扩展。
随着崩溃报告转发:
void main() {
LogPilot.init(
config: LogPilotConfig.debug(),
onError: (error, stack) {
FirebaseCrashlytics.instance.recordError(error, stack);
},
child: const MyApp(),
);
}限制:init()电话WidgetsFlutterBinding.ensureInitialized()和runApp()在自己的区域内。如果你需要运行异步代码 之前runApp()(例如。Firebase.initializeApp()),使用选项B 或者改为C。
选项B: LogPilot.configure() --配置+扩展,您可以处理错误
需要时使用此功能LogPilot日志记录和MCP/DevTools,但希望 自己管理错误处理。configure()注册服务 扩展名和VM URI文件,但确实如此 不 设置错误区域,FlutterError.onError,或PlatformDispatcher.onError.
void main() {
WidgetsFlutterBinding.ensureInitialized();
LogPilot.configure(config: LogPilotConfig(logLevel: LogLevel.info));
runApp(const MyApp());
}选项C:Firebase/Crashlytics/异步启动 *(建议用于生产应用程序)*
这是最常见的生产模式——调用Firebase.initializeApp(),设置Crashlytics,或执行其他异步工作 之前runApp().使用configure()在你自己的runZonedGuarded.
import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_crashlytics/firebase_crashlytics.dart';
import 'package:log_pilot/log_pilot.dart';
void main() {
runZonedGuarded>(() async {
WidgetsFlutterBinding.ensureInitialized();
// Configure LogPilot first — registers service extensions for
// MCP and DevTools, and writes the VM URI for auto-discovery.
LogPilot.configure(config: LogPilotConfig.debug());
await Firebase.initializeApp();
await FirebaseCrashlytics.instance
.setCrashlyticsCollectionEnabled(!kDebugMode);
FlutterError.onError = (details) {
LogPilot.error(
'Flutter error: ${details.summary}',
error: details.exception,
stackTrace: details.stack,
);
FirebaseCrashlytics.instance.recordFlutterError(details);
};
runApp(const MyApp());
}, (error, stack) {
LogPilot.error('Uncaught', error: error, stackTrace: stack);
FirebaseCrashlytics.instance.recordError(error, stack);
});
}为什么configure()而不是init()?init()电话ensureInitialized()和runApp()在它自己的区域内——你不能 跑await Firebase.initializeApp()在两者之间。configure()给予 您可以完全控制绑定初始化、异步设置、错误区域, 和runApp()定时,同时仍在注册服务扩展 MCP和DevTools所需。
选项D:零设置
LogPilot.info('Hello world'); // no init needed in debug mode注: Zero设置仅提供基本的控制台日志记录。服务 扩展(MCP工具和DevTools所需)、日志历史、接收器、, 面包屑都需要LogPilot.init()或LogPilot.configure().
______________________________________________________________________
控制台输出
LogPilot将每个日志包装在一个带有级别、时间戳、, 可点击的呼叫者位置和您的消息:
Console Output — Pretty Format
有三种输出模式可供选择:
| 模式 | 用例 | 示例 | |
|---|---|---|---|
OutputFormat.pretty | IDE中的人(默认) | 框边,彩色 | |
OutputFormat.plain | AI代理/CI | `[INFO] [auth] User signed in \ | {"userId": "123"}` |
OutputFormat.json | 结构化管道 | {"level":"INFO","timestamp":"...","message":"..."} |
|漂亮|普通| NDJSON| |:-:|:-:|:-:| | Pretty | Plain | NDJSON |
______________________________________________________________________
日志消息
每种方法都支持可选 error, stackTrace, metadata,以及 tag:
LogPilot.verbose('Starting sync...');
LogPilot.debug('Cache key: user_42');
LogPilot.info('Order placed', metadata: {'orderId': 'ORD-456', 'total': 29.99});
LogPilot.warning('Retry attempt 3/5');
LogPilot.error('Checkout failed', error: e, stackTrace: st);
LogPilot.fatal('Database corrupted');|详细|信息+元数据| |:-:|:-:| | Verbose | Info |
|错误+堆栈跟踪+面包屑|致命| |:-:|:-:| | Error | Fatal |
______________________________________________________________________
JSON漂亮打印
LogPilot.json('{"users": [{"id": 1, "name": "Alice"}]}');关键帧和值以不同的颜色渲染。自定义 jsonKeyColor 和 jsonValueColor 在配置中。
______________________________________________________________________
作用域实例记录器
创建一个 LogPilotLogger 对于类级日志记录,每个日志都是自动生成的 标记:
class AuthService {
// LogPilotLogger has a const constructor for compile-time constant loggers:
static const _log = LogPilotLogger('AuthService');
// Or use the convenience factory: LogPilot.create('AuthService')
Future signIn(String email) async {
_log.info('Attempting sign in', metadata: {'email': email});
try {
await _auth.signIn(email);
_log.info('Sign in successful');
} catch (e, st) {
_log.error('Sign in failed', error: e, stackTrace: st);
}
}
}所有实例方法都接受可选 tag: override——提供时 替换该单个调用的实例标签。这使得静态和 实例API完全兼容: _log.info('msg', tag: 'http') 作用于 两者都没有代码更改。
作用域记录器还为计时器标签添加前缀: _log.time('query') 生产 AuthService/query.
______________________________________________________________________
网络日志记录
这 超文本传输协议 拦截器内置于已发布的包中:
import 'package:log_pilot/log_pilot.dart';
final client = LogPilotHttpClient();
final response = await client.get(Uri.parse('https://api.example.com/users'));响应日志级别由HTTP状态代码设置:5xx-> error,4xx-> warning,2xx/3xx-> info.
LogPilotHttpClient(
logRequestHeaders: true,
logRequestBody: true,
logResponseHeaders: false,
logResponseBody: true, // opt-in (default: false)
maxResponseBodySize: 4 * 1024, // truncate after 4 KB
injectSessionHeader: true, // adds X-LogPilot-Session / X-LogPilot-Trace
createRecords: true, // creates LogPilotRecord entries in history
)根据状态代码覆盖级别:
LogPilotHttpClient(
logLevelForStatus: (status) =>
status == 429 ? LogLevel.error : LogPilotHttpClient.defaultLogLevelForStatus(status),
)从历史记录中查询网络错误:
final httpErrors = LogPilot.historyWhere(tag: 'http', hasError: true);Dio、Chopper、GraphQL和BLoC集成将无法导入 如果 你是从pub.dev安装的。这些桶是.pubignored只有 存在于 源代码仓库. 现在使用它们: 将源文件从仓库复制到项目中 (例如。lib/src/network/log_pilot_dio_interceptor.dart).独立 包裹(log_pilot_dio,log_pilot_bloc等等)。
______________________________________________________________________
原木水槽
将日志记录与控制台输出一起路由到任何目标:
LogPilot.init(
config: LogPilotConfig(
sinks: [
CallbackSink((record) {
FirebaseCrashlytics.instance.log(record.message ?? '');
}),
],
),
child: const MyApp(),
);灭火 即使控制台输出关闭 (enabled: false),制造 它们非常适合生产。实施 LogSink 对于自定义水槽:
class RemoteSink implements LogSink {
@override
void onLog(LogPilotRecord record) {
httpClient.post(apiUrl, body: record.toJsonString());
}
@override
void dispose() {}
}选择合适的水槽
警告:CallbackSink火灾 同步地 日志内部 调度管道。如果您的回拨更新了ValueNotifier或致电setState(),您将在构建过程中使用“setState()”崩溃BufferedCallbackSink用于UI状态或将回调包裹在scheduleMicrotask().
| 水槽 | 交货 | 最适合 |
|---|---|---|
CallbackSink | 同步,逐记录 | 即发即弃:崩溃记者,分析 |
AsyncLogSink | 微任务批处理 | 昂贵的I/O:HTTP上传、文件写入 |
BufferedCallbackSink | 定时器+基于大小的批处理 | UI状态——避免 setState-期间-build |
// Microtask-batched
AsyncLogSink(flush: (records) {
for (final r in records) { analyticsService.track(r.message ?? ''); }
})
// Timer + size-based
BufferedCallbackSink(
maxBatchSize: 50,
flushInterval: Duration(milliseconds: 500),
onFlush: (batch) { setState(() => logRecords.addAll(batch)); },
)每个水槽 onLog 一个破碎的水槽无法让管道安静下来。
______________________________________________________________________
文件记录
FileSink 自动旋转写入本地文件。 移动/桌面 仅 (要求 dart:io):
import 'dart:io';
import 'package:log_pilot/log_pilot.dart';
import 'package:log_pilot/log_pilot_io.dart';
final fileSink = FileSink(
directory: Directory('/path/to/logs'),
maxFileSize: 2 * 1024 * 1024, // 2 MB per file
maxFileCount: 5,
format: FileLogFormat.text, // or .json for NDJSON
baseFileName: 'LogPilot',
);
LogPilot.init(
config: LogPilotConfig(sinks: [fileSink]),
child: const MyApp(),
);
// Export all logs for bug reports:
final allLogs = await fileSink.readAll();______________________________________________________________________
配置预设
LogPilotConfig.debug() // verbose, all details, colors on
LogPilotConfig.staging() // info+, compact, 5s dedup window
LogPilotConfig.production( // console off, warning+, sinks only
sinks: [myCrashlyticsSink],
)
LogPilotConfig.web() // info+, plain output, no caller capture, 5s dedup| 工厂 | 日志级别 | 呼叫者 | 详细信息 | 重复数据删除 | 历史记录/面包屑 | 最适合 |
|---|---|---|---|---|---|---|
LogPilotConfig() | verbose | 是 | 是 | 关闭 | 500/20 | 默认 |
.debug() | verbose | 是 | 是 | 关闭 | 500/20 | IDE开发 |
.staging() | 信息 | 是 | 否 | 5s | 500/20 | QA构建 |
.production() | 警告 | 否 | 否 | 5s | 500/20 | 释放(控制台关闭) |
.web() | info | 否 | 否 | 5s | 200/10 | Flutter Web(也: stackTraceDepth: 4, maxPayloadSize: 4096) |
______________________________________________________________________
标记日志和聚焦模式
LogPilot.info('Starting payment', tag: 'checkout');
// Only show specific tags during development:
LogPilotConfig(onlyTags: {'checkout', 'auth'})______________________________________________________________________
速率限制/重复数据删除
在时间窗口内折叠相同的消息:
LogPilotConfig(deduplicateWindow: Duration(seconds: 5))当相同的消息+级别重复时,只打印第一个。之后 在窗口中,将显示摘要:
│ RenderFlex overflowed by 42.0 pixels
│ ... repeated 47 times重复数据删除适用于控制台输出和接收器调度。这 在记忆中,历史仍然会接收每一条记录。
______________________________________________________________________
日志历史记录/环形缓冲区
final records = LogPilot.history;
final errors = LogPilot.historyWhere(level: LogLevel.error);
final text = LogPilot.export();
final json = LogPilot.export(format: ExportFormat.json);
LogPilot.clearHistory();historyWhere 支持丰富的过滤功能——所有参数均与AND结合使用 逻辑。 这 level 参数是最小严重性筛选器,不是 完全匹配-- LogLevel.warning 返回警告、错误和致命信息:
LogPilot.historyWhere(
level: LogLevel.warning,
tag: 'http',
messageContains: 'timeout',
traceId: 'req-abc',
hasError: true,
after: DateTime.now().subtract(const Duration(minutes: 5)),
before: DateTime.now(),
metadataKey: 'statusCode',
);配置缓冲区大小(默认值500,设置为0禁用):
LogPilotConfig(maxHistorySize: 1000)______________________________________________________________________
会话和跟踪ID
每次应用程序启动都会获得一个唯一的会话UUID:
print(LogPilot.sessionId); // "a1b2c3d4-e5f6-4a7b-..."对于每个请求的相关性,请使用作用域助手:
await LogPilot.withTraceId('req-12345', () async {
await processPayment(); // all logs carry traceId 'req-12345'
await sendReceipt();
});
// traceId is null here — even if processPayment threw同步变体也可用:
final total = LogPilot.withTraceIdSync('calc-1', () => computeTotal(cart));网络拦截器自动注入 X-LogPilot-Session 和 X-LogPilot-Trace 标题。
______________________________________________________________________
导航日志
自动记录每次路线转换:
MaterialApp(
navigatorObservers: [LogPilotNavigatorObserver()],
)定制:
LogPilotNavigatorObserver(
logLevel: LogLevel.info,
tag: 'Nav',
logArguments: false, // hide sensitive route arguments
)______________________________________________________________________
BLoC观察员
尚未发布 --此导入 将失败 如果您已安装log_pilot来自pub.dev。BLoC集成在 只有来源。 现在使用它: 复制lib/src/state/log_pilot_bloc_observer.dart在您的项目中,调整导入。独立log_pilot_bloc计划好了。
记录BLoC/Cubit生命周期事件:
// ⚠ REPO ONLY — this import does not work from the pub.dev package.
// See the note above for how to use this integration today.
import 'package:log_pilot/log_pilot_bloc.dart';
void main() {
Bloc.observer = LogPilotBlocObserver();
LogPilot.init(child: const MyApp());
}定制:
LogPilotBlocObserver(
tag: 'state',
logEvents: true,
logTransitions: true,
logCreations: false,
transitionLevel: LogLevel.debug,
)______________________________________________________________________
性能计时
LogPilot.time('fetchUsers');
final users = await api.fetchUsers();
LogPilot.timeEnd('fetchUsers'); // logs: "fetchUsers: 342ms"异常安全范围计时:
final users = await LogPilot.withTimer('fetchUsers', work: () => api.getUsers());
final config = LogPilot.withTimerSync('parseConfig', work: () => parse(raw));多个计时器同时运行。范围记录器自动前缀:
final log = LogPilot.create('DB');
log.time('query'); // label: "DB/query"
log.timeEnd('query'); // logs: "DB/query: 12ms" with tag "DB"timeCancel 删除计时器而不记录经过的时间。如果没有 匹配计时器存在,a verbose-记录级别提示以帮助检测 标签拼写错误或双重取消。注: timeEnd 日志在 warning 缺少计时器的级别,同时 timeCancel 日志在 verbose --这个 是故意的,因为 timeEnd 表示预期的测量值,即 失踪。
______________________________________________________________________
面包屑错误
每次错误前自动跟踪事件:
LogPilot.info('User tapped checkout', tag: 'UI');
LogPilot.info('Cart validated', tag: 'Cart');
LogPilot.error('Payment failed', error: e, stackTrace: st);
// ↑ Breadcrumbs for the 2 prior events are attached手动面包屑:
LogPilot.addBreadcrumb('Button tapped', category: 'ui');
LogPilot.addBreadcrumb('Theme changed', category: 'state', metadata: {'theme': 'dark'});配置: LogPilotConfig(maxBreadcrumbs: 30) (默认值为20,0表示禁用)。
______________________________________________________________________
错误ID
每个错误/致命日志都会收到一个基于哈希的确定性ID:
LogPilot.error('Network timeout', error: TimeoutException('connect'));
// Record includes: errorId: "lk-a1b2c3"相同的错误签名在会话之间总是产生相同的ID。 数值变化被归一化——“索引5超出范围10”和 “索引3超出范围8”产生相同的ID。
______________________________________________________________________
敏感场遮蔽
LogPilotConfig(
maskPatterns: [
'password', // substring — masks any key containing "password"
'=accessToken', // exact — masks only the key "accessToken"
'~^(refresh|auth)_.*', // regex — matches keys via RegExp
'Authorization',
'secret',
],
)| 前缀 | 匹配类型 | 示例 | 掩码 |
|---|---|---|---|
| *(无)* | 变电站 | 'token' | accessToken, tokenExpiry, refresh_token |
= | 精确密钥 | '=accessToken' | accessToken 只有 |
~ | 正则表达式 | '~^api_key$' | api_key 只有 |
递归掩码适用于标头和嵌套的JSON正文。
______________________________________________________________________
错误静音
抑制控制台中已知的嘈杂错误——崩溃记者仍然会收到这些错误:
LogPilotConfig(silencedErrors: {'RenderFlex overflowed', 'HTTP 404'})______________________________________________________________________
运行时日志级别覆盖
在不编辑代码或重新启动的情况下更改详细程度:
LogPilot.setLogLevel(LogLevel.verbose); // crank up for debugging
// ... reproduce the issue ...
LogPilot.setLogLevel(LogLevel.warning); // quiet down______________________________________________________________________
延迟消息评估
LogPilot.debug(() => 'Cache: ${cache.entries.map((e) => e.key).join(", ")}');只有当调试级别处于活动状态时,才会调用闭包。
______________________________________________________________________
仪器助手
使用自动计时、结果记录和错误捕获来包装任何表达式:
final config = LogPilot.instrument('parseConfig', () => parseConfig(raw));
final users = await LogPilot.instrumentAsync('fetchUsers', () => api.getUsers());关于成功:登录 debug 返回值和经过的时间。 失败时:登录 error 使用异常和堆栈跟踪级别,然后重新抛出。
______________________________________________________________________
自我诊断
监控LogPilot自身的性能并自动降低冗长程度 当吞吐量达到峰值时:
LogPilot.enableDiagnostics(
autoDegrade: true,
throughputThreshold: 50, // records per second before degrading
);
final snap = LogPilot.diagnostics?.snapshot;
// LogPilotDiagnosticsSnapshot(records: 142, avgSinkLatency: 34us, ...)
LogPilot.disableDiagnostics();当吞吐量超过阈值时,最小日志级别为 自动提升至 warning,通过过滤来减少冗长 详细/调试/信息消息。当吞吐量降至一半以下时 阈值,恢复到原始水平。
______________________________________________________________________
Crash Reporter集成
使用 init()s onError 简单应用程序的回调,或 configure() 里面 你自己的 runZonedGuarded 用于Firebase/async-启动。看 选项A 和 选项C 在快速入门中查看完整的代码示例。
______________________________________________________________________
诊断快照
最近LogPilot活动的一个结构化摘要:
final snap = LogPilot.snapshot();
// Returns Map with: sessionId, traceId, config, history counts,
// recentErrors (last 5), recentLogs (last 10), activeTimers
final jsonStr = LogPilot.snapshotAsJson();按标签对最近的日志进行分组:
final snap = LogPilot.snapshot(groupByTag: true, perTagLimit: 3);
// snap['recentByTag']['Auth'] -> {total: 15, recent: [...last 3...]}______________________________________________________________________
网络平台
地心末日 package:log_pilot/log_pilot.dart 完全与网络兼容--零 dart:io 附属国。所有功能都适用于Flutter Web:
- 控制台输出、日志历史、导航观察器、计时
- 应用内日志查看器覆盖
- 网络日志记录
LogPilotHttpClient - DevTools扩展
- BLoC观察者(仅限回购——请参阅 包装进口)
文件日志记录需要 dart:io --进口 package:log_pilot/log_pilot_io.dart 仅适用于移动设备/台式机。使用 LogPilotConfig.web() 用于优化web默认设置。
______________________________________________________________________
测试
tearDown(() {
LogPilot.reset(); // clears config, history, timers, and trace IDs
});______________________________________________________________________
配置参考
| 选项 | 类型 | 默认值 | 描述 |
|---|---|---|---|
enabled | bool | kDebugMode | 主开关。释放。 |
logLevel | LogLevel | verbose | 打印的最低严重性 |
outputFormat | OutputFormat | pretty | pretty / plain / json |
showTimestamp | bool | true | 显示HH:mm:ss。SSS |
showCaller | bool | true | 可点击的源位置 |
showDetails | bool | true | 错误体、堆栈跟踪 |
colorize | bool | true | ANSI颜色 |
maxLineWidth | int | 100 | 框宽度(字符) |
stackTraceDepth | int | 8 | 显示的最大堆栈帧数 |
maxPayloadSize | int | 10240 | 截断有效载荷(字节) |
maskPatterns | List | ['Authorization', 'password', 'token', 'secret'] | 要屏蔽的字段(=exact, ~regex,或子字符串) |
jsonKeyColor | AnsiColor | cyan | JSON密钥颜色 |
jsonValueColor | AnsiColor | green | JSON值颜色 |
silencedErrors | Set | {} | 抑制匹配错误 |
onlyTags | Set | {} | 仅打印匹配的标签 |
sinks | List | [] | 其他输出目的地 |
deduplicateWindow | Duration | Duration.zero | 折叠相同的消息(控制台+接收器) |
maxHistorySize | int | 500 | 环形缓冲区大小(0=关闭) |
maxBreadcrumbs | int | 20 | 面包屑缓冲区(0=关闭) |
______________________________________________________________________
包装进口
| 导入 | 你得到了什么 | 网络安全? | 出版? |
|---|---|---|---|
package:log_pilot/log_pilot.dart | 核心: LogPilot, LogPilotLogger, LogPilotConfig, LogPilotRecord, LogLevel, LogSink, CallbackSink, AsyncLogSink, BufferedCallbackSink, LogHistory, ExportFormat, LogPilotNavigatorObserver, LogPilotOverlay, LogPilotHttpClient,ANSI助手 | 是 | 是 |
package:log_pilot/log_pilot_io.dart | FileSink, FileLogFormat (要求 dart:io) | 否 | 是 |
package:log_pilot/log_pilot_dio.dart | LogPilotDioInterceptor (添加 dio 到pubspec) | 是 | 不 --仅限回购\* |
package:log_pilot/log_pilot_chopper.dart | LogPilotChopperInterceptor (添加 chopper) | 是 | 不 --仅限回购\* |
package:log_pilot/log_pilot_graphql.dart | LogPilotGraphQLLink (添加 gql, gql_exec, gql_link) | 是 | 不 --仅限回购\* |
package:log_pilot/log_pilot_bloc.dart | LogPilotBlocObserver (添加 bloc) | 是 | 不 --仅限回购\* |
\* 这些导入将从pub.dev包中失败。 他们在 源代码仓库,但.pubignored来自已公布的文件包。复制源 将文件放入项目中,或等待独立包(例如。log_pilot_dio)在a 未来的释放。
______________________________________________________________________
示例应用程序
一个完整的可运行示例,每个功能都有可点击的按钮 example/:
cd example && flutter run______________________________________________________________________
一览特性
| 功能 | 它的作用 |
|---|---|
| MCP服务器 | AI代理通过MCP协议查询、过滤、监视和控制实时日志 |
| DevTools扩展 | Dart DevTools中的实时日志查看器选项卡--零配置 |
| 应用内日志查看器 | LogPilotOverlay 带有过滤器、搜索和实时更新的调试表 |
| LLM出口 | 压缩AI上下文窗口的日志历史记录 |
| 单线设置 | 更换 runApp() 和 LogPilot.init() --每个错误都是自动格式化的 |
| 漂亮的Flutter错误 | 15+上下文提示、简化堆栈、可点击源位置 |
| 基于级别的日志记录 | verbose / debug / info / warning / error / fatal 具有结构化元数据和标签 |
| 范围记录仪 | const LogPilotLogger('Tag') 或 LogPilot.create('Tag') 用于类级自动标记 |
| 原木水槽 | 将记录路由到文件、Crashlytics、Sentry或任何后端 |
| 内置文件日志 | FileSink 按大小、文本或JSON格式自动旋转 |
| 懒惰的消息 | LogPilot.debug(() => expensiveString()) --筛选时跳过工作 |
| 网络拦截器 | http(已发布);Dio、Chopper、GraphQL(回购/未来包) |
| JSON突出显示 | 自动检测和着色键/值 |
| 敏感场屏蔽 | 标头和JSON正文中的递归掩码 |
| 配置预设 | LogPilotConfig.debug(), .staging(), .production(), .web() |
| 速率限制/数据消除 | 在时间窗口内折叠相同的消息 |
| 日志历史记录 | 内存环形缓冲区——过滤、导出、附加到错误报告 |
| 输出格式 | pretty, plain, json --人机模式 |
| 诊断快照 | LogPilot.snapshot() --bug报告的一次通话摘要 |
| 面包屑错误 | 每次出错前自动跟踪事件 |
| 错误ID | 确定性 lk-XXXXXX 用于跨会话跟踪的哈希 |
| 运行时日志级别覆盖 | 在运行时更改冗长程度,而无需重新启动 |
| 仪器助手 | 一行计时+任何表达式的错误捕获 |
| 会话和跟踪ID | 自动生成的会话UUID+每个请求的跟踪ID |
| 导航日志 | 自动记录推送/弹出/替换路由名称和参数 |
| BLoC观察者 | 日志创建/关闭、事件、状态更改和错误 |
| 性能计时 | LogPilot.time / LogPilot.timeEnd --喜欢 console.time |
| 网络兼容 | 岩芯筒 dart:io-免费——适用于Flutter Web |
| 轻质核心 | Flutter SDK之外无需依赖;Dio、Chopper、GraphQL、BLoC集成可用 源代码仓库 (计划提供独立软件包) |
______________________________________________________________________
贡献
欢迎捐款。看 贡献.md 为了 架构细节和开发设置。
______________________________________________________________________
许可证
麻省理工学院——见 许可证.
______________________________________________________________________
从plog迁移
如果您正在从升级 plog 包,这是一个快速映射:
| plog | log_pilot |
|---|---|
import 'package:plog/plog.dart' | import 'package:log_pilot/log_pilot.dart' |
Plog.init(child: ...) | LogPilot.init(child: ...) |
Plog.info(...) | LogPilot.info(...) |
PlogLogger('Tag') | LogPilotLogger('Tag') (仍可施工) |
Plog.create('Tag') | LogPilot.create('Tag') |
PlogConfig(...) | LogPilotConfig(...) |
PlogRecord | LogPilotRecord |
plog_dio.dart | log_pilot_dio.dart (仅限回购) |
plog_bloc.dart | log_pilot_bloc.dart (仅限回购) |
PlogNavigatorObserver | LogPilotNavigatorObserver |
PlogOverlay | LogPilotOverlay |
PlogHttpClient | LogPilotHttpClient |
所有API在功能上都是相同的——只是名称发生了变化。A. 项目范围内的查找和替换 Plog → LogPilot 和 plog → log_pilot 涵盖了绝大多数案件。
