


























@@ -14,6 +14,11 @@ import { getRuntimeConfig } from "../config/config.js";
1414import { resolveStorePath } from "../config/sessions/paths.js";
1515import type { OpenClawConfig } from "../config/types.openclaw.js";
1616import { buildGatewayConnectionDetails, callGateway } from "../gateway/call.js";
17+import {
18+DEFAULT_CHANNEL_CONNECT_GRACE_MS,
19+DEFAULT_CHANNEL_STALE_EVENT_THRESHOLD_MS,
20+evaluateChannelHealth,
21+} from "../gateway/channel-health-policy.js";
1722import type { ChannelRuntimeSnapshot } from "../gateway/server-channel-runtime.types.js";
1823import { info } from "../globals.js";
1924import { isTruthyEnvValue } from "../infra/env.js";
@@ -92,6 +97,20 @@ const formatDurationParts = (ms: number): string => {
9297return parts.join(" ");
9398};
9499100+function formatEventLoopHealthLine(summary: HealthSummary): string | null {
101+const eventLoop = summary.eventLoop;
102+if (!eventLoop) {
103+return null;
104+}
105+const state = eventLoop.degraded ? "degraded" : "ok";
106+const reasons = eventLoop.reasons.length > 0 ? ` reasons=${eventLoop.reasons.join(",")}` : "";
107+return `Gateway event loop: ${state}${reasons} max=${Math.round(
108+ eventLoop.delayMaxMs,
109+ )}ms p99=${Math.round(eventLoop.delayP99Ms)}ms util=${eventLoop.utilization} cpu=${
110+ eventLoop.cpuCoreRatio
111+ }`;
112+}
113+95114const resolveHeartbeatSummary = (cfg: OpenClawConfig, agentId: string) =>
96115resolveHeartbeatSummaryForAgent(cfg, agentId);
97116@@ -307,6 +326,7 @@ export async function getHealthSnapshot(params?: {
307326probe?: boolean;
308327includeSensitive?: boolean;
309328runtimeSnapshot?: ChannelRuntimeSnapshot;
329+eventLoop?: HealthSummary["eventLoop"];
310330}): Promise<HealthSummary> {
311331const timeoutMs = params?.timeoutMs;
312332const cfg = getRuntimeConfig();
@@ -432,6 +452,15 @@ export async function getHealthSnapshot(params?: {
432452if (lastProbeAt) {
433453snapshot.lastProbeAt = lastProbeAt;
434454}
455+const health = evaluateChannelHealth(snapshot, {
456+channelId: plugin.id,
457+now: Date.now(),
458+staleEventThresholdMs: DEFAULT_CHANNEL_STALE_EVENT_THRESHOLD_MS,
459+channelConnectGraceMs: DEFAULT_CHANNEL_CONNECT_GRACE_MS,
460+});
461+if (!health.healthy) {
462+snapshot.healthState = health.reason;
463+}
435464436465const summary = plugin.status?.buildChannelSummary
437466 ? await plugin.status.buildChannelSummary({
@@ -483,6 +512,7 @@ export async function getHealthSnapshot(params?: {
483512ok: true,
484513ts: Date.now(),
485514durationMs: Date.now() - start,
515+ ...(params?.eventLoop ? { eventLoop: params.eventLoop } : {}),
486516 ...(pluginHealth ? { plugins: pluginHealth } : {}),
487517 channels,
488518 channelOrder,
@@ -667,6 +697,10 @@ export async function healthCommand(
667697for (const line of channelLines) {
668698runtime.log(styleHealthChannelLine(line, rich));
669699}
700+const eventLoopLine = formatEventLoopHealthLine(summary);
701+if (eventLoopLine) {
702+runtime.log(styleHealthChannelLine(eventLoopLine, rich));
703+}
670704for (const plugin of displayPlugins) {
671705const channelSummary = summary.channels?.[plugin.id];
672706if (!channelSummary || channelSummary.linked !== true) {
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。