@openclaw/diagnostics-prometheus 2026.7.2-beta.7 → 2026.8.1-beta.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +133 -124
- package/package.json +4 -4
package/dist/index.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { isInternalDiagnosticEventMetadata, redactSensitiveText } from "./api.js";
|
|
2
2
|
import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry";
|
|
3
|
+
import { normalizeDiagnosticLane, normalizeDiagnosticValue } from "openclaw/plugin-sdk/diagnostic-runtime";
|
|
4
|
+
import { asNonNegativeFiniteNumber } from "openclaw/plugin-sdk/number-runtime";
|
|
3
5
|
import { truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
|
|
4
6
|
//#region extensions/diagnostics-prometheus/src/service.ts
|
|
5
7
|
const DURATION_BUCKETS_SECONDS = [
|
|
@@ -61,29 +63,10 @@ const RATIO_BUCKETS = [
|
|
|
61
63
|
8,
|
|
62
64
|
16
|
|
63
65
|
];
|
|
64
|
-
const LOW_CARDINALITY_VALUE_RE = /^[A-Za-z0-9_.:-]{1,120}$/u;
|
|
65
66
|
const MAX_PROMETHEUS_SERIES = 2048;
|
|
66
67
|
const DROPPED_SERIES_COUNTER_NAME = "openclaw_prometheus_series_dropped_total";
|
|
67
|
-
function lowCardinalityLabel(value, fallback = "unknown") {
|
|
68
|
-
if (!value) return fallback;
|
|
69
|
-
const redacted = redactSensitiveText(value.trim());
|
|
70
|
-
const redactedLower = redacted.toLowerCase();
|
|
71
|
-
if (redactedLower.startsWith("agent:") || redactedLower.includes(":agent:")) return fallback;
|
|
72
|
-
return LOW_CARDINALITY_VALUE_RE.test(redacted) ? redacted : fallback;
|
|
73
|
-
}
|
|
74
|
-
function lowCardinalityQueueLaneLabel(value, fallback = "unknown") {
|
|
75
|
-
if (!value) return fallback;
|
|
76
|
-
const redacted = redactSensitiveText(value.trim());
|
|
77
|
-
if (redacted.toLowerCase().startsWith("agent:")) return fallback;
|
|
78
|
-
const scopedLaneIndex = redacted.indexOf(":");
|
|
79
|
-
const lane = scopedLaneIndex >= 0 ? redacted.slice(0, scopedLaneIndex) : redacted;
|
|
80
|
-
return LOW_CARDINALITY_VALUE_RE.test(lane) ? lane : fallback;
|
|
81
|
-
}
|
|
82
|
-
function numericValue(value) {
|
|
83
|
-
return typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : void 0;
|
|
84
|
-
}
|
|
85
68
|
function seconds(ms) {
|
|
86
|
-
const value =
|
|
69
|
+
const value = asNonNegativeFiniteNumber(ms);
|
|
87
70
|
return value === void 0 ? void 0 : value / 1e3;
|
|
88
71
|
}
|
|
89
72
|
function sortedLabels(labels) {
|
|
@@ -246,126 +229,126 @@ function renderPrometheusMetrics(store) {
|
|
|
246
229
|
}
|
|
247
230
|
function runLabels(evt) {
|
|
248
231
|
return {
|
|
249
|
-
...evt.blockedBy ? { blocked_by:
|
|
250
|
-
channel:
|
|
251
|
-
model:
|
|
252
|
-
outcome:
|
|
253
|
-
provider:
|
|
254
|
-
trigger:
|
|
232
|
+
...evt.blockedBy ? { blocked_by: normalizeDiagnosticValue(evt.blockedBy) } : {},
|
|
233
|
+
channel: normalizeDiagnosticValue(evt.channel),
|
|
234
|
+
model: normalizeDiagnosticValue(evt.model),
|
|
235
|
+
outcome: normalizeDiagnosticValue(evt.outcome, "unknown"),
|
|
236
|
+
provider: normalizeDiagnosticValue(evt.provider),
|
|
237
|
+
trigger: normalizeDiagnosticValue(evt.trigger)
|
|
255
238
|
};
|
|
256
239
|
}
|
|
257
240
|
function modelCallLabels(evt) {
|
|
258
241
|
return {
|
|
259
|
-
api:
|
|
260
|
-
error_category: evt.type === "model.call.error" ?
|
|
261
|
-
model:
|
|
242
|
+
api: normalizeDiagnosticValue(evt.api),
|
|
243
|
+
error_category: evt.type === "model.call.error" ? normalizeDiagnosticValue(evt.errorCategory, "other") : "none",
|
|
244
|
+
model: normalizeDiagnosticValue(evt.model),
|
|
262
245
|
observation_unit: evt.observationUnit === "turn" ? "turn" : "request",
|
|
263
246
|
outcome: evt.type === "model.call.error" ? "error" : "completed",
|
|
264
|
-
provider:
|
|
265
|
-
transport:
|
|
247
|
+
provider: normalizeDiagnosticValue(evt.provider),
|
|
248
|
+
transport: normalizeDiagnosticValue(evt.transport)
|
|
266
249
|
};
|
|
267
250
|
}
|
|
268
251
|
function modelFailoverLabels(evt) {
|
|
269
252
|
return {
|
|
270
|
-
from_model:
|
|
271
|
-
from_provider:
|
|
272
|
-
lane:
|
|
273
|
-
reason:
|
|
253
|
+
from_model: normalizeDiagnosticValue(evt.fromModel),
|
|
254
|
+
from_provider: normalizeDiagnosticValue(evt.fromProvider),
|
|
255
|
+
lane: normalizeDiagnosticLane(evt.lane),
|
|
256
|
+
reason: normalizeDiagnosticValue(evt.reason, "other"),
|
|
274
257
|
suspended: evt.suspended === void 0 ? "unknown" : String(evt.suspended),
|
|
275
|
-
to_model:
|
|
276
|
-
to_provider:
|
|
258
|
+
to_model: normalizeDiagnosticValue(evt.toModel),
|
|
259
|
+
to_provider: normalizeDiagnosticValue(evt.toProvider)
|
|
277
260
|
};
|
|
278
261
|
}
|
|
279
262
|
function toolExecutionLabels(evt) {
|
|
280
263
|
return {
|
|
281
|
-
error_category: evt.type === "tool.execution.error" ?
|
|
264
|
+
error_category: evt.type === "tool.execution.error" ? normalizeDiagnosticValue(evt.errorCategory, "other") : "none",
|
|
282
265
|
outcome: evt.type === "tool.execution.error" ? "error" : "completed",
|
|
283
|
-
params_kind:
|
|
284
|
-
tool:
|
|
285
|
-
tool_owner:
|
|
286
|
-
tool_source:
|
|
266
|
+
params_kind: normalizeDiagnosticValue(evt.paramsSummary?.kind),
|
|
267
|
+
tool: normalizeDiagnosticValue(evt.toolName, "tool"),
|
|
268
|
+
tool_owner: normalizeDiagnosticValue(evt.toolOwner, "none"),
|
|
269
|
+
tool_source: normalizeDiagnosticValue(evt.toolSource, "core")
|
|
287
270
|
};
|
|
288
271
|
}
|
|
289
272
|
function toolExecutionBlockedLabels(evt) {
|
|
290
273
|
return {
|
|
291
|
-
denied_reason:
|
|
292
|
-
params_kind:
|
|
293
|
-
tool:
|
|
294
|
-
tool_owner:
|
|
295
|
-
tool_source:
|
|
274
|
+
denied_reason: normalizeDiagnosticValue(evt.deniedReason, "other"),
|
|
275
|
+
params_kind: normalizeDiagnosticValue(evt.paramsSummary?.kind),
|
|
276
|
+
tool: normalizeDiagnosticValue(evt.toolName, "tool"),
|
|
277
|
+
tool_owner: normalizeDiagnosticValue(evt.toolOwner, "none"),
|
|
278
|
+
tool_source: normalizeDiagnosticValue(evt.toolSource, "core")
|
|
296
279
|
};
|
|
297
280
|
}
|
|
298
281
|
function skillLabels(evt) {
|
|
299
282
|
return {
|
|
300
|
-
activation:
|
|
301
|
-
agent:
|
|
302
|
-
skill:
|
|
303
|
-
source:
|
|
283
|
+
activation: normalizeDiagnosticValue(evt.activation, "unknown"),
|
|
284
|
+
agent: normalizeDiagnosticValue(evt.agentId),
|
|
285
|
+
skill: normalizeDiagnosticValue(evt.skillName, "skill"),
|
|
286
|
+
source: normalizeDiagnosticValue(evt.skillSource)
|
|
304
287
|
};
|
|
305
288
|
}
|
|
306
289
|
function harnessLabels(evt) {
|
|
307
290
|
return {
|
|
308
|
-
channel:
|
|
309
|
-
error_category: evt.type === "harness.run.error" ?
|
|
310
|
-
harness:
|
|
311
|
-
model:
|
|
312
|
-
outcome: evt.type === "harness.run.error" ? "error" :
|
|
313
|
-
phase: evt.type === "harness.run.error" ?
|
|
314
|
-
plugin:
|
|
315
|
-
provider:
|
|
291
|
+
channel: normalizeDiagnosticValue(evt.channel),
|
|
292
|
+
error_category: evt.type === "harness.run.error" ? normalizeDiagnosticValue(evt.errorCategory, "other") : "none",
|
|
293
|
+
harness: normalizeDiagnosticValue(evt.harnessId),
|
|
294
|
+
model: normalizeDiagnosticValue(evt.model),
|
|
295
|
+
outcome: evt.type === "harness.run.error" ? "error" : normalizeDiagnosticValue(evt.outcome),
|
|
296
|
+
phase: evt.type === "harness.run.error" ? normalizeDiagnosticValue(evt.phase) : "none",
|
|
297
|
+
plugin: normalizeDiagnosticValue(evt.pluginId),
|
|
298
|
+
provider: normalizeDiagnosticValue(evt.provider)
|
|
316
299
|
};
|
|
317
300
|
}
|
|
318
301
|
function webhookLabels(evt) {
|
|
319
302
|
return {
|
|
320
|
-
channel:
|
|
321
|
-
webhook:
|
|
303
|
+
channel: normalizeDiagnosticValue(evt.channel),
|
|
304
|
+
webhook: normalizeDiagnosticValue(evt.updateType)
|
|
322
305
|
};
|
|
323
306
|
}
|
|
324
307
|
function sessionStuckLabels(evt) {
|
|
325
308
|
return {
|
|
326
|
-
reason:
|
|
309
|
+
reason: normalizeDiagnosticValue(evt.reason, "none"),
|
|
327
310
|
state: evt.state
|
|
328
311
|
};
|
|
329
312
|
}
|
|
330
313
|
function sessionRecoveryLabels(evt) {
|
|
331
314
|
return {
|
|
332
|
-
action: evt.type === "session.recovery.completed" ?
|
|
333
|
-
active_work_kind:
|
|
315
|
+
action: evt.type === "session.recovery.completed" ? normalizeDiagnosticValue(evt.action, "unknown") : evt.allowActiveAbort ? "abort" : "recover",
|
|
316
|
+
active_work_kind: normalizeDiagnosticValue(evt.activeWorkKind, "none"),
|
|
334
317
|
state: evt.state,
|
|
335
318
|
status: evt.type === "session.recovery.completed" ? evt.status : "requested"
|
|
336
319
|
};
|
|
337
320
|
}
|
|
338
321
|
function livenessLabels(evt) {
|
|
339
|
-
return { reason:
|
|
322
|
+
return { reason: normalizeDiagnosticValue(evt.reasons.join(":"), "unknown") };
|
|
340
323
|
}
|
|
341
324
|
function payloadLargeLabels(evt) {
|
|
342
325
|
return {
|
|
343
326
|
action: evt.action,
|
|
344
|
-
channel:
|
|
345
|
-
plugin:
|
|
346
|
-
reason:
|
|
347
|
-
surface:
|
|
327
|
+
channel: normalizeDiagnosticValue(evt.channel, "none"),
|
|
328
|
+
plugin: normalizeDiagnosticValue(evt.pluginId, "none"),
|
|
329
|
+
reason: normalizeDiagnosticValue(evt.reason, "none"),
|
|
330
|
+
surface: normalizeDiagnosticValue(evt.surface, "unknown")
|
|
348
331
|
};
|
|
349
332
|
}
|
|
350
333
|
function talkLabels(evt) {
|
|
351
334
|
return {
|
|
352
|
-
brain:
|
|
353
|
-
event_type:
|
|
354
|
-
mode:
|
|
355
|
-
provider:
|
|
356
|
-
transport:
|
|
335
|
+
brain: normalizeDiagnosticValue(evt.brain),
|
|
336
|
+
event_type: normalizeDiagnosticValue(evt.talkEventType),
|
|
337
|
+
mode: normalizeDiagnosticValue(evt.mode),
|
|
338
|
+
provider: normalizeDiagnosticValue(evt.provider),
|
|
339
|
+
transport: normalizeDiagnosticValue(evt.transport)
|
|
357
340
|
};
|
|
358
341
|
}
|
|
359
342
|
function recordModelUsage(store, evt) {
|
|
360
343
|
const labels = {
|
|
361
|
-
agent:
|
|
362
|
-
channel:
|
|
363
|
-
model:
|
|
364
|
-
provider:
|
|
344
|
+
agent: normalizeDiagnosticValue(evt.agentId),
|
|
345
|
+
channel: normalizeDiagnosticValue(evt.channel),
|
|
346
|
+
model: normalizeDiagnosticValue(evt.model),
|
|
347
|
+
provider: normalizeDiagnosticValue(evt.provider)
|
|
365
348
|
};
|
|
366
349
|
const usage = evt.usage;
|
|
367
350
|
const recordTokens = (tokenType, value) => {
|
|
368
|
-
const amount =
|
|
351
|
+
const amount = asNonNegativeFiniteNumber(value);
|
|
369
352
|
if (amount === void 0 || amount === 0) return;
|
|
370
353
|
store.counter("openclaw_model_tokens_total", "Model tokens reported by diagnostic usage events.", {
|
|
371
354
|
...labels,
|
|
@@ -383,7 +366,7 @@ function recordModelUsage(store, evt) {
|
|
|
383
366
|
recordTokens("cache_write", usage.cacheWrite);
|
|
384
367
|
recordTokens("prompt", usage.promptTokens);
|
|
385
368
|
recordTokens("total", usage.total);
|
|
386
|
-
store.counter("openclaw_model_cost_usd_total", "Estimated model cost in USD reported by diagnostic usage events.", labels,
|
|
369
|
+
store.counter("openclaw_model_cost_usd_total", "Estimated model cost in USD reported by diagnostic usage events.", labels, asNonNegativeFiniteNumber(evt.costUsd) ?? 0);
|
|
387
370
|
store.histogram("openclaw_model_usage_duration_seconds", "Model usage event duration in seconds.", labels, seconds(evt.durationMs));
|
|
388
371
|
}
|
|
389
372
|
function recordDiagnosticEvent(store, evt, metadata) {
|
|
@@ -422,14 +405,14 @@ function recordDiagnosticEvent(store, evt, metadata) {
|
|
|
422
405
|
return;
|
|
423
406
|
case "message.processed":
|
|
424
407
|
store.counter("openclaw_message_processed_total", "Inbound messages processed by outcome.", {
|
|
425
|
-
channel:
|
|
408
|
+
channel: normalizeDiagnosticValue(evt.channel),
|
|
426
409
|
outcome: evt.outcome,
|
|
427
|
-
reason:
|
|
410
|
+
reason: normalizeDiagnosticValue(evt.reason, "none")
|
|
428
411
|
});
|
|
429
412
|
store.histogram("openclaw_message_processed_duration_seconds", "Inbound message processing duration in seconds.", {
|
|
430
|
-
channel:
|
|
413
|
+
channel: normalizeDiagnosticValue(evt.channel),
|
|
431
414
|
outcome: evt.outcome,
|
|
432
|
-
reason:
|
|
415
|
+
reason: normalizeDiagnosticValue(evt.reason, "none")
|
|
433
416
|
}, seconds(evt.durationMs));
|
|
434
417
|
return;
|
|
435
418
|
case "webhook.received":
|
|
@@ -443,55 +426,55 @@ function recordDiagnosticEvent(store, evt, metadata) {
|
|
|
443
426
|
return;
|
|
444
427
|
case "message.delivery.started":
|
|
445
428
|
store.counter("openclaw_message_delivery_started_total", "Outbound message delivery attempts started.", {
|
|
446
|
-
channel:
|
|
447
|
-
delivery_kind:
|
|
429
|
+
channel: normalizeDiagnosticValue(evt.channel),
|
|
430
|
+
delivery_kind: normalizeDiagnosticValue(evt.deliveryKind, "other")
|
|
448
431
|
});
|
|
449
432
|
return;
|
|
450
433
|
case "message.received":
|
|
451
434
|
store.counter("openclaw_message_received_total", "Inbound messages received by channel.", {
|
|
452
|
-
channel:
|
|
453
|
-
source:
|
|
435
|
+
channel: normalizeDiagnosticValue(evt.channel),
|
|
436
|
+
source: normalizeDiagnosticValue(evt.source)
|
|
454
437
|
});
|
|
455
438
|
return;
|
|
456
439
|
case "message.dispatch.started":
|
|
457
440
|
store.counter("openclaw_message_dispatch_started_total", "Inbound message dispatch attempts started by channel.", {
|
|
458
|
-
channel:
|
|
459
|
-
source:
|
|
441
|
+
channel: normalizeDiagnosticValue(evt.channel),
|
|
442
|
+
source: normalizeDiagnosticValue(evt.source)
|
|
460
443
|
});
|
|
461
444
|
return;
|
|
462
445
|
case "message.dispatch.completed":
|
|
463
446
|
store.counter("openclaw_message_dispatch_completed_total", "Inbound message dispatch attempts completed by outcome.", {
|
|
464
|
-
channel:
|
|
447
|
+
channel: normalizeDiagnosticValue(evt.channel),
|
|
465
448
|
outcome: evt.outcome,
|
|
466
|
-
reason:
|
|
467
|
-
source:
|
|
449
|
+
reason: normalizeDiagnosticValue(evt.reason, "none"),
|
|
450
|
+
source: normalizeDiagnosticValue(evt.source)
|
|
468
451
|
});
|
|
469
452
|
store.histogram("openclaw_message_dispatch_duration_seconds", "Inbound message dispatch duration in seconds.", {
|
|
470
|
-
channel:
|
|
453
|
+
channel: normalizeDiagnosticValue(evt.channel),
|
|
471
454
|
outcome: evt.outcome,
|
|
472
|
-
reason:
|
|
473
|
-
source:
|
|
455
|
+
reason: normalizeDiagnosticValue(evt.reason, "none"),
|
|
456
|
+
source: normalizeDiagnosticValue(evt.source)
|
|
474
457
|
}, seconds(evt.durationMs));
|
|
475
458
|
return;
|
|
476
459
|
case "message.delivery.completed":
|
|
477
460
|
case "message.delivery.error":
|
|
478
461
|
store.counter("openclaw_message_delivery_total", "Outbound message delivery attempts by outcome.", {
|
|
479
|
-
channel:
|
|
480
|
-
delivery_kind:
|
|
481
|
-
error_category: evt.type === "message.delivery.error" ?
|
|
462
|
+
channel: normalizeDiagnosticValue(evt.channel),
|
|
463
|
+
delivery_kind: normalizeDiagnosticValue(evt.deliveryKind, "other"),
|
|
464
|
+
error_category: evt.type === "message.delivery.error" ? normalizeDiagnosticValue(evt.errorCategory, "other") : "none",
|
|
482
465
|
outcome: evt.type === "message.delivery.error" ? "error" : "completed"
|
|
483
466
|
});
|
|
484
467
|
store.histogram("openclaw_message_delivery_duration_seconds", "Outbound message delivery duration in seconds.", {
|
|
485
|
-
channel:
|
|
486
|
-
delivery_kind:
|
|
487
|
-
error_category: evt.type === "message.delivery.error" ?
|
|
468
|
+
channel: normalizeDiagnosticValue(evt.channel),
|
|
469
|
+
delivery_kind: normalizeDiagnosticValue(evt.deliveryKind, "other"),
|
|
470
|
+
error_category: evt.type === "message.delivery.error" ? normalizeDiagnosticValue(evt.errorCategory, "other") : "none",
|
|
488
471
|
outcome: evt.type === "message.delivery.error" ? "error" : "completed"
|
|
489
472
|
}, seconds(evt.durationMs));
|
|
490
473
|
return;
|
|
491
474
|
case "talk.event":
|
|
492
475
|
store.counter("openclaw_talk_event_total", "Talk events emitted by type.", talkLabels(evt));
|
|
493
476
|
store.histogram("openclaw_talk_event_duration_seconds", "Talk event duration in seconds when reported.", talkLabels(evt), seconds(evt.durationMs));
|
|
494
|
-
store.histogram("openclaw_talk_audio_bytes", "Talk audio frame byte lengths.", talkLabels(evt),
|
|
477
|
+
store.histogram("openclaw_talk_audio_bytes", "Talk audio frame byte lengths.", talkLabels(evt), asNonNegativeFiniteNumber(evt.byteLength), BYTE_BUCKETS);
|
|
495
478
|
return;
|
|
496
479
|
case "session.recovery.requested":
|
|
497
480
|
case "session.recovery.completed":
|
|
@@ -500,15 +483,15 @@ function recordDiagnosticEvent(store, evt, metadata) {
|
|
|
500
483
|
return;
|
|
501
484
|
case "queue.lane.enqueue":
|
|
502
485
|
case "queue.lane.dequeue":
|
|
503
|
-
store.gauge("openclaw_queue_lane_size", "Current diagnostic queue lane size.", { lane:
|
|
504
|
-
if (evt.type === "queue.lane.dequeue") store.histogram("openclaw_queue_lane_wait_seconds", "Queue lane wait time in seconds.", { lane:
|
|
486
|
+
store.gauge("openclaw_queue_lane_size", "Current diagnostic queue lane size.", { lane: normalizeDiagnosticLane(evt.lane) }, asNonNegativeFiniteNumber(evt.queueSize));
|
|
487
|
+
if (evt.type === "queue.lane.dequeue") store.histogram("openclaw_queue_lane_wait_seconds", "Queue lane wait time in seconds.", { lane: normalizeDiagnosticLane(evt.lane) }, seconds(evt.waitMs));
|
|
505
488
|
return;
|
|
506
489
|
case "session.state":
|
|
507
490
|
store.counter("openclaw_session_state_total", "Session state observations.", {
|
|
508
|
-
reason:
|
|
491
|
+
reason: normalizeDiagnosticValue(evt.reason, "none"),
|
|
509
492
|
state: evt.state
|
|
510
493
|
});
|
|
511
|
-
if (evt.queueDepth !== void 0) store.gauge("openclaw_session_queue_depth", "Latest observed session queue depth.", { state: evt.state },
|
|
494
|
+
if (evt.queueDepth !== void 0) store.gauge("openclaw_session_queue_depth", "Latest observed session queue depth.", { state: evt.state }, asNonNegativeFiniteNumber(evt.queueDepth));
|
|
512
495
|
return;
|
|
513
496
|
case "session.stuck":
|
|
514
497
|
store.counter("openclaw_session_stuck_total", "Stale session bookkeeping observations with no active work.", sessionStuckLabels(evt));
|
|
@@ -516,8 +499,8 @@ function recordDiagnosticEvent(store, evt, metadata) {
|
|
|
516
499
|
return;
|
|
517
500
|
case "session.turn.created":
|
|
518
501
|
store.counter("openclaw_session_turn_created_total", "Agent session turns created.", {
|
|
519
|
-
agent:
|
|
520
|
-
channel:
|
|
502
|
+
agent: normalizeDiagnosticValue(evt.agentId),
|
|
503
|
+
channel: normalizeDiagnosticValue(evt.channel),
|
|
521
504
|
trigger: evt.trigger
|
|
522
505
|
});
|
|
523
506
|
return;
|
|
@@ -525,7 +508,7 @@ function recordDiagnosticEvent(store, evt, metadata) {
|
|
|
525
508
|
store.gauge("openclaw_memory_bytes", "Latest process memory usage by memory kind.", { kind: "rss" }, evt.memory.rssBytes);
|
|
526
509
|
store.gauge("openclaw_memory_bytes", "Latest process memory usage by memory kind.", { kind: "heap_total" }, evt.memory.heapTotalBytes);
|
|
527
510
|
store.gauge("openclaw_memory_bytes", "Latest process memory usage by memory kind.", { kind: "heap_used" }, evt.memory.heapUsedBytes);
|
|
528
|
-
store.histogram("openclaw_memory_rss_bytes", "RSS memory sample distribution in bytes.", {},
|
|
511
|
+
store.histogram("openclaw_memory_rss_bytes", "RSS memory sample distribution in bytes.", {}, asNonNegativeFiniteNumber(evt.memory.rssBytes), BYTE_BUCKETS);
|
|
529
512
|
return;
|
|
530
513
|
case "diagnostic.memory.pressure":
|
|
531
514
|
store.counter("openclaw_memory_pressure_total", "Memory pressure events by level and reason.", {
|
|
@@ -535,33 +518,33 @@ function recordDiagnosticEvent(store, evt, metadata) {
|
|
|
535
518
|
return;
|
|
536
519
|
case "diagnostic.liveness.warning":
|
|
537
520
|
store.counter("openclaw_liveness_warning_total", "Diagnostic liveness warning events.", livenessLabels(evt));
|
|
538
|
-
store.gauge("openclaw_liveness_sessions", "Latest session counts reported with diagnostic liveness warnings.", { state: "active" },
|
|
539
|
-
store.gauge("openclaw_liveness_sessions", "Latest session counts reported with diagnostic liveness warnings.", { state: "waiting" },
|
|
540
|
-
store.gauge("openclaw_liveness_sessions", "Latest session counts reported with diagnostic liveness warnings.", { state: "queued" },
|
|
521
|
+
store.gauge("openclaw_liveness_sessions", "Latest session counts reported with diagnostic liveness warnings.", { state: "active" }, asNonNegativeFiniteNumber(evt.active));
|
|
522
|
+
store.gauge("openclaw_liveness_sessions", "Latest session counts reported with diagnostic liveness warnings.", { state: "waiting" }, asNonNegativeFiniteNumber(evt.waiting));
|
|
523
|
+
store.gauge("openclaw_liveness_sessions", "Latest session counts reported with diagnostic liveness warnings.", { state: "queued" }, asNonNegativeFiniteNumber(evt.queued));
|
|
541
524
|
store.histogram("openclaw_liveness_event_loop_delay_p99_seconds", "P99 event-loop delay reported by diagnostic liveness warnings in seconds.", livenessLabels(evt), seconds(evt.eventLoopDelayP99Ms));
|
|
542
525
|
store.histogram("openclaw_liveness_event_loop_delay_max_seconds", "Maximum event-loop delay reported by diagnostic liveness warnings in seconds.", livenessLabels(evt), seconds(evt.eventLoopDelayMaxMs));
|
|
543
|
-
store.histogram("openclaw_liveness_event_loop_utilization_ratio", "Event-loop utilization reported by diagnostic liveness warnings.", livenessLabels(evt),
|
|
544
|
-
store.histogram("openclaw_liveness_cpu_core_ratio", "CPU core ratio reported by diagnostic liveness warnings.", livenessLabels(evt),
|
|
526
|
+
store.histogram("openclaw_liveness_event_loop_utilization_ratio", "Event-loop utilization reported by diagnostic liveness warnings.", livenessLabels(evt), asNonNegativeFiniteNumber(evt.eventLoopUtilization), RATIO_BUCKETS);
|
|
527
|
+
store.histogram("openclaw_liveness_cpu_core_ratio", "CPU core ratio reported by diagnostic liveness warnings.", livenessLabels(evt), asNonNegativeFiniteNumber(evt.cpuCoreRatio), RATIO_BUCKETS);
|
|
545
528
|
return;
|
|
546
529
|
case "diagnostic.async_queue.dropped":
|
|
547
|
-
store.counter("openclaw_diagnostic_async_queue_dropped_total", "Async diagnostic queue drops by dropped event class.", { drop_class: "total" },
|
|
548
|
-
if (evt.droppedTrustedEvents !== void 0) store.counter("openclaw_diagnostic_async_queue_dropped_total", "Async diagnostic queue drops by dropped event class.", { drop_class: "trusted" },
|
|
549
|
-
if (evt.droppedUntrustedEvents !== void 0) store.counter("openclaw_diagnostic_async_queue_dropped_total", "Async diagnostic queue drops by dropped event class.", { drop_class: "untrusted" },
|
|
550
|
-
if (evt.droppedPriorityEvents !== void 0) store.counter("openclaw_diagnostic_async_queue_dropped_total", "Async diagnostic queue drops by dropped event class.", { drop_class: "priority" },
|
|
551
|
-
store.gauge("openclaw_diagnostic_async_queue_length", "Latest async diagnostic queue length after a drop summary.", {},
|
|
530
|
+
store.counter("openclaw_diagnostic_async_queue_dropped_total", "Async diagnostic queue drops by dropped event class.", { drop_class: "total" }, asNonNegativeFiniteNumber(evt.droppedEvents));
|
|
531
|
+
if (evt.droppedTrustedEvents !== void 0) store.counter("openclaw_diagnostic_async_queue_dropped_total", "Async diagnostic queue drops by dropped event class.", { drop_class: "trusted" }, asNonNegativeFiniteNumber(evt.droppedTrustedEvents));
|
|
532
|
+
if (evt.droppedUntrustedEvents !== void 0) store.counter("openclaw_diagnostic_async_queue_dropped_total", "Async diagnostic queue drops by dropped event class.", { drop_class: "untrusted" }, asNonNegativeFiniteNumber(evt.droppedUntrustedEvents));
|
|
533
|
+
if (evt.droppedPriorityEvents !== void 0) store.counter("openclaw_diagnostic_async_queue_dropped_total", "Async diagnostic queue drops by dropped event class.", { drop_class: "priority" }, asNonNegativeFiniteNumber(evt.droppedPriorityEvents));
|
|
534
|
+
store.gauge("openclaw_diagnostic_async_queue_length", "Latest async diagnostic queue length after a drop summary.", {}, asNonNegativeFiniteNumber(evt.queueLength));
|
|
552
535
|
break;
|
|
553
536
|
case "diagnostic.heartbeat": break;
|
|
554
537
|
case "telemetry.exporter":
|
|
555
538
|
store.counter("openclaw_telemetry_exporter_total", "Telemetry exporter lifecycle events.", {
|
|
556
|
-
exporter:
|
|
557
|
-
reason:
|
|
539
|
+
exporter: normalizeDiagnosticValue(evt.exporter),
|
|
540
|
+
reason: normalizeDiagnosticValue(evt.reason, "none"),
|
|
558
541
|
signal: evt.signal,
|
|
559
542
|
status: evt.status
|
|
560
543
|
});
|
|
561
544
|
return;
|
|
562
545
|
case "payload.large":
|
|
563
546
|
store.counter("openclaw_payload_large_total", "Oversized payload diagnostics by surface and action.", payloadLargeLabels(evt));
|
|
564
|
-
store.histogram("openclaw_payload_large_bytes", "Oversized payload byte sizes by surface and action.", payloadLargeLabels(evt),
|
|
547
|
+
store.histogram("openclaw_payload_large_bytes", "Oversized payload byte sizes by surface and action.", payloadLargeLabels(evt), asNonNegativeFiniteNumber(evt.bytes), BYTE_BUCKETS);
|
|
565
548
|
default:
|
|
566
549
|
}
|
|
567
550
|
}
|
|
@@ -577,6 +560,7 @@ function createMetricsHandler(store) {
|
|
|
577
560
|
res.statusCode = 200;
|
|
578
561
|
res.setHeader("Cache-Control", "no-store");
|
|
579
562
|
res.setHeader("Content-Type", "text/plain; version=0.0.4; charset=utf-8");
|
|
563
|
+
res.setHeader("Content-Length", String(Buffer.byteLength(body)));
|
|
580
564
|
if (req.method === "HEAD") {
|
|
581
565
|
res.end();
|
|
582
566
|
return true;
|
|
@@ -588,6 +572,12 @@ function createMetricsHandler(store) {
|
|
|
588
572
|
function createDiagnosticsPrometheusExporter() {
|
|
589
573
|
const store = createPrometheusMetricStore();
|
|
590
574
|
let unsubscribe;
|
|
575
|
+
let internalDiagnostics;
|
|
576
|
+
const reportExporterHealth = (update) => {
|
|
577
|
+
try {
|
|
578
|
+
internalDiagnostics?.reportExporterHealth?.(update);
|
|
579
|
+
} catch {}
|
|
580
|
+
};
|
|
591
581
|
return {
|
|
592
582
|
handler: createMetricsHandler(store),
|
|
593
583
|
render: () => renderPrometheusMetrics(store),
|
|
@@ -606,7 +596,14 @@ function createDiagnosticsPrometheusExporter() {
|
|
|
606
596
|
ctx.logger.error(`diagnostics-prometheus: event handler failed (${event.type}): ${safeErrorMessage(err)}`);
|
|
607
597
|
}
|
|
608
598
|
});
|
|
609
|
-
ctx.internalDiagnostics
|
|
599
|
+
internalDiagnostics = ctx.internalDiagnostics;
|
|
600
|
+
reportExporterHealth({
|
|
601
|
+
signal: "metrics",
|
|
602
|
+
transport: "prometheus-scrape",
|
|
603
|
+
status: "started",
|
|
604
|
+
reason: "configured"
|
|
605
|
+
});
|
|
606
|
+
internalDiagnostics.emit({
|
|
610
607
|
type: "telemetry.exporter",
|
|
611
608
|
exporter: "diagnostics-prometheus",
|
|
612
609
|
signal: "metrics",
|
|
@@ -617,6 +614,18 @@ function createDiagnosticsPrometheusExporter() {
|
|
|
617
614
|
stop() {
|
|
618
615
|
unsubscribe?.();
|
|
619
616
|
unsubscribe = void 0;
|
|
617
|
+
reportExporterHealth({
|
|
618
|
+
signal: "metrics",
|
|
619
|
+
transport: "prometheus-scrape",
|
|
620
|
+
status: "dropped"
|
|
621
|
+
});
|
|
622
|
+
internalDiagnostics?.emit({
|
|
623
|
+
type: "telemetry.exporter",
|
|
624
|
+
exporter: "diagnostics-prometheus",
|
|
625
|
+
signal: "metrics",
|
|
626
|
+
status: "dropped"
|
|
627
|
+
});
|
|
628
|
+
internalDiagnostics = void 0;
|
|
620
629
|
store.reset();
|
|
621
630
|
}
|
|
622
631
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openclaw/diagnostics-prometheus",
|
|
3
|
-
"version": "2026.
|
|
3
|
+
"version": "2026.8.1-beta.3",
|
|
4
4
|
"description": "OpenClaw diagnostics Prometheus exporter for runtime metrics.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -18,10 +18,10 @@
|
|
|
18
18
|
"minHostVersion": ">=2026.4.25"
|
|
19
19
|
},
|
|
20
20
|
"compat": {
|
|
21
|
-
"pluginApi": ">=2026.
|
|
21
|
+
"pluginApi": ">=2026.8.1-beta.3"
|
|
22
22
|
},
|
|
23
23
|
"build": {
|
|
24
|
-
"openclawVersion": "2026.
|
|
24
|
+
"openclawVersion": "2026.8.1-beta.3"
|
|
25
25
|
},
|
|
26
26
|
"release": {
|
|
27
27
|
"publishToClawHub": true,
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
"README.md"
|
|
38
38
|
],
|
|
39
39
|
"peerDependencies": {
|
|
40
|
-
"openclaw": ">=2026.
|
|
40
|
+
"openclaw": ">=2026.8.1-beta.3"
|
|
41
41
|
},
|
|
42
42
|
"peerDependenciesMeta": {
|
|
43
43
|
"openclaw": {
|