使用说明
LogPilot
第一个为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<void> 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 <ws://...>从主机。代理:更喜欢全局激活。 它避免了工作目录 问题--
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.jsonGemini CLI ~/.gemini/settings.json使用以下逻辑构建JSON:
命令: 关于macOS/Linux的使用
"log_pilot_mcp"。在Windows上使用 绝对路径"C:\\Users\\<USER>\\AppData\\Local\\Pub\\Cache\\bin\\log_pilot_mcp.bat". 如果您使用开发依赖项而不是全局激活,请使用"dart"和["run", "log_pilot_mcp"]作为args。Args——只添加需要的内容:
- 窗户: 添加
"--project-root=<ABSOLUTE_PATH_TO_APP>" - Flutter Web: 添加
"--vm-service-uri=<URI_FROM_STEP_2>"(每次重新启动时都会更改) - 开发人员依赖关系不在工作区根目录: 添加
"cwd": "<PATH_TO_APP>"在服务器级别
典型结果(全局激活,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) | <-- ext.LogPilot.* | (MCP server) |
+-------+--------+ +---------+--------+
| |
| writes URI | MCP protocol
| on start |
v v
.dart_tool/ +------------------+
log_pilot_vm_service_uri | Cursor / Claude |
| | Windsurf / ... |
+--- watched by MCP server --->+------------------+
- 当你的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=<APP_PATH>如果需要)。MCP服务器的文件 监视器检测到更改并自动重新连接。--project-root--添加--project-root=<ABSOLUTE_PATH_TO_YOUR_APP>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=<ABSOLUTE_PATH_TO_YOUR_APP>到args数组因此 服务器知道在哪里可以找到.dart_tool/log_pilot_vm_service_uri. - 手动传递URI --复制
ws://...Flutter的URI 调试控制台并添加--vm-service-uri=ws://127.0.0.1:PORT/TOKEN=/ws到args数组inmcp.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=<ABSOLUTE_PATH_TO_APP> 到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=<APP_PATH>,使用 log_pilot_mcp write-uri <ws://...>,或 --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服务扩展,而不是表达式求值)
| 按级别+标签筛选 | 包含元数据和面包屑的详细信息 |
|---|---|
应用内日志查看器
MaterialApp(
builder: (context, child) => LogPilotOverlay(child: child!),
home: const MyHome(),
)
A. 可拖动、可调整大小的底板 具有完整的调试功能:
- 在25%、50%、75%和全屏时捕捉点
- 液位过滤器芯片 (全部、详细、调试、信息、警告、错误、致命)
- 标签过滤器芯片 --从记录的记录中动态生成
- 跨消息、标签和级别的文本搜索
- 记录详细视图 --点击任何条目以获取完整元数据、错误、堆栈跟踪, 面包屑、调用者、会话/跟踪/错误ID,并复制到剪贴板
- 自动滚动切换
- 将完整历史记录复制为文本或NDJSON
- 清除历史记录按钮
汽车正在生产中。覆盖 LogPilotOverlay(enabled: true).
控制FAB位置: LogPilotOverlay(entryButtonAlignment: Alignment.bottomLeft).
| 叠加列表视图 | 记录详细信息视图 |
|---|---|
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<Future<void>>(() 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将每个日志包装在一个带有级别、时间戳、, 可点击的呼叫者位置和您的消息:
有三种输出模式可供选择:
| 模式 | 用例 | 示例 |
|---|---|---|
OutputFormat.pretty |
IDE中的人(默认) | 框边,彩色 |
OutputFormat.plain |
AI代理/CI | [INFO] [auth] User signed in | {"userId": "123"} |
OutputFormat.json |
结构化管道 | {"level":"INFO","timestamp":"...","message":"..."} |
| 漂亮 | 普通 | 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');
| 详细 | 信息+元数据 |
|---|---|
| 错误+堆栈跟踪+面包屑 | 致命 |
|---|---|
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<void> 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集成在 GitHub仓库 只有来源。现在使用它: 复制
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<String> |
['Authorization', 'password', 'token', 'secret'] |
要屏蔽的字段(=exact, ~regex,或子字符串) |
jsonKeyColor |
AnsiColor |
cyan |
JSON密钥颜色 |
jsonValueColor |
AnsiColor |
green |
JSON值颜色 |
silencedErrors |
Set<String> |
{} |
抑制匹配错误 |
onlyTags |
Set<String> |
{} |
仅打印匹配的标签 |
sinks |
List<LogSink> |
[] |
其他输出目的地 |
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 涵盖了绝大多数案件。