@openclaw/diagnostics-prometheus 2026.11.3 → 2026.11.5-beta.2

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 CHANGED
@@ -4,6 +4,7 @@ import { isDiagnosticsEnabled, normalizeDiagnosticLane, normalizeDiagnosticValue
4
4
  import { asNonNegativeFiniteNumber } from "openclaw/plugin-sdk/number-runtime";
5
5
  import { getPluginRuntimeGatewayRequestScope } from "openclaw/plugin-sdk/plugin-runtime";
6
6
  import { truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
7
+ import { expectDefined } from "openclaw/plugin-sdk/expect-runtime";
7
8
  //#region extensions/diagnostics-prometheus/src/prometheus-format.ts
8
9
  function sortedLabels(labels) {
9
10
  const entries = Object.entries(labels);
@@ -33,7 +34,167 @@ function seconds(ms) {
33
34
  return value === void 0 ? void 0 : value / 1e3;
34
35
  }
35
36
  //#endregion
37
+ //#region extensions/diagnostics-prometheus/src/prometheus-family-budget.ts
38
+ const PROMETHEUS_FAMILY_RESERVES = {
39
+ openclaw_chat_history_requests_total: 4,
40
+ openclaw_chat_history_response_bytes_total: 4,
41
+ openclaw_chat_send_phase_seconds: 32,
42
+ openclaw_child_process_spawn_total: 4,
43
+ openclaw_code_mode_worker_heap_bytes: 128,
44
+ openclaw_code_mode_worker_memory_bytes: 128,
45
+ openclaw_control_ui_document_duration_seconds: 4,
46
+ openclaw_cron_list_requests_total: 4,
47
+ openclaw_diagnostic_async_queue_dropped_total: 4,
48
+ openclaw_diagnostic_async_queue_length: 4,
49
+ openclaw_diff_cache_misses_total: 4,
50
+ openclaw_diff_stage_seconds: 4,
51
+ openclaw_gateway_active_work: 4,
52
+ openclaw_gateway_build_info: 4,
53
+ openclaw_gateway_event_loop_delay_max_seconds: 4,
54
+ openclaw_gateway_event_loop_observed_seconds_total: 4,
55
+ openclaw_gateway_http_cancelled_total: 4,
56
+ openclaw_gateway_main_thread_stall_seconds: 4,
57
+ openclaw_gateway_main_thread_stalls_dropped_total: 4,
58
+ openclaw_gateway_rpc_admission_seconds: 256,
59
+ openclaw_gateway_rpc_client_requests_total: 512,
60
+ openclaw_gateway_rpc_first_response_seconds: 256,
61
+ openclaw_gateway_rpc_handler_heap_delta_bytes: 256,
62
+ openclaw_gateway_rpc_handler_heap_delta_exclusive_total: 256,
63
+ openclaw_gateway_rpc_handler_seconds: 256,
64
+ openclaw_gateway_rpc_outcomes_total: 256,
65
+ openclaw_gateway_rpc_queue_wait_seconds: 256,
66
+ openclaw_gateway_rpc_requests_total: 256,
67
+ openclaw_gateway_rpc_response_bytes: 256,
68
+ openclaw_gateway_rpc_stage_seconds: 256,
69
+ openclaw_gateway_rpc_stage_thread_cpu_seconds: 256,
70
+ openclaw_gateway_ws_event_bytes_total: 256,
71
+ openclaw_gateway_ws_event_frame_bytes: 2048,
72
+ openclaw_gateway_ws_event_wire_bytes_total: 256,
73
+ openclaw_gateway_ws_events_total: 256,
74
+ openclaw_gateway_ws_operator_connections: 4,
75
+ openclaw_gateway_ws_presence_bytes_total: 4,
76
+ openclaw_gateway_ws_presence_frames_total: 4,
77
+ openclaw_gateway_ws_session_event_bytes_total: 256,
78
+ openclaw_gateway_ws_session_event_frames_total: 256,
79
+ openclaw_gc_duration_seconds: 384,
80
+ openclaw_gen_ai_client_token_usage: 128,
81
+ openclaw_harness_run_duration_seconds: 128,
82
+ openclaw_harness_run_total: 128,
83
+ openclaw_heap_old_generation_after_gc_bytes: 128,
84
+ openclaw_heap_old_generation_after_gc_ratio: 128,
85
+ openclaw_heap_size_limit_bytes: 128,
86
+ openclaw_heap_space_bytes: 256,
87
+ openclaw_heap_used_after_major_gc_bytes: 64,
88
+ openclaw_heap_used_after_major_gc_timestamp_seconds: 64,
89
+ openclaw_heap_used_ratio: 128,
90
+ openclaw_liveness_cpu_core_ratio: 4,
91
+ openclaw_liveness_event_loop_delay_max_seconds: 4,
92
+ openclaw_liveness_event_loop_delay_p99_seconds: 4,
93
+ openclaw_liveness_event_loop_utilization_ratio: 4,
94
+ openclaw_liveness_sessions: 4,
95
+ openclaw_liveness_warning_total: 4,
96
+ openclaw_memory_bytes: 128,
97
+ openclaw_memory_pressure_total: 4,
98
+ openclaw_memory_rss_bytes: 4,
99
+ openclaw_message_delivery_duration_seconds: 4,
100
+ openclaw_message_delivery_started_total: 4,
101
+ openclaw_message_delivery_total: 4,
102
+ openclaw_message_dispatch_completed_total: 4,
103
+ openclaw_message_dispatch_duration_seconds: 4,
104
+ openclaw_message_dispatch_started_total: 4,
105
+ openclaw_message_processed_duration_seconds: 4,
106
+ openclaw_message_processed_total: 4,
107
+ openclaw_message_received_total: 4,
108
+ openclaw_model_call_duration_seconds: 128,
109
+ openclaw_model_call_total: 128,
110
+ openclaw_model_cost_usd_total: 128,
111
+ openclaw_model_failover_total: 128,
112
+ openclaw_model_tokens_total: 128,
113
+ openclaw_model_usage_duration_seconds: 128,
114
+ openclaw_models_list_requests_total: 4,
115
+ openclaw_payload_large_bytes: 4,
116
+ openclaw_payload_large_total: 4,
117
+ openclaw_process_anonymous_memory_bytes: 4,
118
+ openclaw_queue_lane_size: 4,
119
+ openclaw_queue_lane_wait_seconds: 4,
120
+ openclaw_run_completed_total: 128,
121
+ openclaw_run_duration_seconds: 128,
122
+ openclaw_secret_egress_requests_total: 4,
123
+ openclaw_session_event_rows_reset_total: 4,
124
+ openclaw_session_event_rows_retained_bytes: 4,
125
+ openclaw_session_event_rows_retained_entries: 4,
126
+ openclaw_session_queue_depth: 4,
127
+ openclaw_session_recovery_age_seconds: 4,
128
+ openclaw_session_recovery_total: 4,
129
+ openclaw_session_row_selection_resets_total: 4,
130
+ openclaw_session_row_partition_cache_total: 32,
131
+ openclaw_session_row_partition_revisions_total: 8,
132
+ openclaw_session_row_selection_rows_total: 64,
133
+ openclaw_session_row_selections_total: 64,
134
+ openclaw_session_state_total: 4,
135
+ openclaw_session_stuck_age_seconds: 4,
136
+ openclaw_session_stuck_total: 4,
137
+ openclaw_session_turn_created_total: 4,
138
+ openclaw_sessions_active: 4,
139
+ openclaw_sessions_list_base_misses_total: 4,
140
+ openclaw_sessions_list_baseline_retirements_total: 4,
141
+ openclaw_sessions_list_requests_total: 4,
142
+ openclaw_sessions_list_response_bytes_total: 4,
143
+ openclaw_sessions_list_retained_bytes: 4,
144
+ openclaw_sessions_list_retained_refs: 4,
145
+ openclaw_sessions_list_retained_views: 4,
146
+ openclaw_skill_used_total: 4,
147
+ openclaw_talk_audio_bytes: 4,
148
+ openclaw_talk_event_duration_seconds: 4,
149
+ openclaw_talk_event_total: 4,
150
+ openclaw_telemetry_exporter_total: 4,
151
+ openclaw_tool_catalog_tools_total: 128,
152
+ openclaw_tool_catalog_total: 128,
153
+ openclaw_tool_execution_blocked_total: 128,
154
+ openclaw_tool_execution_duration_seconds: 128,
155
+ openclaw_tool_execution_total: 128,
156
+ openclaw_webhook_duration_seconds: 4,
157
+ openclaw_webhook_error_total: 4,
158
+ openclaw_webhook_received_total: 4,
159
+ openclaw_worker_array_buffers_bytes: 128,
160
+ openclaw_worker_count: 128,
161
+ openclaw_worker_external_bytes: 128,
162
+ openclaw_worker_gc_collections_total: 128,
163
+ openclaw_worker_gc_duration_seconds_total: 128,
164
+ openclaw_worker_gc_reclaimed_bytes_total: 128,
165
+ openclaw_worker_heap_sampled_count: 128,
166
+ openclaw_worker_heap_space_used_bytes: 640,
167
+ openclaw_worker_heap_used_bytes: 128,
168
+ openclaw_worker_heap_used_high_water_bytes: 128,
169
+ openclaw_worker_message_payload_bytes_total: 512,
170
+ openclaw_worker_message_unmeasured_total: 64,
171
+ openclaw_worker_messages_total: 64,
172
+ openclaw_worker_queue_depth: 512,
173
+ openclaw_worker_queue_wait_seconds: 512,
174
+ openclaw_worker_request_phase_seconds: 1024,
175
+ openclaw_worker_request_seconds: 512,
176
+ openclaw_worker_retired_total: 64,
177
+ openclaw_worker_started_total: 64,
178
+ openclaw_worker_tasks_completed_total: 128,
179
+ openclaw_worktree_preparation_seconds: 32
180
+ };
181
+ const SHARED_PROMETHEUS_SERIES = 16384 - Object.values(PROMETHEUS_FAMILY_RESERVES).reduce((sum, n) => sum + n, 0);
182
+ //#endregion
36
183
  //#region extensions/diagnostics-prometheus/src/prometheus-metric-store.ts
184
+ function createSeries() {
185
+ return {
186
+ samples: [],
187
+ shapes: /* @__PURE__ */ new Map(),
188
+ sorted: true
189
+ };
190
+ }
191
+ function sortedSamples(series) {
192
+ if (!series.sorted) {
193
+ series.samples.sort((left, right) => left.key.localeCompare(right.key));
194
+ series.sorted = true;
195
+ }
196
+ return series.samples;
197
+ }
37
198
  const DURATION_BUCKETS_SECONDS = [
38
199
  .005,
39
200
  .01,
@@ -58,101 +219,270 @@ const AGENT_DURATION_BUCKETS_SECONDS = [
58
219
  1800,
59
220
  3600
60
221
  ];
61
- const MAX_PROMETHEUS_SERIES = 2048;
62
222
  const DROPPED_SERIES_COUNTER_NAME = "openclaw_prometheus_series_dropped_total";
63
- function createPrometheusMetricStore() {
64
- const counters = /* @__PURE__ */ new Map();
65
- const gauges = /* @__PURE__ */ new Map();
66
- const histograms = /* @__PURE__ */ new Map();
223
+ const DROPPED_FAMILY_COUNTER_NAME = "openclaw_prometheus_family_series_dropped_total";
224
+ const warnedFamilies = /* @__PURE__ */ new Set();
225
+ function createPrometheusMetricStore(onSeriesDropped) {
226
+ const counters = createSeries();
227
+ const gauges = createSeries();
228
+ const histograms = createSeries();
67
229
  let droppedSeries = 0;
68
- const canCreateSeries = (map, key) => {
69
- if (map.has(key)) return true;
70
- if (counters.size + gauges.size + histograms.size < MAX_PROMETHEUS_SERIES) return true;
71
- droppedSeries += 1;
72
- return false;
230
+ let sharedSeries = 0;
231
+ const familyCounts = /* @__PURE__ */ new Map();
232
+ const familyDrops = /* @__PURE__ */ new Map();
233
+ const headers = /* @__PURE__ */ new Map();
234
+ let prefixBuffer = Buffer.alloc(0);
235
+ let prefixOffset = 0;
236
+ const encodePrefix = (text) => {
237
+ const bytes = Buffer.byteLength(text);
238
+ if (bytes > 4096) {
239
+ const prefix = Buffer.allocUnsafeSlow(bytes);
240
+ prefix.write(text);
241
+ return prefix;
242
+ }
243
+ if (prefixOffset + bytes > prefixBuffer.length) {
244
+ prefixBuffer = Buffer.allocUnsafeSlow(4096);
245
+ prefixOffset = 0;
246
+ }
247
+ const start = prefixOffset;
248
+ prefixOffset += prefixBuffer.write(text, prefixOffset);
249
+ return prefixBuffer.subarray(start, prefixOffset);
73
250
  };
74
- const counter = (name, help, labels, amount = 1) => {
75
- if (!Number.isFinite(amount) || amount <= 0) return;
76
- const key = metricKey(name, labels);
77
- if (!canCreateSeries(counters, key)) return;
78
- const existing = counters.get(key);
79
- if (existing) {
80
- existing.value += amount;
251
+ let scratch = Buffer.alloc(0);
252
+ const series = (map, name, labels) => {
253
+ let labelCount = 0;
254
+ for (const key in labels) if (Object.hasOwn(labels, key)) labelCount++;
255
+ let shape;
256
+ shapes: for (const candidate of map.shapes.get(name) ?? []) {
257
+ if (candidate.keys.length !== labelCount) continue;
258
+ let node = candidate.root;
259
+ for (const key of candidate.keys) {
260
+ const value = labels[key];
261
+ if (value === void 0 || !Object.prototype.propertyIsEnumerable.call(labels, key)) continue shapes;
262
+ node = node?.values?.get(value);
263
+ }
264
+ shape = candidate;
265
+ if (node?.sample) return node;
266
+ break;
267
+ }
268
+ const count = familyCounts.get(name) ?? 0;
269
+ const reserve = PROMETHEUS_FAMILY_RESERVES[name];
270
+ if (count >= Math.max(128, reserve) || count >= reserve && sharedSeries >= SHARED_PROMETHEUS_SERIES) {
271
+ droppedSeries++;
272
+ familyDrops.set(name, (familyDrops.get(name) ?? 0) + 1);
273
+ if (onSeriesDropped && !warnedFamilies.has(name)) {
274
+ warnedFamilies.add(name);
275
+ onSeriesDropped(name);
276
+ }
81
277
  return;
82
278
  }
83
- counters.set(key, {
84
- help,
85
- labels,
86
- value: amount
87
- });
88
- };
89
- const gauge = (name, help, labels, value) => {
90
- if (value === void 0 || !Number.isFinite(value)) return;
91
- const key = metricKey(name, labels);
92
- if (!canCreateSeries(gauges, key)) return;
93
- gauges.set(key, {
94
- help,
95
- labels,
96
- value
97
- });
279
+ familyCounts.set(name, count + 1);
280
+ if (count >= reserve) sharedSeries++;
281
+ if (!shape) {
282
+ shape = {
283
+ keys: Object.keys(labels).toSorted((left, right) => left.localeCompare(right)),
284
+ root: {}
285
+ };
286
+ const shapes = map.shapes.get(name);
287
+ if (shapes) shapes.push(shape);
288
+ else map.shapes.set(name, [shape]);
289
+ }
290
+ let node = shape.root;
291
+ for (const key of shape.keys) {
292
+ const value = expectDefined(labels[key], "Prometheus label");
293
+ const values = node.values ??= /* @__PURE__ */ new Map();
294
+ let child = values.get(value);
295
+ if (!child) {
296
+ child = {};
297
+ values.set(value, child);
298
+ }
299
+ node = child;
300
+ }
301
+ return node;
98
302
  };
99
- const counterValue = (name, help, labels, value) => {
100
- if (!Number.isFinite(value) || value < 0) return;
101
- const key = metricKey(name, labels);
102
- if (canCreateSeries(counters, key)) counters.set(key, {
103
- help,
104
- labels,
105
- value: Math.max(counters.get(key)?.value ?? 0, value)
106
- });
303
+ const scalar = (map, name, help, labels, value, mode) => {
304
+ if (value === void 0 || !Number.isFinite(value) || mode !== "set" && value < 0 || mode === "add" && value === 0) return;
305
+ const node = series(map, name, labels);
306
+ if (!node) return;
307
+ if (node.sample) {
308
+ const next = mode === "add" ? node.sample.value + value : mode === "max" ? Math.max(node.sample.value, value) : value;
309
+ if (next !== node.sample.value) {
310
+ node.sample.value = next;
311
+ node.sample.renderedDirty = true;
312
+ }
313
+ } else {
314
+ node.sample = {
315
+ help,
316
+ key: metricKey(name, labels),
317
+ name,
318
+ prefix: encodePrefix(`${name}${formatLabels(labels)} `),
319
+ value,
320
+ renderedDirty: true
321
+ };
322
+ map.samples.push(node.sample);
323
+ map.sorted = false;
324
+ }
107
325
  };
326
+ const counter = (name, help, labels, amount = 1) => scalar(counters, name, help, labels, amount, "add");
327
+ const counterValue = (name, help, labels, value) => scalar(counters, name, help, labels, value, "max");
328
+ const gauge = (name, help, labels, value) => scalar(gauges, name, help, labels, value, "set");
108
329
  const clearGauges = (name) => {
109
- for (const key of gauges.keys()) if (key.startsWith(`${name}|`)) gauges.delete(key);
330
+ headers.delete(name);
331
+ const previousLength = gauges.samples.length;
332
+ gauges.shapes.delete(name);
333
+ gauges.samples = gauges.samples.filter((sample) => sample.name !== name);
334
+ const count = familyCounts.get(name) ?? 0;
335
+ const remaining = count - (previousLength - gauges.samples.length);
336
+ const reserve = PROMETHEUS_FAMILY_RESERVES[name];
337
+ sharedSeries -= Math.max(0, count - reserve) - Math.max(0, remaining - reserve);
338
+ familyCounts.set(name, remaining);
110
339
  };
111
- const histogram = (name, help, labels, value, buckets = DURATION_BUCKETS_SECONDS) => {
112
- if (value === void 0 || !Number.isFinite(value)) return;
113
- const key = metricKey(name, labels);
114
- if (!canCreateSeries(histograms, key)) return;
115
- let sample = histograms.get(key);
340
+ const getHistogram = (name, help, labels, buckets = DURATION_BUCKETS_SECONDS) => {
341
+ const node = series(histograms, name, labels);
342
+ if (!node) return;
343
+ let sample = node.sample;
116
344
  if (!sample) {
117
345
  sample = {
118
346
  buckets,
119
- bucketPrefixes: [...buckets, "+Inf"].map((le) => `${name}_bucket${formatLabels({
347
+ bucketPrefixes: [...buckets, "+Inf"].map((le) => encodePrefix(`${name}_bucket${formatLabels({
120
348
  ...labels,
121
349
  le: String(le)
122
- })} `),
350
+ })} `)),
123
351
  counts: buckets.map(() => 0),
124
352
  count: 0,
125
353
  help,
126
- labels: formatLabels(labels),
127
- sum: 0
354
+ key: metricKey(name, labels),
355
+ name,
356
+ sumPrefix: encodePrefix(`${name}_sum${formatLabels(labels)} `),
357
+ countPrefix: encodePrefix(`${name}_count${formatLabels(labels)} `),
358
+ sum: 0,
359
+ renderedDirty: true
128
360
  };
129
- histograms.set(key, sample);
361
+ node.sample = sample;
362
+ histograms.samples.push(sample);
363
+ histograms.sorted = false;
130
364
  }
365
+ return sample;
366
+ };
367
+ const reserveHistogram = (name, help, labels) => {
368
+ getHistogram(name, help, labels);
369
+ };
370
+ const histogram = (name, help, labels, value, buckets = DURATION_BUCKETS_SECONDS) => {
371
+ if (value === void 0 || !Number.isFinite(value)) return;
372
+ const sample = getHistogram(name, help, labels, buckets);
373
+ if (!sample) return;
131
374
  sample.count += 1;
132
375
  sample.sum += value;
376
+ sample.renderedDirty = true;
133
377
  for (let index = 0; index < sample.buckets.length; index += 1) {
134
378
  const bucket = sample.buckets[index];
135
379
  if (bucket !== void 0 && value <= bucket) sample.counts[index] = (sample.counts[index] ?? 0) + 1;
136
380
  }
137
381
  };
138
- const snapshot = () => {
139
- const counterSnapshot = [...counters];
140
- if (droppedSeries > 0) counterSnapshot.push([metricKey(DROPPED_SERIES_COUNTER_NAME, {}), {
141
- help: "Prometheus metric series dropped because the exporter series cap was reached.",
142
- labels: {},
143
- value: droppedSeries
144
- }]);
145
- return {
146
- counters: counterSnapshot,
147
- gauges: [...gauges],
148
- histograms: [...histograms]
382
+ const render = () => {
383
+ let scratchOffset = 0;
384
+ let totalBytes = 0;
385
+ const chunks = [];
386
+ const append = (chunk) => {
387
+ chunks.push(chunk);
388
+ totalBytes += chunk.length;
149
389
  };
390
+ const write = (prefix, value) => {
391
+ const number = formatPrometheusNumber(value);
392
+ const required = scratchOffset + prefix.length + number.length + 1;
393
+ if (required > scratch.length) {
394
+ const grown = Buffer.allocUnsafeSlow(Math.max(required, scratch.length * 2));
395
+ grown.set(scratch.subarray(0, scratchOffset));
396
+ scratch = grown;
397
+ }
398
+ scratch.set(prefix, scratchOffset);
399
+ scratchOffset += prefix.length;
400
+ scratchOffset += scratch.write(number, scratchOffset, "ascii");
401
+ scratch[scratchOffset++] = 10;
402
+ };
403
+ const finish = (previous) => {
404
+ const output = previous?.length === scratchOffset ? previous : new Uint8Array(scratchOffset);
405
+ scratch.copy(output, 0, 0, scratchOffset);
406
+ scratchOffset = 0;
407
+ return output;
408
+ };
409
+ const emitted = /* @__PURE__ */ new Set();
410
+ let previousName;
411
+ const header = (name, type, help) => {
412
+ if (previousName === name) return;
413
+ previousName = name;
414
+ if (!emitted.has(name)) {
415
+ emitted.add(name);
416
+ let cached = headers.get(name);
417
+ if (!cached || cached.type !== type || cached.help !== help) {
418
+ cached = {
419
+ type,
420
+ help,
421
+ bytes: encodePrefix(`# HELP ${name} ${escapeHelp(help)}\n# TYPE ${name} ${type}\n`)
422
+ };
423
+ headers.set(name, cached);
424
+ }
425
+ append(cached.bytes);
426
+ }
427
+ };
428
+ for (const [type, map] of [["counter", counters], ["gauge", gauges]]) {
429
+ for (const sample of sortedSamples(map)) {
430
+ header(sample.name, type, sample.help);
431
+ if (!sample.rendered || sample.renderedDirty) {
432
+ write(sample.prefix, sample.value);
433
+ sample.rendered = finish(sample.rendered);
434
+ sample.renderedDirty = false;
435
+ }
436
+ append(sample.rendered);
437
+ }
438
+ if (type === "counter" && droppedSeries > 0) {
439
+ header(DROPPED_SERIES_COUNTER_NAME, type, "New-series observations dropped because an exporter series budget was reached.");
440
+ write(Buffer.from(`${DROPPED_SERIES_COUNTER_NAME} `), droppedSeries);
441
+ append(finish());
442
+ header(DROPPED_FAMILY_COUNTER_NAME, type, "New-series observations dropped by bounded metric family.");
443
+ for (const [family, count] of familyDrops) write(Buffer.from(`${DROPPED_FAMILY_COUNTER_NAME}${formatLabels({ family })} `), count);
444
+ append(finish());
445
+ }
446
+ }
447
+ for (const sample of sortedSamples(histograms)) {
448
+ if (sample.count === 0) continue;
449
+ header(sample.name, "histogram", sample.help);
450
+ if (!sample.rendered || sample.renderedDirty) {
451
+ sample.bucketPrefixes.forEach((prefix, bucketIndex) => {
452
+ write(prefix, sample.counts[bucketIndex] ?? sample.count);
453
+ });
454
+ write(sample.sumPrefix, sample.sum);
455
+ write(sample.countPrefix, sample.count);
456
+ sample.rendered = finish(sample.rendered);
457
+ sample.renderedDirty = false;
458
+ }
459
+ append(sample.rendered);
460
+ }
461
+ const output = Buffer.from(new ArrayBuffer(totalBytes));
462
+ let offset = 0;
463
+ for (const chunk of chunks) {
464
+ output.set(chunk, offset);
465
+ offset += chunk.length;
466
+ }
467
+ return output;
150
468
  };
151
469
  const reset = () => {
152
- counters.clear();
153
- gauges.clear();
154
- histograms.clear();
470
+ for (const map of [
471
+ counters,
472
+ gauges,
473
+ histograms
474
+ ]) {
475
+ map.samples.length = 0;
476
+ map.shapes.clear();
477
+ }
155
478
  droppedSeries = 0;
479
+ sharedSeries = 0;
480
+ familyCounts.clear();
481
+ familyDrops.clear();
482
+ headers.clear();
483
+ scratch = Buffer.alloc(0);
484
+ prefixBuffer = Buffer.alloc(0);
485
+ prefixOffset = 0;
156
486
  };
157
487
  return {
158
488
  counter,
@@ -160,31 +490,142 @@ function createPrometheusMetricStore() {
160
490
  gauge,
161
491
  clearGauges,
162
492
  histogram,
493
+ reserveHistogram,
163
494
  reset,
164
- snapshot
495
+ render
165
496
  };
166
497
  }
167
498
  //#endregion
168
- //#region extensions/diagnostics-prometheus/src/service-child-process.ts
169
- /** Git owners and SQLite session transports carry bounded operation labels. */
170
- function recordChildProcessSpawn(store, evt) {
171
- store.counter("openclaw_child_process_spawn_total", "Successful child launches through the shared spawn and exec owners.", {
172
- family: normalizeDiagnosticValue(evt.family),
173
- operation: normalizeDiagnosticValue(evt.operation ?? (evt.family === "git" ? "unknown" : "none"))
174
- }, asNonNegativeFiniteNumber(evt.count) ?? 0);
499
+ //#region extensions/diagnostics-prometheus/src/service-gateway-ws.ts
500
+ function createGatewayWsMetricsRecorder(store) {
501
+ const labels = { event: "" };
502
+ const frameLabels = {
503
+ event: "",
504
+ le: ""
505
+ };
506
+ const presenceLabels = { mode: "" };
507
+ const sessionLabels = {
508
+ event: "",
509
+ reason: "",
510
+ delivery: ""
511
+ };
512
+ return (event, count, bytes, wireBytes, buckets, bucketLabels, sessionEvent, presenceMode) => {
513
+ labels.event = frameLabels.event = sessionLabels.event = event;
514
+ store.counter("openclaw_gateway_ws_events_total", "Outbound Gateway WebSocket event frames accepted by the sender per socket.", labels, count);
515
+ store.counter("openclaw_gateway_ws_event_bytes_total", "Encoded outbound Gateway WebSocket event bytes before framing or compression per socket.", labels, bytes);
516
+ if (wireBytes !== void 0) store.counter("openclaw_gateway_ws_event_wire_bytes_total", "Outbound Gateway WebSocket payload bytes after compression per socket, excluding framing.", labels, wireBytes);
517
+ if (count > 0) {
518
+ let cumulative = 0;
519
+ for (let i = 0; i < bucketLabels.length; i++) {
520
+ cumulative += buckets[i];
521
+ frameLabels.le = bucketLabels[i];
522
+ (cumulative === 0 ? store.counterValue : store.counter)("openclaw_gateway_ws_event_frame_bytes", "Cumulative counts of accepted raw Gateway event frames by byte-size upper bound.", frameLabels, cumulative);
523
+ }
524
+ }
525
+ if (presenceMode) {
526
+ presenceLabels.mode = presenceMode;
527
+ store.counter("openclaw_gateway_ws_presence_frames_total", "Outbound presence frames accepted by the sender per socket, by delivery mode.", presenceLabels, count);
528
+ store.counter("openclaw_gateway_ws_presence_bytes_total", "Encoded outbound presence bytes before framing or compression per socket, by delivery mode.", presenceLabels, bytes);
529
+ }
530
+ if (sessionEvent) {
531
+ sessionLabels.reason = sessionEvent.reason;
532
+ sessionLabels.delivery = sessionEvent.delivery;
533
+ store.counter("openclaw_gateway_ws_session_event_frames_total", "Outbound session event frames by reason and exact message subscription per socket.", sessionLabels, count);
534
+ store.counter("openclaw_gateway_ws_session_event_bytes_total", "Encoded outbound session event bytes by reason and exact message subscription per socket.", sessionLabels, bytes);
535
+ }
536
+ };
537
+ }
538
+ function recordGatewayWsConnections(store, operators) {
539
+ store.gauge("openclaw_gateway_ws_operator_connections", "Connected authenticated Gateway operator WebSockets.", {}, operators);
175
540
  }
176
541
  //#endregion
177
542
  //#region extensions/diagnostics-prometheus/src/service-memory.ts
543
+ const WORKER_MEMORY_METRICS = [
544
+ [
545
+ "heapUsed",
546
+ "openclaw_worker_heap_used_bytes",
547
+ "heap usage"
548
+ ],
549
+ [
550
+ "external",
551
+ "openclaw_worker_external_bytes",
552
+ "external memory"
553
+ ],
554
+ [
555
+ "arrayBuffers",
556
+ "openclaw_worker_array_buffers_bytes",
557
+ "ArrayBuffer memory (included in external)"
558
+ ]
559
+ ];
560
+ const HEAP_HEADROOM_METRICS = [
561
+ [
562
+ "limit",
563
+ "openclaw_heap_size_limit_bytes",
564
+ "Smallest live isolate V8 heap limit by thread group."
565
+ ],
566
+ [
567
+ "usedRatio",
568
+ "openclaw_heap_used_ratio",
569
+ "Largest live isolate used/limit ratio by thread group."
570
+ ],
571
+ [
572
+ "oldBytes",
573
+ "openclaw_heap_old_generation_after_gc_bytes",
574
+ "Largest live isolate old_space plus large_object_space usage at its last observed major GC."
575
+ ],
576
+ [
577
+ "oldRatio",
578
+ "openclaw_heap_old_generation_after_gc_ratio",
579
+ "Largest live isolate post-major-GC old-generation/heap-limit ratio by thread group."
580
+ ]
581
+ ];
582
+ function collectHeapHeadroom(headroom, used, limit, oldBytes) {
583
+ if (limit === void 0 || limit <= 0) return;
584
+ headroom.limit = Math.min(headroom.limit ?? limit, limit);
585
+ headroom.usedRatio = Math.max(headroom.usedRatio ?? 0, used / limit);
586
+ if (oldBytes !== void 0) {
587
+ headroom.oldBytes = Math.max(headroom.oldBytes ?? 0, oldBytes);
588
+ headroom.oldRatio = Math.max(headroom.oldRatio ?? 0, oldBytes / limit);
589
+ }
590
+ }
591
+ function recordHeapHeadroom(store, thread, headroom) {
592
+ for (const [field, name, description] of HEAP_HEADROOM_METRICS) store.gauge(name, description, { thread }, headroom[field]);
593
+ }
178
594
  function recordMemorySample(store, memory, byteBuckets) {
595
+ for (const [, name] of HEAP_HEADROOM_METRICS) store.clearGauges(name);
596
+ const mainHeadroom = {};
597
+ collectHeapHeadroom(mainHeadroom, memory.heapUsedBytes, memory.heapSizeLimitBytes, memory.oldGenerationAfterGcBytes);
598
+ recordHeapHeadroom(store, "main", mainHeadroom);
599
+ store.clearGauges("openclaw_process_anonymous_memory_bytes");
600
+ for (const [kind, field] of [["rss", "anonymousRssBytes"], ["swap", "anonymousSwapBytes"]]) store.gauge("openclaw_process_anonymous_memory_bytes", "Latest Linux process anonymous RSS and swap; includes managed heaps and buffers, not allocator totals.", { kind }, asNonNegativeFiniteNumber(memory[field]));
179
601
  for (const message of memory.workerMessages ?? []) {
180
602
  const labels = {
181
603
  kind: message.kind,
182
604
  direction: message.direction
183
605
  };
184
606
  store.counterValue("openclaw_worker_messages_total", "Messages crossing the Gateway main-thread worker boundary.", labels, message.messages);
185
- store.counterValue("openclaw_worker_message_payload_bytes_total", "Known worker payload bytes, excluding transport framing and unmeasured object graphs.", labels, message.payloadBytes);
607
+ for (const [requestClass, bytes] of Object.entries(message.payloadBytesByRequestClass ?? { [message.kind === "sqlite_request" || message.kind === "sqlite_reply" || message.kind === "authority" ? "other" : ""]: message.payloadBytes })) store.counterValue("openclaw_worker_message_payload_bytes_total", "Known worker payload bytes, excluding transport framing and unmeasured object graphs.", requestClass ? {
608
+ ...labels,
609
+ request_class: requestClass
610
+ } : labels, bytes);
186
611
  store.counterValue("openclaw_worker_message_unmeasured_total", "Worker messages without a known payload size; these do not contribute to payload bytes.", labels, message.unmeasuredMessages);
187
612
  }
613
+ for (const selection of memory.sessionRowSelections?.selections ?? []) {
614
+ const labels = {
615
+ kind: selection.kind,
616
+ task: selection.task
617
+ };
618
+ store.counterValue("openclaw_session_row_selections_total", "Successful resident session-row selections by query shape and enclosing task.", labels, selection.calls);
619
+ store.counterValue("openclaw_session_row_selection_rows_total", "Resident session rows returned by selections, before federation and visibility filtering.", labels, selection.rows);
620
+ }
621
+ if (memory.sessionRowSelections) {
622
+ for (const partition of memory.sessionRowSelections.partitions) for (const outcome of ["hits", "misses"]) store.counterValue("openclaw_session_row_partition_cache_total", "Retained session-list partition lookups by outcome and enclosing selection task.", {
623
+ task: partition.task,
624
+ outcome: outcome === "hits" ? "hit" : "miss"
625
+ }, partition[outcome]);
626
+ for (const revision of memory.sessionRowSelections.partitionRevisions) store.counterValue("openclaw_session_row_partition_revisions_total", "Session-list partition revisions by synchronous internal change scope; excludes initial partitions.", { reason: revision.reason }, revision.changes);
627
+ store.counterValue("openclaw_session_row_selection_resets_total", "Broad session-row selection invalidations, including topology replacement and disposal.", {}, memory.sessionRowSelections.resets);
628
+ }
188
629
  for (const worker of memory.workerLifecycle ?? []) {
189
630
  store.counterValue("openclaw_worker_started_total", "Worker isolates created by bounded script basename.", { script: worker.script }, worker.started);
190
631
  for (const { reason, count } of worker.retired) store.counterValue("openclaw_worker_retired_total", "Worker isolates confirmed exited by owner retirement reason.", {
@@ -192,6 +633,23 @@ function recordMemorySample(store, memory, byteBuckets) {
192
633
  reason
193
634
  }, count);
194
635
  }
636
+ for (const worker of memory.workerTaskMetrics ?? []) {
637
+ const labels = {
638
+ worker_class: worker.workerClass,
639
+ script: worker.script
640
+ };
641
+ store.counterValue("openclaw_worker_tasks_completed_total", "Observed terminal task replies from pool workers, including handled failures.", labels, worker.tasksCompleted);
642
+ store.gauge("openclaw_worker_heap_used_high_water_bytes", "Largest observed pool worker heap usage, retained across worker retirement.", labels, worker.heapUsedHighWaterBytes);
643
+ for (const gc of worker.gc) {
644
+ const gcLabels = {
645
+ ...labels,
646
+ kind: gc.kind
647
+ };
648
+ store.counterValue("openclaw_worker_gc_duration_seconds_total", "Observed pool worker garbage collection duration, retained across worker retirement.", gcLabels, gc.durationMs / 1e3);
649
+ store.counterValue("openclaw_worker_gc_collections_total", "Observed pool worker garbage collections, retained across worker retirement.", gcLabels, gc.count);
650
+ store.counterValue("openclaw_worker_gc_reclaimed_bytes_total", "Sum of positive heap usage decreases across observed pool worker garbage collections.", gcLabels, gc.reclaimedBytes);
651
+ }
652
+ }
195
653
  for (const [kind, field] of [
196
654
  ["rss", "rssBytes"],
197
655
  ["heap_total", "heapTotalBytes"],
@@ -217,36 +675,54 @@ function recordMemorySample(store, memory, byteBuckets) {
217
675
  }, space[field]);
218
676
  const workers = /* @__PURE__ */ new Map();
219
677
  store.clearGauges("openclaw_code_mode_worker_heap_bytes");
678
+ store.clearGauges("openclaw_code_mode_worker_memory_bytes");
220
679
  for (const worker of memory.workerHeaps ?? []) {
221
- if (worker.workerSlot !== void 0) for (const [kind, value] of [["used", worker.heapUsed], ["limit", worker.heapSizeLimitBytes]]) store.gauge("openclaw_code_mode_worker_heap_bytes", "Latest live Code Mode isolate heap usage and limit by reusable worker slot.", {
222
- slot: String(worker.workerSlot),
223
- kind
224
- }, value);
680
+ if (worker.workerSlot !== void 0) {
681
+ for (const [kind, value] of [["used", worker.heapUsed], ["limit", worker.heapSizeLimitBytes]]) store.gauge("openclaw_code_mode_worker_heap_bytes", "Latest live Code Mode isolate heap usage and limit by reusable worker slot.", {
682
+ slot: String(worker.workerSlot),
683
+ kind
684
+ }, value);
685
+ for (const [kind, value] of [["external", worker.external], ["array_buffers", worker.arrayBuffers]]) store.gauge("openclaw_code_mode_worker_memory_bytes", "Latest live Code Mode isolate external and ArrayBuffer memory (included in external) by reusable worker slot.", {
686
+ slot: String(worker.workerSlot),
687
+ kind
688
+ }, value);
689
+ }
225
690
  const totals = workers.get(worker.script) ?? {
226
691
  count: 0,
227
- sampled: 0,
228
- heapUsed: void 0
692
+ sampled: 0
229
693
  };
230
694
  totals.count++;
231
695
  totals.sampled++;
232
- const heapUsed = asNonNegativeFiniteNumber(worker.heapUsed);
233
- if (heapUsed !== void 0) totals.heapUsed = (totals.heapUsed ?? 0) + heapUsed;
696
+ collectHeapHeadroom(totals, worker.heapUsed, worker.heapSizeLimitBytes, worker.oldGenerationAfterGcBytes);
697
+ for (const [field] of WORKER_MEMORY_METRICS) {
698
+ const value = worker[field];
699
+ if (value !== void 0) totals[field] = (totals[field] ?? 0) + value;
700
+ }
701
+ for (const space of worker.heapSpaces ?? []) {
702
+ totals.heapSpaces ??= /* @__PURE__ */ new Map();
703
+ totals.heapSpaces.set(space.space_name, (totals.heapSpaces.get(space.space_name) ?? 0) + space.space_used_size);
704
+ }
234
705
  workers.set(worker.script, totals);
235
706
  }
236
707
  for (const worker of memory.workerMemoryMissing ?? []) {
237
708
  const totals = workers.get(worker.script) ?? {
238
709
  count: 0,
239
- sampled: 0,
240
- heapUsed: void 0
710
+ sampled: 0
241
711
  };
242
712
  totals.count++;
243
713
  workers.set(worker.script, totals);
244
714
  }
245
- store.clearGauges("openclaw_worker_heap_used_bytes");
715
+ for (const [, name] of WORKER_MEMORY_METRICS) store.clearGauges(name);
716
+ store.clearGauges("openclaw_worker_heap_space_used_bytes");
246
717
  for (const [script, totals] of workers) {
718
+ recordHeapHeadroom(store, script, totals);
247
719
  store.gauge("openclaw_worker_count", "Worker isolate counts.", { script }, totals.count);
248
720
  store.gauge("openclaw_worker_heap_sampled_count", "Worker isolate counts.", { script }, totals.sampled);
249
- store.gauge("openclaw_worker_heap_used_bytes", "Latest live Worker heap usage by bounded script basename.", { script }, totals.heapUsed);
721
+ for (const [field, name, description] of WORKER_MEMORY_METRICS) store.gauge(name, `Latest live Worker ${description} by bounded script basename.`, { script }, totals[field]);
722
+ for (const [space, bytes] of totals.heapSpaces ?? []) store.gauge("openclaw_worker_heap_space_used_bytes", "Latest live Worker V8 heap space usage by bounded script basename.", {
723
+ script,
724
+ space
725
+ }, bytes);
250
726
  }
251
727
  store.histogram("openclaw_memory_rss_bytes", "RSS memory sample distribution in bytes.", {}, asNonNegativeFiniteNumber(memory.rssBytes), byteBuckets);
252
728
  }
@@ -297,6 +773,20 @@ function recordModelUsage(store, evt) {
297
773
  }
298
774
  //#endregion
299
775
  //#region extensions/diagnostics-prometheus/src/service-operation-timing.ts
776
+ const GC_DURATION_NAME = "openclaw_gc_duration_seconds";
777
+ const GC_DURATION_HELP = "Elapsed garbage collection duration in seconds by collection kind, callback flags, and isolate role.";
778
+ function reserveMainGcHistograms(store) {
779
+ for (const kind of [
780
+ "scavenge",
781
+ "mark_sweep_compact",
782
+ "incremental",
783
+ "weak_processing"
784
+ ]) store.reserveHistogram(GC_DURATION_NAME, GC_DURATION_HELP, {
785
+ kind,
786
+ reason: "other",
787
+ thread: "main"
788
+ });
789
+ }
300
790
  const RPC_DURATION_BUCKETS_SECONDS = [
301
791
  .005,
302
792
  .01,
@@ -341,6 +831,14 @@ const RPC_DURATION_BUCKETS_SECONDS = [
341
831
  600
342
832
  ];
343
833
  const RESPONSE_BYTE_BUCKETS = Array.from({ length: 17 }, (_, index) => 1024 * 2 ** index);
834
+ const MAIN_THREAD_STALL_BUCKETS_SECONDS = [
835
+ .25,
836
+ .5,
837
+ 1,
838
+ 2.5,
839
+ 5,
840
+ 10
841
+ ];
344
842
  const HEAP_DELTA_BYTE_BUCKETS = [
345
843
  ...RESPONSE_BYTE_BUCKETS.toReversed().map((bytes) => -bytes),
346
844
  0,
@@ -356,9 +854,41 @@ const CATALOG_LIST_PHASES = /* @__PURE__ */ new Set([
356
854
  "projection_final",
357
855
  "delivery"
358
856
  ]);
857
+ const CHAT_STARTUP_PHASE = /^chat\.startup\.(session_entry|pending_inputs|history_page|startup_projection|projection_final|session_info)$/;
858
+ const DIFF_CACHE_MISS_REASONS = /* @__PURE__ */ new Set([
859
+ "cold",
860
+ "evicted",
861
+ "pending",
862
+ "no-revision",
863
+ "no-revision-environment",
864
+ "no-revision-platform",
865
+ "no-revision-metadata",
866
+ "no-revision-git-installation",
867
+ "no-revision-config",
868
+ "no-revision-reftable",
869
+ "no-revision-alternates",
870
+ "no-revision-symlink",
871
+ "no-revision-nested-repository",
872
+ "no-revision-status",
873
+ "no-revision-unmerged",
874
+ "no-revision-submodule",
875
+ "no-revision-permission",
876
+ "no-revision-missing",
877
+ "no-revision-io",
878
+ "oversized",
879
+ "revision-changed-head",
880
+ "revision-changed-index",
881
+ "revision-changed-untracked-set",
882
+ "revision-changed-worktree-mtime",
883
+ "revision-changed-metadata"
884
+ ]);
359
885
  const CHAT_SEND_PHASE = /^chat\.send\.(authority|admission|preparation|attachments|replyContext|authoring|persist|runAdmission|replyInitialization|snapshot|worktree|effects|response|dispatch)$/;
886
+ const CONTROL_UI_DOCUMENT_STAGE = /^control-ui\.document\.(auth_grants|identity_avatar|plugin_catalog|git_branch|preferences|render|total)$/;
360
887
  function recordOperationTimingEvent(store, evt, metadata) {
361
888
  switch (evt.type) {
889
+ case "sessions.list.baseline.retired":
890
+ if (metadata.trusted) store.counter("openclaw_sessions_list_baseline_retirements_total", "Session list baseline retirement events by cause, not rows or views retired.", { cause: evt.cause });
891
+ return;
362
892
  case "secret.egress":
363
893
  store.counter("openclaw_secret_egress_requests_total", "Secret egress proxy requests by outcome and credential substitution.", {
364
894
  outcome: evt.outcome,
@@ -378,20 +908,87 @@ function recordOperationTimingEvent(store, evt, metadata) {
378
908
  store.counter("openclaw_gateway_http_cancelled_total", "Gateway HTTP requests cancelled before completion.", { source: evt.source });
379
909
  return;
380
910
  case "diagnostic.gc":
381
- store.histogram("openclaw_gc_duration_seconds", "Elapsed garbage collection duration in seconds by collection kind and isolate role.", {
911
+ if (evt.kind === "mark_sweep_compact") {
912
+ const labels = { thread: evt.thread ?? "main" };
913
+ store.gauge("openclaw_heap_used_after_major_gc_bytes", "Native used heap after the latest received major collection by isolate role.", labels, evt.heapUsedAfterMajorGcBytes);
914
+ store.gauge("openclaw_heap_used_after_major_gc_timestamp_seconds", "Unix observation time of the latest received native major-GC heap sample.", labels, evt.heapUsedAfterMajorGcTimestampSeconds);
915
+ }
916
+ store.histogram(GC_DURATION_NAME, GC_DURATION_HELP, {
382
917
  kind: evt.kind ?? "unknown",
918
+ reason: evt.reason ?? "other",
383
919
  thread: evt.thread ?? "main",
384
920
  ...evt.workerSlot === void 0 ? {} : { slot: String(evt.workerSlot) }
385
921
  }, seconds(evt.durationMs));
386
922
  return;
923
+ case "gateway.main_thread.stall":
924
+ for (const stall of evt.stalls) store.histogram("openclaw_gateway_main_thread_stall_seconds", "Elapsed main-thread tasks lasting at least 250 milliseconds, by work scope.", { task: normalizeDiagnosticValue(stall.task) }, seconds(stall.durationMs), MAIN_THREAD_STALL_BUCKETS_SECONDS);
925
+ store.counter("openclaw_gateway_main_thread_stalls_dropped_total", "Main-thread stall records dropped because the attribution buffer was full.", {}, evt.dropped);
926
+ return;
387
927
  case "gateway.event_loop.sample":
388
928
  store.histogram("openclaw_gateway_event_loop_delay_max_seconds", "Maximum event-loop delay per completed Gateway observation window in seconds.", {}, seconds(evt.delayMaxMs));
389
929
  store.counter("openclaw_gateway_event_loop_observed_seconds_total", "Elapsed seconds covered by completed Gateway event-loop observation windows.", {}, evt.intervalMs / 1e3);
390
930
  return;
391
931
  case "diagnostic.phase.completed": {
392
932
  if (!metadata.trusted) return;
933
+ const documentStage = CONTROL_UI_DOCUMENT_STAGE.exec(evt.name)?.[1];
934
+ if (documentStage) {
935
+ store.histogram("openclaw_control_ui_document_duration_seconds", "Elapsed Control UI HTML document handler stages in seconds; stages can overlap.", { stage: documentStage }, seconds(evt.durationMs));
936
+ return;
937
+ }
938
+ if (evt.name === "sessions.diff" || evt.name === "checkout.diff") {
939
+ const outcome = evt.details?.outcome;
940
+ if (outcome !== "cache-hit" && outcome !== "cache-miss") return;
941
+ const reason = evt.details?.missReason;
942
+ if (outcome === "cache-miss" && typeof reason === "string" && DIFF_CACHE_MISS_REASONS.has(reason)) store.counter("openclaw_diff_cache_misses_total", "Local diff cache misses by bounded reason; pending reads join an existing load.", {
943
+ method: evt.name,
944
+ reason
945
+ });
946
+ for (const stage of [
947
+ "admission",
948
+ "checkout-resolve",
949
+ "cache-validate",
950
+ "worker-queue",
951
+ "git-wall",
952
+ "encode"
953
+ ]) {
954
+ const elapsed = evt.details?.[stage];
955
+ store.histogram("openclaw_diff_stage_seconds", "Elapsed local diff stages; cache validation includes its worker queue and Git wall time.", {
956
+ method: evt.name,
957
+ stage,
958
+ outcome
959
+ }, typeof elapsed === "number" ? seconds(elapsed) : void 0, RPC_DURATION_BUCKETS_SECONDS);
960
+ }
961
+ return;
962
+ }
393
963
  if (evt.name === "worktree.preparation") {
394
- recordWorktreePreparation(store, evt);
964
+ const { kind, template, outcome } = evt.details ?? {};
965
+ if (kind !== "managed" && kind !== "sandbox" || ![
966
+ "warm",
967
+ "cold",
968
+ "unavailable",
969
+ "reused"
970
+ ].includes(String(template)) || outcome !== "returned" && outcome !== "threw") return;
971
+ for (const preparationPhase of [
972
+ "total",
973
+ "allocate",
974
+ "checkout",
975
+ "setup",
976
+ "templatePrepare",
977
+ "templateApply",
978
+ "snapshot",
979
+ "synchronizeCanonical",
980
+ "synchronizeProjection",
981
+ "containerStart",
982
+ "workspaceLayout"
983
+ ]) {
984
+ const elapsed = preparationPhase === "total" ? evt.durationMs : evt.details?.[preparationPhase];
985
+ store.histogram("openclaw_worktree_preparation_seconds", "Elapsed managed worktree preparation time; nested phases are inclusive.", {
986
+ kind,
987
+ template: String(template),
988
+ outcome,
989
+ phase: preparationPhase
990
+ }, typeof elapsed === "number" ? seconds(elapsed) : void 0);
991
+ }
395
992
  return;
396
993
  }
397
994
  const sendPhase = CHAT_SEND_PHASE.exec(evt.name)?.[1];
@@ -403,6 +1000,14 @@ function recordOperationTimingEvent(store, evt, metadata) {
403
1000
  }, seconds(evt.durationMs));
404
1001
  return;
405
1002
  }
1003
+ const startupPhase = CHAT_STARTUP_PHASE.exec(evt.name)?.[1];
1004
+ if (startupPhase) {
1005
+ store.histogram("openclaw_gateway_rpc_stage_seconds", "Elapsed time for completed Gateway RPC owner stages.", {
1006
+ method: "chat.startup",
1007
+ phase: startupPhase
1008
+ }, seconds(evt.durationMs));
1009
+ return;
1010
+ }
406
1011
  if (!evt.name.startsWith(CATALOG_LIST_PHASE_PREFIX)) return;
407
1012
  const phase = evt.name.slice(CATALOG_LIST_PHASE_PREFIX.length);
408
1013
  const duration = seconds(evt.durationMs);
@@ -420,6 +1025,10 @@ function recordOperationTimingEvent(store, evt, metadata) {
420
1025
  const labels = { method: evt.method };
421
1026
  if (evt.phase === "received") {
422
1027
  store.counter("openclaw_gateway_rpc_requests_total", "Authenticated Gateway WebSocket requests received.", labels);
1028
+ store.counter("openclaw_gateway_rpc_client_requests_total", "Gateway RPC requests by bounded handshake client kind.", {
1029
+ method: evt.method,
1030
+ client_kind: evt.clientKind ?? "other"
1031
+ });
423
1032
  if (evt.method === "models.list" && evt.modelsList) {
424
1033
  const catalog = evt.modelsList;
425
1034
  store.counter("openclaw_models_list_requests_total", "Model catalog requests by client kind and bounded request shape.", {
@@ -431,10 +1040,54 @@ function recordOperationTimingEvent(store, evt, metadata) {
431
1040
  prepared_only: String(catalog.preparedOnly)
432
1041
  });
433
1042
  }
1043
+ if (evt.method === "sessions.list" && evt.sessionsList) {
1044
+ const sessions = evt.sessionsList;
1045
+ store.counter("openclaw_sessions_list_requests_total", "Session list requests by client kind and bounded request shape.", {
1046
+ client_kind: sessions.clientKind,
1047
+ source: sessions.source,
1048
+ caller_source: sessions.callerSource,
1049
+ row_mode: sessions.rowMode,
1050
+ filter: sessions.filter
1051
+ });
1052
+ }
1053
+ if (evt.method === "cron.list" && evt.cronList) {
1054
+ const cron = evt.cronList;
1055
+ store.counter("openclaw_cron_list_requests_total", "Cron list requests by client kind and bounded request shape.", {
1056
+ client_kind: cron.clientKind,
1057
+ source: cron.source,
1058
+ scope: cron.scope,
1059
+ compact: String(cron.compact),
1060
+ page: cron.page
1061
+ });
1062
+ }
434
1063
  return;
435
1064
  }
436
1065
  if (evt.phase === "response" && (evt.outcome === "ok" || evt.outcome === "error")) {
1066
+ if (evt.outcome === "ok" && evt.firstResponse !== false && evt.sessionsList?.baseMissReason) store.counter("openclaw_sessions_list_base_misses_total", "Successful full sidebar responses with a supplied base, by miss reason.", { reason: evt.sessionsList.baseMissReason });
437
1067
  store.histogram("openclaw_gateway_rpc_response_bytes", "Encoded Gateway RPC response frame size in bytes, including later frames.", labels, evt.responseBytes, RESPONSE_BYTE_BUCKETS);
1068
+ if (evt.responseBytes !== void 0 && evt.sessionsList) {
1069
+ const sessions = evt.sessionsList;
1070
+ store.counter("openclaw_sessions_list_response_bytes_total", "Encoded successful session list response bytes by request shape and row outcome.", {
1071
+ client_kind: sessions.clientKind,
1072
+ source: sessions.source,
1073
+ caller_source: sessions.callerSource,
1074
+ row_mode: sessions.rowMode,
1075
+ filter: sessions.filter,
1076
+ rows_outcome: sessions.rowsOutcome
1077
+ }, evt.responseBytes);
1078
+ }
1079
+ if (evt.chatHistory) {
1080
+ const history = evt.chatHistory;
1081
+ const historyLabels = {
1082
+ client_kind: history.clientKind,
1083
+ source: history.source,
1084
+ shape: history.shape,
1085
+ limit_bucket: history.limitBucket,
1086
+ outcome: history.outcome
1087
+ };
1088
+ if (evt.firstResponse !== false) store.counter("openclaw_chat_history_requests_total", "Successful chat history requests by request shape and response outcome.", historyLabels);
1089
+ if (evt.responseBytes !== void 0) store.counter("openclaw_chat_history_response_bytes_total", "Encoded successful chat history response bytes by request shape and response outcome.", historyLabels, evt.responseBytes);
1090
+ }
438
1091
  if (evt.firstResponse === false) return;
439
1092
  }
440
1093
  store.counter("openclaw_gateway_rpc_outcomes_total", "Gateway RPC observations by phase and outcome.", {
@@ -451,53 +1104,24 @@ function recordOperationTimingEvent(store, evt, metadata) {
451
1104
  }
452
1105
  }
453
1106
  }
454
- function recordWorktreePreparation(store, evt) {
455
- const { kind, template, outcome } = evt.details ?? {};
456
- if (kind !== "managed" && kind !== "sandbox" || ![
457
- "warm",
458
- "cold",
459
- "unavailable",
460
- "reused"
461
- ].includes(String(template)) || outcome !== "returned" && outcome !== "threw") return;
462
- for (const phase of [
463
- "total",
464
- "allocate",
465
- "checkout",
466
- "setup",
467
- "templatePrepare",
468
- "templateApply",
469
- "snapshot",
470
- "synchronizeCanonical",
471
- "synchronizeProjection",
472
- "containerStart",
473
- "workspaceLayout"
474
- ]) {
475
- const elapsed = phase === "total" ? evt.durationMs : evt.details?.[phase];
476
- store.histogram("openclaw_worktree_preparation_seconds", "Elapsed managed worktree preparation time; nested phases are inclusive.", {
477
- kind,
478
- template: String(template),
479
- outcome,
480
- phase
481
- }, typeof elapsed === "number" ? seconds(elapsed) : void 0);
1107
+ //#endregion
1108
+ //#region extensions/diagnostics-prometheus/src/service-retention.ts
1109
+ function recordSessionRetention(store, evt) {
1110
+ if (evt.type === "sessions.event.rows.reset") {
1111
+ store.counter("openclaw_session_event_rows_reset_total", "Retained session-event wire baselines retired by cause.", { cause: evt.cause }, evt.count);
1112
+ return;
482
1113
  }
1114
+ if (evt.type === "sessions.list.retention") {
1115
+ store.gauge("openclaw_sessions_list_retained_views", "Retained session-list views across all publications.", {}, evt.views);
1116
+ store.gauge("openclaw_sessions_list_retained_refs", "Row references and selection entries retained by session-list views.", {}, evt.refs);
1117
+ store.gauge("openclaw_sessions_list_retained_bytes", "Estimated memory retained by session-list views.", {}, evt.estimatedBytes);
1118
+ return;
1119
+ }
1120
+ store.gauge("openclaw_session_event_rows_retained_entries", "Session-event field-hash vectors retained across all connections.", {}, evt.entries);
1121
+ store.gauge("openclaw_session_event_rows_retained_bytes", "Estimated memory retained by session-event field-hash vectors.", {}, evt.estimatedBytes);
483
1122
  }
484
1123
  //#endregion
485
1124
  //#region extensions/diagnostics-prometheus/src/service-sessions.ts
486
- function createGatewayWorkMetricsRecorder(store) {
487
- return (snapshot) => {
488
- if (!snapshot) {
489
- store.clearGauges("openclaw_sessions_active");
490
- store.clearGauges("openclaw_gateway_active_work");
491
- return;
492
- }
493
- for (const state of ["running", "queued"]) store.gauge("openclaw_sessions_active", "Current active sessions in the Gateway session roster by run state.", { state }, snapshot.sessions[state]);
494
- for (const kind of [
495
- "agentRuns",
496
- "chatRuns",
497
- "queuedTurns"
498
- ]) store.gauge("openclaw_gateway_active_work", "Current Gateway work by owner; categories may overlap.", { kind }, snapshot.work[kind]);
499
- };
500
- }
501
1125
  function recordSessionDiagnosticEvent(store, evt) {
502
1126
  switch (evt.type) {
503
1127
  case "session.recovery.requested":
@@ -537,60 +1161,62 @@ function recordSessionDiagnosticEvent(store, evt) {
537
1161
  }
538
1162
  //#endregion
539
1163
  //#region extensions/diagnostics-prometheus/src/service-worker.ts
540
- function recordWorkerRequest(store, event) {
541
- store.gauge("openclaw_worker_queue_depth", "Requests awaiting dispatch across worker owners of this kind.", { kind: event.kind }, event.queueDepth);
542
- const labels = {
543
- kind: event.kind,
544
- request_class: event.requestClass
1164
+ function createWorkerRequestMetricsRecorder(store) {
1165
+ return (kind, requestClass, requestPhase, queueDepth, queueWaitMs, durationMs, timing) => {
1166
+ store.gauge("openclaw_worker_queue_depth", "Requests awaiting dispatch across worker owners of this kind.", { kind }, queueDepth);
1167
+ const labels = {
1168
+ kind,
1169
+ request_class: requestClass
1170
+ };
1171
+ store.histogram("openclaw_worker_queue_wait_seconds", "Elapsed time from worker request enqueue to dispatch.", labels, seconds(queueWaitMs));
1172
+ store.histogram("openclaw_worker_request_seconds", "Elapsed time from worker dispatch to reply or failure, including preparation and transport.", labels, seconds(durationMs));
1173
+ if (requestPhase !== "completed" || !timing) return;
1174
+ for (const [phase, start, end] of [
1175
+ [
1176
+ "dispatch",
1177
+ "dispatched",
1178
+ "posted"
1179
+ ],
1180
+ [
1181
+ "delivery",
1182
+ "posted",
1183
+ "received"
1184
+ ],
1185
+ [
1186
+ "input",
1187
+ "received",
1188
+ "inputReady"
1189
+ ],
1190
+ [
1191
+ "prepare",
1192
+ "inputReady",
1193
+ "executeStarted"
1194
+ ],
1195
+ [
1196
+ "execute",
1197
+ "executeStarted",
1198
+ "executeFinished"
1199
+ ],
1200
+ [
1201
+ "result",
1202
+ "executeFinished",
1203
+ "replied"
1204
+ ],
1205
+ [
1206
+ "reply",
1207
+ "replied",
1208
+ "replyReceived"
1209
+ ]
1210
+ ]) {
1211
+ const started = timing[start];
1212
+ const finished = timing[end];
1213
+ if (started === void 0 || finished === void 0) continue;
1214
+ store.histogram("openclaw_worker_request_phase_seconds", "Elapsed time within an observed SQLite worker request phase, including transport.", {
1215
+ ...labels,
1216
+ phase
1217
+ }, seconds(finished - started));
1218
+ }
545
1219
  };
546
- store.histogram("openclaw_worker_queue_wait_seconds", "Elapsed time from worker request enqueue to dispatch.", labels, seconds(event.queueWaitMs));
547
- store.histogram("openclaw_worker_request_seconds", "Elapsed time from worker dispatch to reply or failure, including preparation and transport.", labels, seconds(event.durationMs));
548
- if (event.phase !== "completed" || !event.timing) return;
549
- for (const [phase, start, end] of [
550
- [
551
- "dispatch",
552
- "dispatched",
553
- "posted"
554
- ],
555
- [
556
- "delivery",
557
- "posted",
558
- "received"
559
- ],
560
- [
561
- "input",
562
- "received",
563
- "inputReady"
564
- ],
565
- [
566
- "prepare",
567
- "inputReady",
568
- "executeStarted"
569
- ],
570
- [
571
- "execute",
572
- "executeStarted",
573
- "executeFinished"
574
- ],
575
- [
576
- "result",
577
- "executeFinished",
578
- "replied"
579
- ],
580
- [
581
- "reply",
582
- "replied",
583
- "replyReceived"
584
- ]
585
- ]) {
586
- const started = event.timing[start];
587
- const finished = event.timing[end];
588
- if (started === void 0 || finished === void 0) continue;
589
- store.histogram("openclaw_worker_request_phase_seconds", "Elapsed time within an observed SQLite worker request phase, including transport.", {
590
- ...labels,
591
- phase
592
- }, seconds(finished - started));
593
- }
594
1220
  }
595
1221
  //#endregion
596
1222
  //#region extensions/diagnostics-prometheus/src/service.ts
@@ -626,36 +1252,6 @@ function safeErrorMessage(err) {
626
1252
  const message = err instanceof Error ? err.message ?? err.name : String(err);
627
1253
  return truncateUtf16Safe(redactSensitiveText(message).replaceAll("\0", " ").replace(/[\r\n\t\u2028\u2029]/gu, " "), 500);
628
1254
  }
629
- function renderPrometheusMetrics(store) {
630
- const snapshot = store.snapshot();
631
- const lines = [];
632
- const emitted = /* @__PURE__ */ new Set();
633
- const emitHeader = (name, type, help) => {
634
- if (emitted.has(name)) return;
635
- emitted.add(name);
636
- lines.push(`# HELP ${name} ${escapeHelp(help)}`);
637
- lines.push(`# TYPE ${name} ${type}`);
638
- };
639
- for (const [type, entries] of [["counter", snapshot.counters], ["gauge", snapshot.gauges]]) {
640
- entries.sort(([left], [right]) => left.localeCompare(right));
641
- for (const [key, sample] of entries) {
642
- const name = key.split("|", 1)[0] ?? "";
643
- emitHeader(name, type, sample.help);
644
- lines.push(`${name}${formatLabels(sample.labels)} ${formatPrometheusNumber(sample.value)}`);
645
- }
646
- }
647
- snapshot.histograms.sort(([left], [right]) => left.localeCompare(right));
648
- for (const [key, sample] of snapshot.histograms) {
649
- const name = key.split("|", 1)[0] ?? "";
650
- emitHeader(name, "histogram", sample.help);
651
- for (let index = 0; index < sample.buckets.length; index += 1) lines.push(`${sample.bucketPrefixes[index]}${formatPrometheusNumber(sample.counts[index] ?? 0)}`);
652
- lines.push(`${sample.bucketPrefixes[sample.buckets.length]}${formatPrometheusNumber(sample.count)}`);
653
- lines.push(`${name}_sum${sample.labels} ${formatPrometheusNumber(sample.sum)}`);
654
- lines.push(`${name}_count${sample.labels} ${formatPrometheusNumber(sample.count)}`);
655
- }
656
- lines.push("");
657
- return lines.join("\n");
658
- }
659
1255
  function webhookLabels(evt) {
660
1256
  return {
661
1257
  channel: normalizeDiagnosticValue(evt.channel),
@@ -665,16 +1261,23 @@ function webhookLabels(evt) {
665
1261
  function recordDiagnosticEvent(store, evt, metadata) {
666
1262
  if (!metadata.trusted && !isInternalDiagnosticEventMetadata(metadata)) return;
667
1263
  switch (evt.type) {
668
- case "worker.request":
669
- if (metadata.trusted) recordWorkerRequest(store, evt);
1264
+ case "gateway.ws.connections":
1265
+ if (metadata.trusted) recordGatewayWsConnections(store, evt.operators);
1266
+ return;
1267
+ case "sessions.list.retention":
1268
+ case "sessions.event.retention":
1269
+ case "sessions.event.rows.reset":
1270
+ if (metadata.trusted) recordSessionRetention(store, evt);
670
1271
  return;
671
1272
  case "diagnostic.phase.completed":
672
1273
  case "secret.egress":
673
1274
  case "tool.catalog":
674
1275
  case "gateway.rpc":
1276
+ case "sessions.list.baseline.retired":
675
1277
  case "gateway.http.cancelled":
676
1278
  case "diagnostic.gc":
677
1279
  case "gateway.event_loop.sample":
1280
+ case "gateway.main_thread.stall":
678
1281
  recordOperationTimingEvent(store, evt, metadata);
679
1282
  return;
680
1283
  case "model.usage":
@@ -850,17 +1453,22 @@ function recordDiagnosticEvent(store, evt, metadata) {
850
1453
  recordSessionDiagnosticEvent(store, evt);
851
1454
  return;
852
1455
  case "queue.lane.enqueue":
853
- case "queue.lane.dequeue":
854
- store.gauge("openclaw_queue_lane_size", "Current diagnostic queue lane size.", { lane: normalizeDiagnosticLane(evt.lane) }, asNonNegativeFiniteNumber(evt.queueSize));
855
- 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));
1456
+ case "queue.lane.dequeue": {
1457
+ const labels = { lane: normalizeDiagnosticLane(evt.lane) };
1458
+ store.gauge("openclaw_queue_lane_size", "Current diagnostic queue lane size.", labels, asNonNegativeFiniteNumber(evt.queueSize));
1459
+ if (evt.type === "queue.lane.dequeue") store.histogram("openclaw_queue_lane_wait_seconds", "Queue lane wait time in seconds.", labels, seconds(evt.waitMs));
856
1460
  return;
1461
+ }
857
1462
  case "session.state":
858
1463
  case "session.stuck":
859
1464
  case "session.turn.created":
860
1465
  recordSessionDiagnosticEvent(store, evt);
861
1466
  return;
862
1467
  case "diagnostic.child_process.spawn":
863
- recordChildProcessSpawn(store, evt);
1468
+ store.counter("openclaw_child_process_spawn_total", "Successful child launches through the shared spawn and exec owners.", {
1469
+ family: normalizeDiagnosticValue(evt.family),
1470
+ operation: normalizeDiagnosticValue(evt.operation ?? (evt.family === "git" ? "unknown" : "none"))
1471
+ }, asNonNegativeFiniteNumber(evt.count) ?? 0);
864
1472
  return;
865
1473
  case "diagnostic.memory.sample":
866
1474
  recordMemorySample(store, evt.memory, BYTE_BUCKETS);
@@ -945,23 +1553,24 @@ function createMetricsHandler(store) {
945
1553
  res.end("Method Not Allowed");
946
1554
  return true;
947
1555
  }
948
- const body = renderPrometheusMetrics(store);
1556
+ const body = store.render();
949
1557
  res.statusCode = 200;
950
1558
  res.setHeader("Cache-Control", "no-store");
951
1559
  res.setHeader("Content-Type", "text/plain; version=0.0.4; charset=utf-8");
952
- res.setHeader("Content-Length", String(Buffer.byteLength(body)));
953
- if (req.method === "HEAD") {
954
- res.end();
955
- return true;
956
- }
957
- res.end(body);
1560
+ res.setHeader("Content-Length", String(body.length));
1561
+ res.flushHeaders();
1562
+ res.end(req.method === "HEAD" ? void 0 : body, (error) => {
1563
+ if (!error) body.buffer.transfer(0);
1564
+ });
958
1565
  return true;
959
1566
  };
960
1567
  }
961
1568
  function createDiagnosticsPrometheusExporter() {
962
- const store = createPrometheusMetricStore();
963
- let unsubscribe;
964
- let unsubscribeWork;
1569
+ let logger;
1570
+ const store = createPrometheusMetricStore((family) => {
1571
+ logger?.warn(`diagnostics-prometheus: series budget reached for ${family}; new label sets are being dropped. See openclaw_prometheus_family_series_dropped_total.`);
1572
+ });
1573
+ let subscriptions = [];
965
1574
  let internalDiagnostics;
966
1575
  const reportExporterStatus = (update) => {
967
1576
  try {
@@ -976,7 +1585,6 @@ function createDiagnosticsPrometheusExporter() {
976
1585
  };
977
1586
  return {
978
1587
  handler: createMetricsHandler(store),
979
- render: () => renderPrometheusMetrics(store),
980
1588
  service: {
981
1589
  id: "diagnostics-prometheus",
982
1590
  start(ctx) {
@@ -985,19 +1593,44 @@ function createDiagnosticsPrometheusExporter() {
985
1593
  ctx.logger.error("diagnostics-prometheus: internal diagnostics capability unavailable");
986
1594
  return;
987
1595
  }
1596
+ logger = ctx.logger;
1597
+ reserveMainGcHistograms(store);
988
1598
  const identity = isDiagnosticsEnabled(ctx.config) ? ctx.internalDiagnostics?.getRuntimeIdentity?.() : void 0;
989
1599
  if (identity) store.gauge("openclaw_gateway_build_info", "Identity of the hosting process and its loaded build; not a health or exporter epoch.", {
990
1600
  process_instance_id: identity.processInstanceId,
991
1601
  ...identity.buildId ? { build_id: identity.buildId } : {}
992
1602
  }, 1);
993
- unsubscribeWork = ctx.internalDiagnostics?.onGatewayWorkMetrics?.(createGatewayWorkMetricsRecorder(store));
994
- unsubscribe = subscribe((event, metadata) => {
995
- try {
996
- recordDiagnosticEvent(store, event, metadata);
997
- } catch (err) {
998
- ctx.logger.error(`diagnostics-prometheus: event handler failed (${event.type}): ${safeErrorMessage(err)}`);
999
- }
1000
- }, { exclude: ["log.record"] }, { includePrivateData: false });
1603
+ subscriptions = [
1604
+ ctx.internalDiagnostics?.onGatewayWorkMetrics?.((snapshot) => {
1605
+ if (!snapshot) {
1606
+ store.clearGauges("openclaw_sessions_active");
1607
+ store.clearGauges("openclaw_gateway_active_work");
1608
+ return;
1609
+ }
1610
+ for (const state of ["running", "queued"]) store.gauge("openclaw_sessions_active", "Current active sessions in the Gateway session roster by run state.", { state }, snapshot.sessions[state]);
1611
+ for (const kind of [
1612
+ "agentRuns",
1613
+ "chatRuns",
1614
+ "queuedTurns"
1615
+ ]) store.gauge("openclaw_gateway_active_work", "Current Gateway work by owner; categories may overlap.", { kind }, snapshot.work[kind]);
1616
+ }),
1617
+ ctx.internalDiagnostics?.onGatewayWsMetrics?.(createGatewayWsMetricsRecorder(store)),
1618
+ ctx.internalDiagnostics?.onWorkerRequestMetrics?.(createWorkerRequestMetricsRecorder(store)),
1619
+ subscribe((event, metadata) => {
1620
+ try {
1621
+ recordDiagnosticEvent(store, event, metadata);
1622
+ } catch (err) {
1623
+ ctx.logger.error(`diagnostics-prometheus: event handler failed (${event.type}): ${safeErrorMessage(err)}`);
1624
+ }
1625
+ }, {
1626
+ exclude: [
1627
+ "log.record",
1628
+ "worker.request",
1629
+ "gateway.ws.event"
1630
+ ],
1631
+ includePromptStats: false
1632
+ }, { includePrivateData: false })
1633
+ ];
1001
1634
  internalDiagnostics = ctx.internalDiagnostics;
1002
1635
  reportExporterStatus({
1003
1636
  signal: "metrics",
@@ -1007,10 +1640,8 @@ function createDiagnosticsPrometheusExporter() {
1007
1640
  });
1008
1641
  },
1009
1642
  stop() {
1010
- unsubscribeWork?.();
1011
- unsubscribeWork = void 0;
1012
- unsubscribe?.();
1013
- unsubscribe = void 0;
1643
+ for (const unsubscribe of subscriptions) unsubscribe?.();
1644
+ subscriptions = [];
1014
1645
  reportExporterStatus({
1015
1646
  signal: "metrics",
1016
1647
  transport: "prometheus-scrape",
@@ -1018,6 +1649,7 @@ function createDiagnosticsPrometheusExporter() {
1018
1649
  });
1019
1650
  internalDiagnostics = void 0;
1020
1651
  store.reset();
1652
+ logger = void 0;
1021
1653
  }
1022
1654
  }
1023
1655
  };