关键词:WebSocket 日志|耗时追踪|紧凑模式|结构化日志|终端友好|性能优先
在 OpenClaw 的多端架构中,WebSocket 是 Web UI 与后端通信的核心通道。每秒可能有数十条 ACP(Agent Communication Protocol)消息穿梭其间——包含用户输入、AI 响应、工具调用、审批请求等。
若直接打印原始 JSON,日志将迅速变成不可读的噪音海洋:
{"method":"chat.sendMessage","params":{"sessionKey":"web_abc","content":"重启服务"}}
{"method":"tool.call.request","params":{"runId":"r-xyz","tool":"bash_exec","args":{"cmd":"kubectl...}}}为解决这一问题,OpenClaw 设计了 src/utils/ws-log.ts —— 一个智能、自适应、终端友好的 WebSocket 日志器。它不仅记录事件,更通过耗时追踪、动态过滤与视觉编码,让日志真正“可读可用”。
本文将详解其三大核心机制:
ws-log.ts 遵循三条原则:
少即是多,慢即是警。
ACP 协议天然具备请求-响应对特性(如 tool.call.request → tool.call.result)。ws-log.ts 利用此特性自动追踪耗时。
// ws-log.ts
const pendingRequests = new Map<string, { method: string; start: number }>();
export function logOutgoing(message: ACPMessage) {
if (message.id && isRequestMessage(message)) {
pendingRequests.set(message.id, {
method: message.method,
start: Date.now()
});
}
// ... 打印发出日志
}
export function logIncoming(message: ACPMessage) {
if (message.id && pendingRequests.has(message.id)) {
const req = pendingRequests.get(message.id)!;
const duration = Date.now() - req.start;
// 打印带耗时的响应日志
printLog('←', `${req.method} [${duration}ms]`, 'dim');
pendingRequests.delete(message.id);
}
// ... 其他消息处理
}→ tool.call.request (runId=r-abc)
← tool.call.result [2450ms] # 自动标注耗时无需埋点,自动获得性能洞察。
为避免日志爆炸,ws-log.ts 默认启用 Compact Mode:

const SLOW_THRESHOLD_MS = 1000; // >1s 视为慢紧凑模式(默认)
→ chat.sendMessage (web_abc)
→ tool.call.request (r-xyz)
← tool.call.result [2450ms] ✗ # 因超时被记录详细模式(调试开启)
→ chat.sendMessage {"sessionKey":"web_abc","content":"重启"}
→ tool.call.request {"runId":"r-xyz","tool":"bash_exec",...}
← tool.call.result {"runId":"r-xyz","error":"Command failed",...}只在需要时,才展示细节。
ws-log.ts 使用 ANSI 颜色与 Unicode 图标,大幅提升可扫描性:

chalk)import chalk from 'chalk';
function printLog(
arrow: '→' | '←',
message: string,
status?: 'success' | 'error' | 'slow'
) {
const color = arrow === '→' ? 'blue' : 'green';
let icon = '';
if (status === 'success') icon = ' ✓';
if (status === 'error') icon = ' ✗';
if (status === 'slow') icon = chalk.yellow(' [SLOW]');
console.log(chalk[color](`${arrow} ${message}${icon}`));
}→ chat.sendMessage (web_abc)
→ tool.call.request (r-xyz)
← tool.call.result [2450ms] ✗ ← 红色 "✗" + 黄色 "[2450ms]"一眼识别流向、状态与性能瓶颈。
尽管终端日志高度可读,ws-log.ts 同时输出结构化 JSON 到文件,供 SIEM 或日志系统消费:
// ws-log.jsonl
{"direction":"out","method":"chat.sendMessage","timestamp":1710234567,"sessionId":"web_abc"}
{"direction":"in","method":"tool.call.result","duration":2450,"error":true,"runId":"r-xyz"}人机各取所需。
pendingRequests Map 定期清理超时请求(>30s)process.env.NODE_ENV !== 'production' 时启用# 仅错误
LOG_LEVEL=error npm start
# 详细模式
WS_LOG_VERBOSE=1 npm start在 Web UI 中点击“调试模式”,自动发送 ACP 指令:
{
"method": "system.setLogLevel",
"params": { "level": "verbose", "duration": 300 } // 5 分钟
}后端动态调整 ws-log.ts 行为,无需重启。
调试随需启停,生产保持安静。
ws-log.ts 的存在,体现了 OpenClaw 对开发者的尊重:我们不仅构建系统,更构建理解系统的工具。通过将枯燥的协议流转化为富有语义的视觉叙事,它让每一次交互都可追溯、可理解、可优化。
在 AI 系统日益复杂的今天,良好的可观测性不是奢侈品,而是安全与效率的基石。
在下一篇中,我们将探讨 OpenClaw 的测试体系:如何对非确定性 AI 系统进行确定性验证。
下一篇预告: 第 20 篇:从零部署 OpenClaw —— 实战:接入 WhatsApp + 创建自定义 Skill
您的 AI 助手,从此由您定义。若感兴趣可以浏览本书其他章节内容:
openclaw.mjs、config.yaml 与环境变量管理run.ts 上篇 —— 模型调度、账号轮询与上下文守护机制run.ts 下篇 —— 故障转移、重试策略与结果封装memory-search.ts 中的 RAG 配置解析与合并逻辑exec.ts 上篇 —— 安全执行 Shell 命令的三层隔离模型exec.ts 下篇 —— 用户审批、后台任务与权限提升控制process.ts —— AI 如何像开发者一样管理后台进程server-channels.ts —— 渠道插件生命周期管理器session.ts 与 Baileys 的健壮连接管理monitor-inbox.ts 如何解析、去重与防抖chat.ts 中的历史查询、发送与中止逻辑ws-log.ts 如何让 WebSocket 日志可读可用