用途
analyze-logs 用于读取并分析来自本地 .evlog/logs/ 目录的结构化宽事件日志,帮助调试错误、排查性能问题、理解应用行为。
适用时机:
- 用户要求调试错误、排查 bug,或理解某次失败的原因
- 用户询问请求模式、慢端点或错误率
- 用户询问应用“发生了什么”或“现在什么情况”
- 用户要求分析日志、查看近期错误或回顾应用行为
- 用户提到他们看到的具体错误信息或状态码
主要能力
该技能可读取由 evlog 文件系统 drain 写入的 .jsonl 日志,按日期组织(如 2026-03-14.jsonl),并优先从最新文件开始分析。
通过 CLI 分析
优先使用项目已安装的 CLI 副本(如 pnpm evlog)。按需拉取的包装器(npx、bunx、npm exec)会在项目未安装时安装 @evlog/cli,运行一个 lockfile 从未锁定的版本,执行前应先询问。
示例命令:
pnpm evlog logs --json # 最近 50 条事件
pnpm evlog logs errors --since 1h --json # 失败的事件
pnpm evlog logs slow --over 1s --json # 慢请求,最慢的在前
pnpm evlog logs "$REQUEST_ID" --json # 某个请求,所有带该 id 的事件
pnpm evlog logs stats --json # 按路由:计数、错误、p50、p95;按状态与级别
pnpm evlog logs --where 'payment.amount>5000' --where audit.outcome=failure --json
--json 是一个信封结构(sources、view、matched、events;stats 则携带 stats)。可用筛选:--since、--until、--level、--path、--status(500 或 5xx)、--where(支持 field=value、field>n、field~regex、field、!field,作用于任意点分字段,可重复)、--limit、--dir(指定非默认目录)、--url(针对通过 HTTP 暴露 readMemoryLogs() 的内存 drain 应用)。从 monorepo 根目录运行时读取每个应用的 .evlog/logs 并为每个事件打标签。
直接读取文件
日志格式有两种:
- NDJSON(默认,pretty: false):每行一个紧凑 JSON 对象,逐行解析。
- Pretty(pretty: true):每个事件为多行缩进 JSON。可通过读取整个文件并按顶层对象切分(如 JSON.parse('[' + content.replace(/}\n{/g, '},{') + ']'))或使用流式 JSON 解析器解析。
检测方式:检查文件开头若干字节,若第二个字符是换行或 ", 则为 NDJSON;若为空格或换行后跟空格,则为 pretty 格式。
搜索顺序
先检查项目根目录下的 .evlog/logs/,再检查应用目录内的 .evlog/logs/(monorepo:apps/*/.evlog/logs/)。查找日志文件:
.evlog/logs/*.jsonl
*/.evlog/logs/*.jsonl
apps/*/.evlog/logs/*.jsonl
程序化读取
evlog 附带读取器:来自 evlog/fs 的 readFsLogs() 与 tailFsLogs(),是异步生成器,能处理两种格式、日期排序与过滤。当项目已安装 evlog 且分析需求超出快速 grep 时优先使用。
内存 drain 备选
部分应用使用 Memory 适配器(evlog/memory),通过仅开发环境的 HTTP 端点经 readMemoryLogs() 暴露近期事件。若 .evlog/logs/ 为空但应用接入了 createMemoryDrain(),则改为查询该端点。
使用方式
未找到日志时
接线新的 drain 前,可尝试 npx evlog doctor --json(只读),检查 evlog 是否已安装、本地 .evlog/logs drain 是否存在。可选,CLI 不可用则跳过。
文件系统 drain 可能未启用。在 Nuxt、Nitro、Next.js、TanStack Start、Hono、Express、Fastify 上,最快路径是 CLI,它能检测框架并一次性接好 fs drain:
npx evlog init --dry-run --yes # 先预览
npx evlog init --yes --drain fs # 应用
运行前需询问。其他框架(或用户拒绝)时,手动设置:
import { createFsDrain } from 'evlog/fs'
// Nuxt / Nitro: server/plugins/evlog-drain.ts
export default defineNitroPlugin((nitroApp) => {
nitroApp.hooks.hook('evlog:drain', createFsDrain())
})
// Hono / Express / Elysia: 在中间件选项中传入
app.use(evlog({ drain: createFsDrain() }))
// Fastify: 在插件选项中传入
await app.register(evlog, { drain: createFsDrain() })
// NestJS: 在模块选项中传入
EvlogModule.forRoot({ drain: createFsDrain() })
// Standalone: 传给 initLogger
initLogger({ drain: createFsDrain() })
设置后,用户需触发一些请求以生成日志,然后重新分析。
日志格式
每行是一个自包含 JSON 对象(宽事件)。关键字段:
- timestamp(string):ISO 8601 时间戳
- level(string):info、warn、error、debug
- service(string):服务名
- environment(string):development、production 等
- method(string):HTTP 方法
- path(string):请求路径(/api/checkout)
- status(number):HTTP 响应状态码
- duration(string):请求耗时,人类可读格式("234ms")
- durationMs(number):请求耗时毫秒值,筛选和排序使用此字段
- requestId(string):唯一请求标识
- error(object):错误详情 name、message、stack、statusCode、data
- error.data.why(string):发生了什么的可读解释
- error.data.fix(string):建议的修复
- source(string):browser 日志为 client,服务端日志该字段缺省
- userAgent(object):解析后的浏览器/操作系统/设备信息
其他字段均为通过 log.set() 添加的应用特定上下文(如 user、cart、payment)。
分析步骤
步骤 1:读取最近的日志文件。读取最新 .jsonl,每行独立解析。
步骤 2:识别相关事件。按用户问题过滤:
- 错误:level 为 error 或 fatal、status >= 500,或事件带 error 对象,即 evlog logs errors 匹配的内容。单独的 4xx 状态属客户端,不计入;但带上述两种信号之一的 4xx 事件仍会匹配,可通过读取 status 或传 --status 4xx 查看全部
- 特定端点:按 path 匹配
- 慢请求:按 durationMs 过滤(如 durationMs > 500)
- 特定用户/操作:按应用特定字段匹配
- 客户端问题:按 "source":"client" 过滤
- 时间范围:比较 timestamp
步骤 3:分析并解释。对每个相关事件:
- 发生了什么:概括 path、method、status、level
- 为何失败(错误):读取 error.message、error.data.why 与堆栈
- 如何修复:查看 error.data.fix
- 上下文:检查应用特定字段获取业务上下文
- 模式:寻找重复错误、性能退化或相关失败
分析模式
- 找出所有错误:过滤 level === "error",按 error.message 或 path 分组,寻找重复模式与常见失败模式
- 找出慢请求:过滤 durationMs > 1000,按 durationMs 降序,寻找特定端点与时段模式
- 追踪特定请求:过滤 requestId,得到单个宽事件
- 按端点看错误率:按 path 分组,统计各路径总数与错误数,寻找高错误率端点
- 客户端与服务器错误:按 source === "client" 与无 source 字段拆分,比较错误模式,寻找无对应服务端错误的客户端错误(网络问题)
注意事项
- 每行是完整自包含事件,与传统日志不同,无需关联多行,一行即包含一个请求的全部上下文
- error.data.why 与 error.data.fix 是 evlog 特有的结构化错误字段,存在时提供最具可操作性的信息
- duration 带单位(如 "706ms"),durationMs 为毫秒值,筛选排序用 durationMs
- 带 "source":"client" 的事件源于浏览器端日志,经 HTTP drain 端点发送到服务器
- 日志文件已自动加入 .gitignore,仅存在于应用运行的本地机器或服务器上
限制
资料未提供价格、收费模式、平台限制或性能指标等信息。日志分析依赖应用已接入 evlog 的 drain;若无日志,需先完成接入并触发请求后重新分析。高版本或按需拉取的 CLI 包装器可能运行未锁定的发布版本,执行前应先询问。