@openclaw/diagnostics-prometheus 2026.9.1 → 2026.9.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 +125 -73
- package/package.json +4 -4
package/dist/index.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
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";
|
|
3
|
+
import { isDiagnosticsEnabled, normalizeDiagnosticLane, normalizeDiagnosticValue } from "openclaw/plugin-sdk/diagnostic-runtime";
|
|
4
4
|
import { asNonNegativeFiniteNumber } from "openclaw/plugin-sdk/number-runtime";
|
|
5
|
+
import { getPluginRuntimeGatewayRequestScope } from "openclaw/plugin-sdk/plugin-runtime";
|
|
5
6
|
import { truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
|
|
6
7
|
//#region extensions/diagnostics-prometheus/src/service.ts
|
|
7
8
|
const DURATION_BUCKETS_SECONDS = [
|
|
@@ -70,7 +71,9 @@ function seconds(ms) {
|
|
|
70
71
|
return value === void 0 ? void 0 : value / 1e3;
|
|
71
72
|
}
|
|
72
73
|
function sortedLabels(labels) {
|
|
73
|
-
|
|
74
|
+
const entries = Object.entries(labels);
|
|
75
|
+
entries.sort(([left], [right]) => left.localeCompare(right));
|
|
76
|
+
return entries;
|
|
74
77
|
}
|
|
75
78
|
function metricKey(name, labels) {
|
|
76
79
|
return `${name}|${JSON.stringify(sortedLabels(labels))}`;
|
|
@@ -81,10 +84,13 @@ function escapeHelp(value) {
|
|
|
81
84
|
function escapeLabelValue(value) {
|
|
82
85
|
return value.replace(/\\/g, "\\\\").replace(/\n/g, "\\n").replace(/"/g, "\\\"");
|
|
83
86
|
}
|
|
87
|
+
function formatLabelEntry([key, value]) {
|
|
88
|
+
return `${key}="${escapeLabelValue(value)}"`;
|
|
89
|
+
}
|
|
84
90
|
function formatLabels(labels) {
|
|
85
91
|
const entries = sortedLabels(labels);
|
|
86
92
|
if (entries.length === 0) return "";
|
|
87
|
-
return `{${entries.map(
|
|
93
|
+
return `{${entries.map(formatLabelEntry).join(",")}}`;
|
|
88
94
|
}
|
|
89
95
|
function formatPrometheusNumber(value) {
|
|
90
96
|
if (!Number.isFinite(value)) return "0";
|
|
@@ -95,9 +101,8 @@ function createPrometheusMetricStore() {
|
|
|
95
101
|
const gauges = /* @__PURE__ */ new Map();
|
|
96
102
|
const histograms = /* @__PURE__ */ new Map();
|
|
97
103
|
let droppedSeries = 0;
|
|
98
|
-
const canCreateSeries = (map, key
|
|
104
|
+
const canCreateSeries = (map, key) => {
|
|
99
105
|
if (map.has(key)) return true;
|
|
100
|
-
if (metricName === DROPPED_SERIES_COUNTER_NAME) return true;
|
|
101
106
|
if (counters.size + gauges.size + histograms.size < MAX_PROMETHEUS_SERIES) return true;
|
|
102
107
|
droppedSeries += 1;
|
|
103
108
|
return false;
|
|
@@ -105,7 +110,7 @@ function createPrometheusMetricStore() {
|
|
|
105
110
|
const counter = (name, help, labels, amount = 1) => {
|
|
106
111
|
if (!Number.isFinite(amount) || amount <= 0) return;
|
|
107
112
|
const key = metricKey(name, labels);
|
|
108
|
-
if (!canCreateSeries(counters, key
|
|
113
|
+
if (!canCreateSeries(counters, key)) return;
|
|
109
114
|
const existing = counters.get(key);
|
|
110
115
|
if (existing) {
|
|
111
116
|
existing.value += amount;
|
|
@@ -120,7 +125,7 @@ function createPrometheusMetricStore() {
|
|
|
120
125
|
const gauge = (name, help, labels, value) => {
|
|
121
126
|
if (value === void 0 || !Number.isFinite(value)) return;
|
|
122
127
|
const key = metricKey(name, labels);
|
|
123
|
-
if (!canCreateSeries(gauges, key
|
|
128
|
+
if (!canCreateSeries(gauges, key)) return;
|
|
124
129
|
gauges.set(key, {
|
|
125
130
|
help,
|
|
126
131
|
labels,
|
|
@@ -130,7 +135,7 @@ function createPrometheusMetricStore() {
|
|
|
130
135
|
const histogram = (name, help, labels, value, buckets = DURATION_BUCKETS_SECONDS) => {
|
|
131
136
|
if (value === void 0 || !Number.isFinite(value) || value < 0) return;
|
|
132
137
|
const key = metricKey(name, labels);
|
|
133
|
-
if (!canCreateSeries(histograms, key
|
|
138
|
+
if (!canCreateSeries(histograms, key)) return;
|
|
134
139
|
let sample = histograms.get(key);
|
|
135
140
|
if (!sample) {
|
|
136
141
|
sample = {
|
|
@@ -151,16 +156,16 @@ function createPrometheusMetricStore() {
|
|
|
151
156
|
}
|
|
152
157
|
};
|
|
153
158
|
const snapshot = () => {
|
|
154
|
-
const counterSnapshot =
|
|
155
|
-
if (droppedSeries > 0) counterSnapshot.
|
|
159
|
+
const counterSnapshot = [...counters];
|
|
160
|
+
if (droppedSeries > 0) counterSnapshot.push([metricKey(DROPPED_SERIES_COUNTER_NAME, {}), {
|
|
156
161
|
help: "Prometheus metric series dropped because the exporter series cap was reached.",
|
|
157
162
|
labels: {},
|
|
158
163
|
value: droppedSeries
|
|
159
|
-
});
|
|
164
|
+
}]);
|
|
160
165
|
return {
|
|
161
166
|
counters: counterSnapshot,
|
|
162
|
-
gauges:
|
|
163
|
-
histograms:
|
|
167
|
+
gauges: [...gauges],
|
|
168
|
+
histograms: [...histograms]
|
|
164
169
|
};
|
|
165
170
|
};
|
|
166
171
|
const reset = () => {
|
|
@@ -194,36 +199,35 @@ function renderPrometheusMetrics(store) {
|
|
|
194
199
|
lines.push(`# HELP ${name} ${escapeHelp(help)}`);
|
|
195
200
|
lines.push(`# TYPE ${name} ${type}`);
|
|
196
201
|
};
|
|
197
|
-
const
|
|
198
|
-
|
|
199
|
-
const
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
for (const [key, sample] of gaugeEntries) {
|
|
205
|
-
const name = key.split("|", 1)[0] ?? "";
|
|
206
|
-
emitHeader(name, "gauge", sample.help);
|
|
207
|
-
lines.push(`${name}${formatLabels(sample.labels)} ${formatPrometheusNumber(sample.value)}`);
|
|
202
|
+
for (const [type, entries] of [["counter", snapshot.counters], ["gauge", snapshot.gauges]]) {
|
|
203
|
+
entries.sort(([left], [right]) => left.localeCompare(right));
|
|
204
|
+
for (const [key, sample] of entries) {
|
|
205
|
+
const name = key.split("|", 1)[0] ?? "";
|
|
206
|
+
emitHeader(name, type, sample.help);
|
|
207
|
+
lines.push(`${name}${formatLabels(sample.labels)} ${formatPrometheusNumber(sample.value)}`);
|
|
208
|
+
}
|
|
208
209
|
}
|
|
209
|
-
|
|
210
|
-
for (const [key, sample] of
|
|
210
|
+
snapshot.histograms.sort(([left], [right]) => left.localeCompare(right));
|
|
211
|
+
for (const [key, sample] of snapshot.histograms) {
|
|
211
212
|
const name = key.split("|", 1)[0] ?? "";
|
|
212
213
|
emitHeader(name, "histogram", sample.help);
|
|
214
|
+
const labels = formatLabels(sample.labels);
|
|
215
|
+
const bucketLabels = sortedLabels({
|
|
216
|
+
...sample.labels,
|
|
217
|
+
le: ""
|
|
218
|
+
});
|
|
219
|
+
const boundIndex = bucketLabels.findIndex(([labelKey]) => labelKey === "le");
|
|
220
|
+
const bucketFragments = bucketLabels.map(formatLabelEntry);
|
|
213
221
|
for (let index = 0; index < sample.buckets.length; index += 1) {
|
|
214
222
|
const bucket = sample.buckets[index];
|
|
215
223
|
if (bucket === void 0) continue;
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
le: String(bucket)
|
|
219
|
-
})} ${formatPrometheusNumber(sample.counts[index] ?? 0)}`);
|
|
224
|
+
bucketFragments[boundIndex] = `le="${String(bucket)}"`;
|
|
225
|
+
lines.push(`${name}_bucket{${bucketFragments.join(",")}} ${formatPrometheusNumber(sample.counts[index] ?? 0)}`);
|
|
220
226
|
}
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
}
|
|
225
|
-
lines.push(`${name}_sum${formatLabels(sample.labels)} ${formatPrometheusNumber(sample.sum)}`);
|
|
226
|
-
lines.push(`${name}_count${formatLabels(sample.labels)} ${formatPrometheusNumber(sample.count)}`);
|
|
227
|
+
bucketFragments[boundIndex] = "le=\"+Inf\"";
|
|
228
|
+
lines.push(`${name}_bucket{${bucketFragments.join(",")}} ${formatPrometheusNumber(sample.count)}`);
|
|
229
|
+
lines.push(`${name}_sum${labels} ${formatPrometheusNumber(sample.sum)}`);
|
|
230
|
+
lines.push(`${name}_count${labels} ${formatPrometheusNumber(sample.count)}`);
|
|
227
231
|
}
|
|
228
232
|
lines.push("");
|
|
229
233
|
return lines.join("\n");
|
|
@@ -373,6 +377,30 @@ function recordModelUsage(store, evt) {
|
|
|
373
377
|
function recordDiagnosticEvent(store, evt, metadata) {
|
|
374
378
|
if (!shouldRecordDiagnosticEvent(metadata)) return;
|
|
375
379
|
switch (evt.type) {
|
|
380
|
+
case "diagnostic.gc":
|
|
381
|
+
store.histogram("openclaw_gc_duration_seconds", "Elapsed garbage collection duration in seconds for the hosting JavaScript isolate.", {}, seconds(evt.durationMs));
|
|
382
|
+
return;
|
|
383
|
+
case "gateway.event_loop.sample":
|
|
384
|
+
store.histogram("openclaw_gateway_event_loop_delay_max_seconds", "Maximum event-loop delay per completed Gateway observation window in seconds.", {}, seconds(evt.delayMaxMs));
|
|
385
|
+
store.counter("openclaw_gateway_event_loop_observed_seconds_total", "Elapsed seconds covered by completed Gateway event-loop observation windows.", {}, evt.intervalMs / 1e3);
|
|
386
|
+
return;
|
|
387
|
+
case "gateway.rpc": {
|
|
388
|
+
const labels = { method: evt.method };
|
|
389
|
+
if (evt.phase === "received") {
|
|
390
|
+
store.counter("openclaw_gateway_rpc_requests_total", "Authenticated Gateway WebSocket requests received.", labels);
|
|
391
|
+
return;
|
|
392
|
+
}
|
|
393
|
+
store.counter("openclaw_gateway_rpc_outcomes_total", "Gateway RPC observations by phase and outcome.", {
|
|
394
|
+
phase: evt.phase,
|
|
395
|
+
outcome: evt.outcome
|
|
396
|
+
});
|
|
397
|
+
if (evt.phase === "response" && (evt.outcome === "ok" || evt.outcome === "error")) store.histogram("openclaw_gateway_rpc_first_response_seconds", "Elapsed time until the first Gateway RPC response is sent.", labels, seconds(evt.durationMs));
|
|
398
|
+
else if (evt.phase === "handler") {
|
|
399
|
+
store.histogram("openclaw_gateway_rpc_handler_seconds", "Gateway RPC handler duration until return or throw.", labels, seconds(evt.durationMs));
|
|
400
|
+
store.histogram("openclaw_gateway_rpc_admission_seconds", "Elapsed time from Gateway RPC receipt until handler invocation.", labels, seconds(evt.admissionMs));
|
|
401
|
+
} else if (evt.phase === "dispatch") store.histogram("openclaw_gateway_rpc_queue_wait_seconds", "Gateway operator request start queue wait.", labels, seconds(evt.queueWaitMs));
|
|
402
|
+
return;
|
|
403
|
+
}
|
|
376
404
|
case "model.usage":
|
|
377
405
|
recordModelUsage(store, evt);
|
|
378
406
|
return;
|
|
@@ -404,18 +432,16 @@ function recordDiagnosticEvent(store, evt, metadata) {
|
|
|
404
432
|
store.histogram("openclaw_harness_run_duration_seconds", "Agent harness run duration in seconds.", harnessLabels(evt), seconds(evt.durationMs));
|
|
405
433
|
store.counter("openclaw_harness_run_total", "Agent harness runs completed by outcome.", harnessLabels(evt));
|
|
406
434
|
return;
|
|
407
|
-
case "message.processed":
|
|
408
|
-
|
|
435
|
+
case "message.processed": {
|
|
436
|
+
const labels = {
|
|
409
437
|
channel: normalizeDiagnosticValue(evt.channel),
|
|
410
438
|
outcome: evt.outcome,
|
|
411
439
|
reason: normalizeDiagnosticValue(evt.reason, "none")
|
|
412
|
-
}
|
|
413
|
-
store.
|
|
414
|
-
|
|
415
|
-
outcome: evt.outcome,
|
|
416
|
-
reason: normalizeDiagnosticValue(evt.reason, "none")
|
|
417
|
-
}, seconds(evt.durationMs));
|
|
440
|
+
};
|
|
441
|
+
store.counter("openclaw_message_processed_total", "Inbound messages processed by outcome.", labels);
|
|
442
|
+
store.histogram("openclaw_message_processed_duration_seconds", "Inbound message processing duration in seconds.", labels, seconds(evt.durationMs));
|
|
418
443
|
return;
|
|
444
|
+
}
|
|
419
445
|
case "webhook.received":
|
|
420
446
|
store.counter("openclaw_webhook_received_total", "Webhook requests received by channel and update type.", webhookLabels(evt));
|
|
421
447
|
return;
|
|
@@ -443,35 +469,29 @@ function recordDiagnosticEvent(store, evt, metadata) {
|
|
|
443
469
|
source: normalizeDiagnosticValue(evt.source)
|
|
444
470
|
});
|
|
445
471
|
return;
|
|
446
|
-
case "message.dispatch.completed":
|
|
447
|
-
|
|
448
|
-
channel: normalizeDiagnosticValue(evt.channel),
|
|
449
|
-
outcome: evt.outcome,
|
|
450
|
-
reason: normalizeDiagnosticValue(evt.reason, "none"),
|
|
451
|
-
source: normalizeDiagnosticValue(evt.source)
|
|
452
|
-
});
|
|
453
|
-
store.histogram("openclaw_message_dispatch_duration_seconds", "Inbound message dispatch duration in seconds.", {
|
|
472
|
+
case "message.dispatch.completed": {
|
|
473
|
+
const labels = {
|
|
454
474
|
channel: normalizeDiagnosticValue(evt.channel),
|
|
455
475
|
outcome: evt.outcome,
|
|
456
476
|
reason: normalizeDiagnosticValue(evt.reason, "none"),
|
|
457
477
|
source: normalizeDiagnosticValue(evt.source)
|
|
458
|
-
}
|
|
478
|
+
};
|
|
479
|
+
store.counter("openclaw_message_dispatch_completed_total", "Inbound message dispatch attempts completed by outcome.", labels);
|
|
480
|
+
store.histogram("openclaw_message_dispatch_duration_seconds", "Inbound message dispatch duration in seconds.", labels, seconds(evt.durationMs));
|
|
459
481
|
return;
|
|
482
|
+
}
|
|
460
483
|
case "message.delivery.completed":
|
|
461
|
-
case "message.delivery.error":
|
|
462
|
-
|
|
463
|
-
channel: normalizeDiagnosticValue(evt.channel),
|
|
464
|
-
delivery_kind: normalizeDiagnosticValue(evt.deliveryKind, "other"),
|
|
465
|
-
error_category: evt.type === "message.delivery.error" ? normalizeDiagnosticValue(evt.errorCategory, "other") : "none",
|
|
466
|
-
outcome: evt.type === "message.delivery.error" ? "error" : "completed"
|
|
467
|
-
});
|
|
468
|
-
store.histogram("openclaw_message_delivery_duration_seconds", "Outbound message delivery duration in seconds.", {
|
|
484
|
+
case "message.delivery.error": {
|
|
485
|
+
const labels = {
|
|
469
486
|
channel: normalizeDiagnosticValue(evt.channel),
|
|
470
487
|
delivery_kind: normalizeDiagnosticValue(evt.deliveryKind, "other"),
|
|
471
488
|
error_category: evt.type === "message.delivery.error" ? normalizeDiagnosticValue(evt.errorCategory, "other") : "none",
|
|
472
489
|
outcome: evt.type === "message.delivery.error" ? "error" : "completed"
|
|
473
|
-
}
|
|
490
|
+
};
|
|
491
|
+
store.counter("openclaw_message_delivery_total", "Outbound message delivery attempts by outcome.", labels);
|
|
492
|
+
store.histogram("openclaw_message_delivery_duration_seconds", "Outbound message delivery duration in seconds.", labels, seconds(evt.durationMs));
|
|
474
493
|
return;
|
|
494
|
+
}
|
|
475
495
|
case "talk.event":
|
|
476
496
|
store.counter("openclaw_talk_event_total", "Talk events emitted by type.", talkLabels(evt));
|
|
477
497
|
store.histogram("openclaw_talk_event_duration_seconds", "Talk event duration in seconds when reported.", talkLabels(evt), seconds(evt.durationMs));
|
|
@@ -506,9 +526,11 @@ function recordDiagnosticEvent(store, evt, metadata) {
|
|
|
506
526
|
});
|
|
507
527
|
return;
|
|
508
528
|
case "diagnostic.memory.sample":
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
529
|
+
for (const [kind, field] of [
|
|
530
|
+
["rss", "rssBytes"],
|
|
531
|
+
["heap_total", "heapTotalBytes"],
|
|
532
|
+
["heap_used", "heapUsedBytes"]
|
|
533
|
+
]) store.gauge("openclaw_memory_bytes", "Latest process memory usage by memory kind.", { kind }, evt.memory[field]);
|
|
512
534
|
store.histogram("openclaw_memory_rss_bytes", "RSS memory sample distribution in bytes.", {}, asNonNegativeFiniteNumber(evt.memory.rssBytes), BYTE_BUCKETS);
|
|
513
535
|
return;
|
|
514
536
|
case "diagnostic.memory.pressure":
|
|
@@ -519,19 +541,26 @@ function recordDiagnosticEvent(store, evt, metadata) {
|
|
|
519
541
|
return;
|
|
520
542
|
case "diagnostic.liveness.warning":
|
|
521
543
|
store.counter("openclaw_liveness_warning_total", "Diagnostic liveness warning events.", livenessLabels(evt));
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
544
|
+
for (const state of [
|
|
545
|
+
"active",
|
|
546
|
+
"waiting",
|
|
547
|
+
"queued"
|
|
548
|
+
]) store.gauge("openclaw_liveness_sessions", "Latest session counts reported with diagnostic liveness warnings.", { state }, asNonNegativeFiniteNumber(evt[state]));
|
|
525
549
|
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));
|
|
526
550
|
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));
|
|
527
551
|
store.histogram("openclaw_liveness_event_loop_utilization_ratio", "Event-loop utilization reported by diagnostic liveness warnings.", livenessLabels(evt), asNonNegativeFiniteNumber(evt.eventLoopUtilization), RATIO_BUCKETS);
|
|
528
|
-
store.histogram("openclaw_liveness_cpu_core_ratio", "CPU core
|
|
552
|
+
store.histogram("openclaw_liveness_cpu_core_ratio", "Whole-process CPU usage in core equivalents, including worker and native threads; can exceed 1.", livenessLabels(evt), asNonNegativeFiniteNumber(evt.cpuCoreRatio), RATIO_BUCKETS);
|
|
529
553
|
return;
|
|
530
554
|
case "diagnostic.async_queue.dropped":
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
555
|
+
for (const [dropClass, field] of [
|
|
556
|
+
["total", "droppedEvents"],
|
|
557
|
+
["trusted", "droppedTrustedEvents"],
|
|
558
|
+
["untrusted", "droppedUntrustedEvents"],
|
|
559
|
+
["priority", "droppedPriorityEvents"]
|
|
560
|
+
]) {
|
|
561
|
+
if (field !== "droppedEvents" && evt[field] === void 0) continue;
|
|
562
|
+
store.counter("openclaw_diagnostic_async_queue_dropped_total", "Async diagnostic queue drops by dropped event class.", { drop_class: dropClass }, asNonNegativeFiniteNumber(evt[field]));
|
|
563
|
+
}
|
|
535
564
|
store.gauge("openclaw_diagnostic_async_queue_length", "Latest async diagnostic queue length after a drop summary.", {}, asNonNegativeFiniteNumber(evt.queueLength));
|
|
536
565
|
break;
|
|
537
566
|
case "diagnostic.heartbeat": break;
|
|
@@ -548,8 +577,26 @@ function recordDiagnosticEvent(store, evt, metadata) {
|
|
|
548
577
|
store.histogram("openclaw_payload_large_bytes", "Oversized payload byte sizes by surface and action.", payloadLargeLabels(evt), asNonNegativeFiniteNumber(evt.bytes), BYTE_BUCKETS);
|
|
549
578
|
}
|
|
550
579
|
}
|
|
580
|
+
const METRICS_READ_SCOPE = "operator.read";
|
|
581
|
+
const METRICS_READ_IMPLYING_SCOPES = [
|
|
582
|
+
METRICS_READ_SCOPE,
|
|
583
|
+
"operator.write",
|
|
584
|
+
"operator.admin"
|
|
585
|
+
];
|
|
586
|
+
function hasMetricsReadScope() {
|
|
587
|
+
const runtimeScopes = getPluginRuntimeGatewayRequestScope()?.client?.connect?.scopes;
|
|
588
|
+
const scopes = Array.isArray(runtimeScopes) ? runtimeScopes : [];
|
|
589
|
+
return METRICS_READ_IMPLYING_SCOPES.some((scope) => scopes.includes(scope));
|
|
590
|
+
}
|
|
551
591
|
function createMetricsHandler(store) {
|
|
552
592
|
return (req, res) => {
|
|
593
|
+
if (!hasMetricsReadScope()) {
|
|
594
|
+
res.statusCode = 403;
|
|
595
|
+
res.setHeader("Cache-Control", "no-store");
|
|
596
|
+
res.setHeader("Content-Type", "text/plain; charset=utf-8");
|
|
597
|
+
res.end(`missing scope: ${METRICS_READ_SCOPE}`);
|
|
598
|
+
return true;
|
|
599
|
+
}
|
|
553
600
|
if (req.method !== "GET" && req.method !== "HEAD") {
|
|
554
601
|
res.statusCode = 405;
|
|
555
602
|
res.setHeader("Allow", "GET, HEAD");
|
|
@@ -589,6 +636,11 @@ function createDiagnosticsPrometheusExporter() {
|
|
|
589
636
|
ctx.logger.error("diagnostics-prometheus: internal diagnostics capability unavailable");
|
|
590
637
|
return;
|
|
591
638
|
}
|
|
639
|
+
const identity = isDiagnosticsEnabled(ctx.config) ? ctx.internalDiagnostics?.getRuntimeIdentity?.() : void 0;
|
|
640
|
+
if (identity) store.gauge("openclaw_gateway_build_info", "Identity of the hosting process and its loaded build; not a health or exporter epoch.", {
|
|
641
|
+
process_instance_id: identity.processInstanceId,
|
|
642
|
+
...identity.buildId ? { build_id: identity.buildId } : {}
|
|
643
|
+
}, 1);
|
|
592
644
|
unsubscribe = subscribe((event, metadata) => {
|
|
593
645
|
try {
|
|
594
646
|
recordDiagnosticEvent(store, event, metadata);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openclaw/diagnostics-prometheus",
|
|
3
|
-
"version": "2026.9.
|
|
3
|
+
"version": "2026.9.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.9.
|
|
21
|
+
"pluginApi": ">=2026.9.3"
|
|
22
22
|
},
|
|
23
23
|
"build": {
|
|
24
|
-
"openclawVersion": "2026.9.
|
|
24
|
+
"openclawVersion": "2026.9.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.9.
|
|
40
|
+
"openclaw": ">=2026.9.3"
|
|
41
41
|
},
|
|
42
42
|
"peerDependenciesMeta": {
|
|
43
43
|
"openclaw": {
|