Flutter日志工具

LogPilot是一款专为Flutter应用设计的日志工具,支持实时日志查询、过滤和AI代理访问,适用于开发调试和错误跟踪。

  • by MojtabaTavakkoli
  • JavaScript
  • 本地部署
  • 4 星标
  • 更新于
在 GitHub 查看 MojtabaTavakkoli/log_pilot

使用说明

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需要 开发者——你必须问他们并等待。

  1. 安装MCP服务器。 两个选项——选择一个:

    选项1——全局激活(推荐,适用于所有地方):

    dart pub global activate log_pilot_mcp
    

    选项2——开发人员依赖关系(仅限于您的应用程序):

    dart pub add --dev log_pilot_mcp
    
  2. 确保Flutter应用程序在调试模式下运行。 自动发现 要求应用程序正在运行,以便它可以写入VM服务URI。 如果应用程序未运行,请让开发人员运行 flutter run 第一。 开 安卓/iOS,无法从设备自动发现-- 使用 --project-root 在MCP配置中或要求开发人员运行 log_pilot_mcp write-uri <ws://...> 从主机。

    代理:更喜欢全局激活。 它避免了工作目录 问题-- dart run log_pilot_mcp 仅在目录中工作 里面有包裹 pubspec.yaml,但IDE可能会启动MCP 来自不同目录的服务器。

  3. 在调试模式下运行Flutter应用程序 (如果尚未运行):

    flutter run
    

    应用程序必须正在运行,以便LogPilot可以写入VM服务URI。 对于 Flutter Web,注意 ws://... 调试中打印的URI 控制台——您将在步骤3中需要它。

  4. 编写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\\<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 --做 不 覆盖其他服务器。

  5. 要求开发人员重新加载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的规则目录) 为了正确的代理行为。


目录


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 --->+------------------+
  1. 当你的Flutter应用程序在调试模式下启动时, LogPilot.init() 或 LogPilot.configure() 寄存器 ext.LogPilot.* 服务扩展 在Dart VM上,并将VM服务URI写入 .dart_tool/log_pilot_vm_service_uri.
  2. MCP服务器监视该文件,自动发现URI并连接。
  3. AI代理调用MCP工具(get_snapshot, query_logs等等) 服务器转换为正在运行的应用程序上的服务扩展调用。
  4. 在热重启时,VM扩展重新注册,服务器 自动重新连接。完全重新启动时,URI文件会更新,服务器也会更新 几秒钟内重新连接,无需手动操作。

设置

  1. 安装: dart pub global activate log_pilot_mcp (或 dart pub add --dev log_pilot_mcp)
  2. 运行您的应用程序: flutter run
  3. 将LogPilot添加到IDE的MCP配置中
  4. 重新加载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 目录。在主机上使用以下方法之一:

  1. write-uri 命令 --复制 ws://... 来自调试控制台的URI 并运行: log_pilot_mcp write-uri ws://127.0.0.1:PORT/TOKEN=/ws (添加 --project-root=<APP_PATH> 如果需要)。MCP服务器的文件 监视器检测到更改并自动重新连接。
  2. --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),您有两个回退选项:

  1. 传递项目根 --添加 --project-root=<ABSOLUTE_PATH_TO_YOUR_APP> 到 args 数组因此 服务器知道在哪里可以找到 .dart_tool/log_pilot_vm_service_uri.
  2. 手动传递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 来自活动观察者的最新批次(可订阅)

代理调试工作流

  1. get_snapshot --查看发生了什么(错误、配置、计时器)
  2. set_log_level(level: "verbose") --增加细节
  3. 重现该问题
  4. query_logs(level: "error", deduplicate: true) --找出根本原因
  5. export_for_llm(token_budget: 2000) --获取压缩上下文进行分析
  6. 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 涵盖了绝大多数案件。