@openclaw/diagnostics-otel 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 CHANGED
@@ -1,4 +1,4 @@
1
- import { t as createDiagnosticsOtelService } from "./runtime-api-DaBnxBEO.js";
1
+ import { t as createDiagnosticsOtelService } from "./runtime-api-B9-DLHbi.js";
2
2
  import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry";
3
3
  //#region extensions/diagnostics-otel/index.ts
4
4
  var diagnostics_otel_default = definePluginEntry({
@@ -1,5 +1,5 @@
1
1
  import { isValidDiagnosticSpanId, isValidDiagnosticTraceFlags, isValidDiagnosticTraceId, redactSensitiveText } from "./api.js";
2
- import { ROOT_CONTEXT, SpanKind, SpanStatusCode, TraceFlags, context, createContextKey, diag, isSpanContextValid, metrics, propagation, trace } from "@opentelemetry/api";
2
+ import { ROOT_CONTEXT, SpanKind, SpanStatusCode, TraceFlags, context, createContextKey, createNoopMeter, diag, isSpanContextValid, metrics, propagation, trace } from "@opentelemetry/api";
3
3
  import * as otelCore from "@opentelemetry/core";
4
4
  import { CompositePropagator, ExportResultCode, W3CBaggagePropagator, W3CTraceContextPropagator, getStringListFromEnv } from "@opentelemetry/core";
5
5
  import { OTLPMetricExporter } from "@opentelemetry/exporter-metrics-otlp-proto";
@@ -10,7 +10,7 @@ import { BasicTracerProvider, BatchSpanProcessor, ParentBasedSampler, TraceIdRat
10
10
  import { ATTR_SERVICE_NAME } from "@opentelemetry/semantic-conventions";
11
11
  import { registerUnhandledRejectionHandler } from "openclaw/plugin-sdk/runtime-env";
12
12
  import { truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
13
- import { normalizeDiagnosticLane, normalizeDiagnosticValue } from "openclaw/plugin-sdk/diagnostic-runtime";
13
+ import { isInternalDiagnosticEventMetadata, normalizeDiagnosticLane, normalizeDiagnosticValue } from "openclaw/plugin-sdk/diagnostic-runtime";
14
14
  import { readFileSync } from "node:fs";
15
15
  import nodePath from "node:path";
16
16
  import { collectErrorGraphCandidates } from "openclaw/plugin-sdk/error-runtime";
@@ -443,10 +443,19 @@ function findOtlpExporterError(reason) {
443
443
  //#region extensions/diagnostics-otel/src/service-events.ts
444
444
  function createDiagnosticsEventHandler(params) {
445
445
  const { logger, recorders, recordLogRecord, recordSecurityEvent } = params;
446
- const { recordModelUsage, recordWebhookReceived, recordWebhookProcessed, recordWebhookError, recordMessageQueued, recordMessageReceived, recordMessageDispatchStarted, recordMessageDispatchCompleted, recordMessageProcessed, recordMessageDeliveryStarted, recordMessageDeliveryCompleted, recordMessageDeliveryError, recordTalkEvent, recordLaneEnqueue, recordLaneDequeue, recordSessionState, recordSessionTurnCreated, recordSessionStuck, recordSessionRecoveryRequested, recordSessionRecoveryCompleted, recordRunAttempt, recordHeartbeat, recordLivenessWarning, recordDiagnosticPhaseCompleted, recordRunStarted, recordRunCompleted, recordHarnessRunStarted, recordHarnessRunCompleted, recordHarnessRunError, recordContextAssembled, recordModelCallStarted, recordModelCallCompleted, recordModelCallError, recordToolExecutionStarted, recordToolExecutionCompleted, recordToolExecutionError, recordToolExecutionBlocked, recordSkillUsed, recordExecProcessCompleted, recordToolLoop, recordMemorySample, recordMemoryPressure, recordAsyncQueueDropped, recordTelemetryExporter, recordPayloadLarge, recordModelFailover } = recorders;
446
+ const { recordGcDuration, recordGatewayEventLoopSample, recordGatewayRpc, recordModelUsage, recordWebhookReceived, recordWebhookProcessed, recordWebhookError, recordMessageQueued, recordMessageReceived, recordMessageDispatchStarted, recordMessageDispatchCompleted, recordMessageProcessed, recordMessageDeliveryStarted, recordMessageDeliveryCompleted, recordMessageDeliveryError, recordTalkEvent, recordLaneEnqueue, recordLaneDequeue, recordSessionState, recordSessionTurnCreated, recordSessionStuck, recordSessionRecoveryRequested, recordSessionRecoveryCompleted, recordRunAttempt, recordHeartbeat, recordLivenessWarning, recordDiagnosticPhaseCompleted, recordRunStarted, recordRunCompleted, recordHarnessRunStarted, recordHarnessRunCompleted, recordHarnessRunError, recordContextAssembled, recordModelCallStarted, recordModelCallFinished, recordToolExecutionStarted, recordToolExecutionFinished, recordToolExecutionBlocked, recordSkillUsed, recordExecProcessCompleted, recordToolLoop, recordMemorySample, recordMemoryPressure, recordAsyncQueueDropped, recordTelemetryExporter, recordPayloadLarge, recordModelFailover } = recorders;
447
447
  return (evt, metadata, privateData) => {
448
448
  try {
449
449
  switch (evt.type) {
450
+ case "diagnostic.gc":
451
+ recordGcDuration(evt, metadata);
452
+ return;
453
+ case "gateway.event_loop.sample":
454
+ recordGatewayEventLoopSample(evt, metadata);
455
+ return;
456
+ case "gateway.rpc":
457
+ recordGatewayRpc(evt, metadata);
458
+ return;
450
459
  case "model.usage":
451
460
  recordModelUsage(evt, metadata, privateData.hostPluginId);
452
461
  return;
@@ -545,19 +554,15 @@ function createDiagnosticsEventHandler(params) {
545
554
  recordModelCallStarted(evt, metadata);
546
555
  return;
547
556
  case "model.call.completed":
548
- recordModelCallCompleted(evt, metadata, privateData.modelContent);
549
- return;
550
557
  case "model.call.error":
551
- recordModelCallError(evt, metadata, privateData.modelContent);
558
+ recordModelCallFinished(evt, metadata, privateData.modelContent);
552
559
  return;
553
560
  case "tool.execution.started":
554
561
  recordToolExecutionStarted(evt, metadata);
555
562
  return;
556
563
  case "tool.execution.completed":
557
- recordToolExecutionCompleted(evt, metadata, privateData.toolContent);
558
- return;
559
564
  case "tool.execution.error":
560
- recordToolExecutionError(evt, metadata, privateData.toolContent);
565
+ recordToolExecutionFinished(evt, metadata, privateData.toolContent);
561
566
  return;
562
567
  case "tool.execution.blocked":
563
568
  recordToolExecutionBlocked(evt, metadata);
@@ -1056,6 +1061,48 @@ function createDiagnosticsMetrics(meter, metricNamePrefix = DEFAULT_METRIC_NAME_
1056
1061
  const createCounter = (name, options) => meter.createCounter(resolveMetricName(name), options);
1057
1062
  const createHistogram = (name, options) => meter.createHistogram(resolveMetricName(name), options);
1058
1063
  return {
1064
+ gcDurationHistogram: createHistogram("openclaw.gc.duration_ms", {
1065
+ unit: "ms",
1066
+ description: "Elapsed garbage collection duration for the hosting JavaScript isolate",
1067
+ advice: { explicitBucketBoundaries: AGENT_DURATION_MS_BUCKETS }
1068
+ }),
1069
+ gatewayEventLoopDelayMaxHistogram: createHistogram("openclaw.gateway.event_loop.delay_max_ms", {
1070
+ unit: "ms",
1071
+ description: "Maximum event-loop delay per completed Gateway observation window",
1072
+ advice: { explicitBucketBoundaries: AGENT_DURATION_MS_BUCKETS }
1073
+ }),
1074
+ gatewayEventLoopObservedCounter: createCounter("openclaw.gateway.event_loop.observed_ms", {
1075
+ unit: "ms",
1076
+ description: "Elapsed time covered by completed Gateway event-loop observation windows"
1077
+ }),
1078
+ gatewayRpcRequestsCounter: createCounter("openclaw.gateway.rpc.requests", {
1079
+ unit: "1",
1080
+ description: "Authenticated Gateway WebSocket requests received"
1081
+ }),
1082
+ gatewayRpcOutcomesCounter: createCounter("openclaw.gateway.rpc.outcomes", {
1083
+ unit: "1",
1084
+ description: "Gateway RPC observations by phase and outcome"
1085
+ }),
1086
+ gatewayRpcFirstResponseHistogram: createHistogram("openclaw.gateway.rpc.first_response_ms", {
1087
+ unit: "ms",
1088
+ description: "Elapsed time until the first Gateway RPC response is sent",
1089
+ advice: { explicitBucketBoundaries: AGENT_DURATION_MS_BUCKETS }
1090
+ }),
1091
+ gatewayRpcHandlerHistogram: createHistogram("openclaw.gateway.rpc.handler_ms", {
1092
+ unit: "ms",
1093
+ description: "Gateway RPC handler duration until return or throw",
1094
+ advice: { explicitBucketBoundaries: AGENT_DURATION_MS_BUCKETS }
1095
+ }),
1096
+ gatewayRpcAdmissionHistogram: createHistogram("openclaw.gateway.rpc.admission_ms", {
1097
+ unit: "ms",
1098
+ description: "Elapsed time from Gateway RPC receipt until handler invocation",
1099
+ advice: { explicitBucketBoundaries: AGENT_DURATION_MS_BUCKETS }
1100
+ }),
1101
+ gatewayRpcQueueWaitHistogram: createHistogram("openclaw.gateway.rpc.queue_wait_ms", {
1102
+ unit: "ms",
1103
+ description: "Gateway operator request start queue wait",
1104
+ advice: { explicitBucketBoundaries: AGENT_DURATION_MS_BUCKETS }
1105
+ }),
1059
1106
  tokensCounter: createCounter("openclaw.tokens", {
1060
1107
  unit: "1",
1061
1108
  description: "Token usage by type"
@@ -1291,7 +1338,7 @@ function createDiagnosticsMetrics(meter, metricNamePrefix = DEFAULT_METRIC_NAME_
1291
1338
  }),
1292
1339
  livenessCpuCoreRatioHistogram: createHistogram("openclaw.liveness.cpu_core_ratio", {
1293
1340
  unit: "1",
1294
- description: "CPU core ratio reported by diagnostic liveness warnings"
1341
+ description: "Whole-process CPU usage in core equivalents, including worker and native threads; can exceed 1."
1295
1342
  }),
1296
1343
  telemetryExporterCounter: createCounter("openclaw.telemetry.exporter.events", {
1297
1344
  unit: "1",
@@ -1868,15 +1915,6 @@ function createModelRecorders(runtime) {
1868
1915
  "openclaw.transport": normalizeDiagnosticValue(evt.transport),
1869
1916
  "openclaw.model_call.observation_unit": modelCallObservationUnit(evt)
1870
1917
  });
1871
- const genAiModelCallMetricAttrs = (evt, errorType) => ({
1872
- "gen_ai.operation.name": genAiOperationName(evt.api, evt.observationUnit),
1873
- "gen_ai.provider.name": normalizeDiagnosticValue(evt.provider),
1874
- "gen_ai.request.model": normalizeDiagnosticValue(evt.model),
1875
- ...errorType ? { "error.type": errorType } : {}
1876
- });
1877
- const recordGenAiModelCallDuration = (evt, errorType) => {
1878
- genAiOperationDurationHistogram.record(evt.durationMs / 1e3, genAiModelCallMetricAttrs(evt, errorType));
1879
- };
1880
1918
  const recordModelCallSizeTimingMetrics = (evt, attrs) => {
1881
1919
  const requestPayloadBytes = positiveFiniteNumber(evt.requestPayloadBytes);
1882
1920
  if (requestPayloadBytes !== void 0) modelCallRequestBytesHistogram.record(requestPayloadBytes, attrs);
@@ -1903,50 +1941,31 @@ function createModelRecorders(runtime) {
1903
1941
  startTimeMs: evt.ts
1904
1942
  })).spanContext();
1905
1943
  };
1906
- const recordModelCallCompleted = (evt, metadata, modelContent) => {
1907
- const metricAttrs = modelCallMetricAttrs(evt);
1908
- modelCallDurationHistogram.record(evt.durationMs, metricAttrs);
1909
- recordModelCallSizeTimingMetrics(evt, metricAttrs);
1910
- recordGenAiModelCallDuration(evt);
1911
- if (!tracesEnabled) return;
1912
- const spanAttrs = {
1913
- "openclaw.provider": evt.provider,
1914
- "openclaw.model": evt.model
1915
- };
1916
- assignGenAiModelCallAttrs(spanAttrs, evt);
1917
- if (evt.api) spanAttrs["openclaw.api"] = evt.api;
1918
- if (evt.transport) spanAttrs["openclaw.transport"] = evt.transport;
1919
- assignModelCallSizeTimingAttrs(spanAttrs, evt);
1920
- assignModelCallPromptStatsAttrs(spanAttrs, evt);
1921
- assignModelCallUsageAttrs(spanAttrs, evt);
1922
- assignOtelModelContentAttributes(spanAttrs, modelContent, contentCapturePolicy);
1923
- const span = takeTrackedTrustedSpan(evt, metadata) ?? spanWithDuration(modelCallSpanName(evt), spanAttrs, evt.durationMs, {
1924
- kind: modelCallSpanKind(),
1925
- parentContext: activeTrustedParentContext(evt, metadata),
1926
- endTimeMs: evt.ts
1927
- });
1928
- setSpanAttrs(span, spanAttrs);
1929
- addUpstreamRequestIdSpanEvent(span, evt.upstreamRequestIdHash);
1930
- span.end(evt.ts);
1931
- };
1932
- const recordModelCallError = (evt, metadata, modelContent) => {
1933
- const errorType = normalizeDiagnosticValue(evt.errorCategory, "other");
1944
+ const recordModelCallFinished = (evt, metadata, modelContent) => {
1945
+ const errorType = evt.type === "model.call.error" ? normalizeDiagnosticValue(evt.errorCategory, "other") : void 0;
1934
1946
  const metricAttrs = {
1935
1947
  ...modelCallMetricAttrs(evt),
1936
- "openclaw.errorCategory": errorType,
1937
- ...evt.failureKind ? { "openclaw.failureKind": normalizeDiagnosticValue(evt.failureKind, "other") } : {}
1948
+ ...errorType !== void 0 ? { "openclaw.errorCategory": errorType } : {},
1949
+ ...evt.type === "model.call.error" && evt.failureKind ? { "openclaw.failureKind": normalizeDiagnosticValue(evt.failureKind, "other") } : {}
1938
1950
  };
1939
1951
  modelCallDurationHistogram.record(evt.durationMs, metricAttrs);
1940
1952
  recordModelCallSizeTimingMetrics(evt, metricAttrs);
1941
- recordGenAiModelCallDuration(evt, errorType);
1953
+ genAiOperationDurationHistogram.record(evt.durationMs / 1e3, {
1954
+ "gen_ai.operation.name": genAiOperationName(evt.api, evt.observationUnit),
1955
+ "gen_ai.provider.name": normalizeDiagnosticValue(evt.provider),
1956
+ "gen_ai.request.model": normalizeDiagnosticValue(evt.model),
1957
+ ...errorType ? { "error.type": errorType } : {}
1958
+ });
1942
1959
  if (!tracesEnabled) return;
1943
1960
  const spanAttrs = {
1944
1961
  "openclaw.provider": evt.provider,
1945
1962
  "openclaw.model": evt.model,
1946
- "openclaw.errorCategory": errorType,
1947
- "error.type": errorType
1963
+ ...errorType !== void 0 ? {
1964
+ "openclaw.errorCategory": errorType,
1965
+ "error.type": errorType
1966
+ } : {}
1948
1967
  };
1949
- if (evt.failureKind) spanAttrs["openclaw.failureKind"] = normalizeDiagnosticValue(evt.failureKind, "other");
1968
+ if (evt.type === "model.call.error" && evt.failureKind) spanAttrs["openclaw.failureKind"] = normalizeDiagnosticValue(evt.failureKind, "other");
1950
1969
  assignGenAiModelCallAttrs(spanAttrs, evt);
1951
1970
  if (evt.api) spanAttrs["openclaw.api"] = evt.api;
1952
1971
  if (evt.transport) spanAttrs["openclaw.transport"] = evt.transport;
@@ -1961,23 +1980,56 @@ function createModelRecorders(runtime) {
1961
1980
  });
1962
1981
  setSpanAttrs(span, spanAttrs);
1963
1982
  addUpstreamRequestIdSpanEvent(span, evt.upstreamRequestIdHash);
1964
- span.setStatus({
1983
+ if (evt.type === "model.call.error") span.setStatus({
1965
1984
  code: SpanStatusCode.ERROR,
1966
1985
  message: redactSensitiveText(evt.errorCategory)
1967
1986
  });
1968
1987
  span.end(evt.ts);
1969
1988
  };
1970
1989
  return {
1971
- recordModelCallSizeTimingMetrics,
1972
1990
  recordModelCallStarted,
1973
- recordModelCallCompleted,
1974
- recordModelCallError
1991
+ recordModelCallFinished
1975
1992
  };
1976
1993
  }
1977
1994
  //#endregion
1978
1995
  //#region extensions/diagnostics-otel/src/service-recorders-operations.ts
1979
1996
  function createOperationsRecorders(runtime) {
1980
- const { durationHistogram, queueDepthHistogram, queueWaitHistogram, laneEnqueueCounter, laneDequeueCounter, sessionStateCounter, sessionTurnCreatedCounter, sessionStuckCounter, sessionStuckAgeHistogram, sessionRecoveryRequestedCounter, sessionRecoveryCompletedCounter, sessionRecoveryAgeHistogram, talkEventCounter, talkEventDurationHistogram, talkAudioBytesHistogram, runAttemptCounter, toolLoopCounter, memoryRssHistogram, memoryHeapUsedHistogram, memoryHeapTotalHistogram, memoryExternalHistogram, memoryArrayBuffersHistogram, memoryPressureCounter, asyncQueueDroppedCounter, tracer, activeTrustedSpans, spanWithDuration, trustedTraceContext, activeTrustedParentContext, setSpanAttrs, completeTrackedLifecycleSpan, addRunAttrs, tracesEnabled } = runtime;
1997
+ const { durationHistogram, gatewayRpcRequestsCounter, gatewayRpcOutcomesCounter, gatewayRpcFirstResponseHistogram, gatewayRpcHandlerHistogram, gatewayRpcAdmissionHistogram, gatewayRpcQueueWaitHistogram, queueDepthHistogram, queueWaitHistogram, laneEnqueueCounter, laneDequeueCounter, sessionStateCounter, sessionTurnCreatedCounter, sessionStuckCounter, sessionStuckAgeHistogram, sessionRecoveryRequestedCounter, sessionRecoveryCompletedCounter, sessionRecoveryAgeHistogram, talkEventCounter, talkEventDurationHistogram, talkAudioBytesHistogram, runAttemptCounter, toolLoopCounter, memoryRssHistogram, memoryHeapUsedHistogram, memoryHeapTotalHistogram, memoryExternalHistogram, memoryArrayBuffersHistogram, memoryPressureCounter, asyncQueueDroppedCounter, tracer, activeTrustedSpans, spanWithDuration, trustedTraceContext, activeTrustedParentContext, internalOrTrustedExplicitParentContext, setSpanAttrs, completeTrackedLifecycleSpan, addRunAttrs, tracesEnabled } = runtime;
1998
+ const recordGatewayRpc = (evt, metadata) => {
1999
+ if (!metadata.trusted) return;
2000
+ const attrs = { "openclaw.gateway.rpc.method": evt.method };
2001
+ if (evt.phase === "received") {
2002
+ gatewayRpcRequestsCounter.add(1, attrs);
2003
+ return;
2004
+ }
2005
+ const outcomeAttrs = {
2006
+ "openclaw.gateway.rpc.phase": evt.phase,
2007
+ "openclaw.gateway.rpc.outcome": evt.outcome
2008
+ };
2009
+ gatewayRpcOutcomesCounter.add(1, outcomeAttrs);
2010
+ switch (evt.phase) {
2011
+ case "response":
2012
+ if (evt.outcome === "ok" || evt.outcome === "error") gatewayRpcFirstResponseHistogram.record(evt.durationMs, attrs);
2013
+ break;
2014
+ case "handler":
2015
+ gatewayRpcHandlerHistogram.record(evt.durationMs, attrs);
2016
+ gatewayRpcAdmissionHistogram.record(evt.admissionMs, attrs);
2017
+ break;
2018
+ case "dispatch": if (evt.queueWaitMs !== void 0) gatewayRpcQueueWaitHistogram.record(evt.queueWaitMs, attrs);
2019
+ }
2020
+ if (!tracesEnabled) return;
2021
+ const span = spanWithDuration(`openclaw.gateway.rpc.${evt.phase}`, {
2022
+ ...attrs,
2023
+ ...outcomeAttrs,
2024
+ ...evt.phase === "handler" ? { "openclaw.gateway.rpc.admission_ms": evt.admissionMs } : {},
2025
+ ...evt.phase === "dispatch" ? { "openclaw.gateway.rpc.response": evt.response } : {}
2026
+ }, evt.durationMs, {
2027
+ endTimeMs: evt.ts,
2028
+ parentContext: internalOrTrustedExplicitParentContext(evt, metadata) ?? ROOT_CONTEXT
2029
+ });
2030
+ if (evt.outcome === "error" || evt.outcome === "threw") span.setStatus({ code: SpanStatusCode.ERROR });
2031
+ span.end(evt.ts);
2032
+ };
1981
2033
  const recordLaneEnqueue = (evt) => {
1982
2034
  const attrs = { "openclaw.lane": normalizeDiagnosticLane(evt.lane) };
1983
2035
  laneEnqueueCounter.add(1, attrs);
@@ -2151,6 +2203,7 @@ function createOperationsRecorders(runtime) {
2151
2203
  span.end(evt.ts);
2152
2204
  };
2153
2205
  return {
2206
+ recordGatewayRpc,
2154
2207
  recordLaneEnqueue,
2155
2208
  recordLaneDequeue,
2156
2209
  recordSessionState,
@@ -2171,7 +2224,7 @@ function createOperationsRecorders(runtime) {
2171
2224
  //#endregion
2172
2225
  //#region extensions/diagnostics-otel/src/service-recorders-tools.ts
2173
2226
  function createToolAndSystemRecorders(runtime) {
2174
- const { queueDepthHistogram, skillUsedCounter, toolExecutionDurationHistogram, toolExecutionBlockedCounter, execProcessDurationHistogram, payloadLargeCounter, payloadLargeBytesHistogram, livenessWarningCounter, livenessEventLoopDelayP99Histogram, livenessEventLoopDelayMaxHistogram, livenessEventLoopUtilizationHistogram, livenessCpuCoreRatioHistogram, telemetryExporterCounter, spanWithDuration, activeTrustedParentContext, exportedInternalOrTrustedContext, trackTrustedSpan, getTrackedInternalOrTrustedSpan, takeTrackedTrustedSpan, setSpanAttrs, addRunAttrs, paramsSummaryAttrs, contentCapturePolicy, tracesEnabled } = runtime;
2227
+ const { gcDurationHistogram, gatewayEventLoopDelayMaxHistogram, gatewayEventLoopObservedCounter, queueDepthHistogram, skillUsedCounter, toolExecutionDurationHistogram, toolExecutionBlockedCounter, execProcessDurationHistogram, payloadLargeCounter, payloadLargeBytesHistogram, livenessWarningCounter, livenessEventLoopDelayP99Histogram, livenessEventLoopDelayMaxHistogram, livenessEventLoopUtilizationHistogram, livenessCpuCoreRatioHistogram, telemetryExporterCounter, spanWithDuration, activeTrustedParentContext, exportedInternalOrTrustedContext, trackTrustedSpan, getTrackedInternalOrTrustedSpan, takeTrackedTrustedSpan, setSpanAttrs, addRunAttrs, paramsSummaryAttrs, contentCapturePolicy, tracesEnabled } = runtime;
2175
2228
  const toolExecutionBaseAttrs = (evt) => ({
2176
2229
  "openclaw.toolName": evt.toolName,
2177
2230
  "openclaw.tool.source": normalizeDiagnosticValue(evt.toolSource, "core"),
@@ -2212,39 +2265,22 @@ function createToolAndSystemRecorders(runtime) {
2212
2265
  startTimeMs: toolTimestampMs(evt)
2213
2266
  })).spanContext();
2214
2267
  };
2215
- const recordToolExecutionCompleted = (evt, metadata, toolContent) => {
2268
+ const recordToolExecutionFinished = (evt, metadata, toolContent) => {
2216
2269
  const attrs = toolExecutionBaseAttrs(evt);
2270
+ if (evt.type === "tool.execution.error") attrs["openclaw.errorCategory"] = normalizeDiagnosticValue(evt.errorCategory, "other");
2217
2271
  toolExecutionDurationHistogram.record(evt.durationMs, attrs);
2218
2272
  if (!tracesEnabled) return;
2219
2273
  const spanAttrs = { ...attrs };
2220
2274
  addRunAttrs(spanAttrs, evt);
2221
2275
  assignOtelToolIdentityAttributes(spanAttrs, evt);
2276
+ if (evt.type === "tool.execution.error" && evt.errorCode) spanAttrs["openclaw.errorCode"] = normalizeDiagnosticValue(evt.errorCode, "other");
2222
2277
  assignOtelToolContentAttributes(spanAttrs, toolContent, contentCapturePolicy);
2223
2278
  const span = takeTrackedTrustedSpan(evt, metadata) ?? spanWithDuration("openclaw.tool.execution", spanAttrs, evt.durationMs, {
2224
2279
  parentContext: activeTrustedParentContext(evt, metadata),
2225
2280
  endTimeMs: toolTimestampMs(evt)
2226
2281
  });
2227
2282
  setSpanAttrs(span, spanAttrs);
2228
- span.end(toolTimestampMs(evt));
2229
- };
2230
- const recordToolExecutionError = (evt, metadata, toolContent) => {
2231
- const attrs = {
2232
- ...toolExecutionBaseAttrs(evt),
2233
- "openclaw.errorCategory": normalizeDiagnosticValue(evt.errorCategory, "other")
2234
- };
2235
- toolExecutionDurationHistogram.record(evt.durationMs, attrs);
2236
- if (!tracesEnabled) return;
2237
- const spanAttrs = { ...attrs };
2238
- addRunAttrs(spanAttrs, evt);
2239
- assignOtelToolIdentityAttributes(spanAttrs, evt);
2240
- if (evt.errorCode) spanAttrs["openclaw.errorCode"] = normalizeDiagnosticValue(evt.errorCode, "other");
2241
- assignOtelToolContentAttributes(spanAttrs, toolContent, contentCapturePolicy);
2242
- const span = takeTrackedTrustedSpan(evt, metadata) ?? spanWithDuration("openclaw.tool.execution", spanAttrs, evt.durationMs, {
2243
- parentContext: activeTrustedParentContext(evt, metadata),
2244
- endTimeMs: toolTimestampMs(evt)
2245
- });
2246
- setSpanAttrs(span, spanAttrs);
2247
- span.setStatus({
2283
+ if (evt.type === "tool.execution.error") span.setStatus({
2248
2284
  code: SpanStatusCode.ERROR,
2249
2285
  message: redactSensitiveText(evt.errorCategory)
2250
2286
  });
@@ -2308,6 +2344,15 @@ function createToolAndSystemRecorders(runtime) {
2308
2344
  });
2309
2345
  span.end(evt.ts);
2310
2346
  };
2347
+ const recordGcDuration = (evt, metadata) => {
2348
+ if (!metadata.trusted && !isInternalDiagnosticEventMetadata(metadata)) return;
2349
+ gcDurationHistogram.record(evt.durationMs, void 0, ROOT_CONTEXT);
2350
+ };
2351
+ const recordGatewayEventLoopSample = (evt, metadata) => {
2352
+ if (!metadata.trusted && !isInternalDiagnosticEventMetadata(metadata)) return;
2353
+ gatewayEventLoopDelayMaxHistogram.record(evt.delayMaxMs, void 0, ROOT_CONTEXT);
2354
+ gatewayEventLoopObservedCounter.add(evt.intervalMs, void 0, ROOT_CONTEXT);
2355
+ };
2311
2356
  const recordHeartbeat = (evt) => {
2312
2357
  queueDepthHistogram.record(evt.queued, { "openclaw.channel": "heartbeat" });
2313
2358
  };
@@ -2365,10 +2410,11 @@ function createToolAndSystemRecorders(runtime) {
2365
2410
  });
2366
2411
  };
2367
2412
  return {
2413
+ recordGcDuration,
2414
+ recordGatewayEventLoopSample,
2368
2415
  recordSkillUsed,
2369
2416
  recordToolExecutionStarted,
2370
- recordToolExecutionCompleted,
2371
- recordToolExecutionError,
2417
+ recordToolExecutionFinished,
2372
2418
  recordToolExecutionBlocked,
2373
2419
  recordPayloadLarge,
2374
2420
  recordExecProcessCompleted,
@@ -2910,56 +2956,34 @@ function diagnosticTraceContextFromSpanContext(spanContext) {
2910
2956
  };
2911
2957
  }
2912
2958
  function createDiagnosticsOtelService() {
2913
- let traceProvider = null;
2914
- let meterProvider = null;
2915
- let logProvider = null;
2916
- let unsubscribe = null;
2917
- let unregisterTracePropagationBridge = null;
2918
- let stopActiveTrustedSpans = null;
2919
- let unregisterOwnedSdkRuntime = null;
2920
- let unregisterUnhandledRejectionHandler = null;
2921
- let retireExporterRoutes = null;
2959
+ let state = {};
2922
2960
  let preserveExporterRoutesOnNextStop = false;
2923
2961
  const stopStarted = async (options) => {
2924
- const currentUnsubscribe = unsubscribe;
2925
- const currentUnregisterTracePropagationBridge = unregisterTracePropagationBridge;
2926
- const currentLogProvider = logProvider;
2927
- const currentTraceProvider = traceProvider;
2928
- const currentMeterProvider = meterProvider;
2929
- const currentStopActiveTrustedSpans = stopActiveTrustedSpans;
2930
- const currentUnregisterOwnedSdkRuntime = unregisterOwnedSdkRuntime;
2931
- const currentUnregisterUnhandledRejectionHandler = unregisterUnhandledRejectionHandler;
2932
- const currentRetireExporterRoutes = retireExporterRoutes;
2933
- unsubscribe = null;
2934
- unregisterTracePropagationBridge = null;
2935
- logProvider = null;
2936
- traceProvider = null;
2937
- meterProvider = null;
2938
- stopActiveTrustedSpans = null;
2939
- unregisterOwnedSdkRuntime = null;
2940
- unregisterUnhandledRejectionHandler = null;
2941
- retireExporterRoutes = options?.preserveExporterRoutes ? currentRetireExporterRoutes : null;
2962
+ const current = state;
2963
+ state = options?.preserveExporterRoutes ? { retireExporterRoutes: current.retireExporterRoutes } : {};
2942
2964
  const settle = async (...stops) => (await Promise.allSettled(stops.map((stop) => Promise.resolve().then(() => stop?.())))).flatMap((result) => result.status === "rejected" ? [result.reason] : []);
2943
- const failures = await settle(currentUnregisterTracePropagationBridge, currentUnsubscribe, currentStopActiveTrustedSpans, currentUnregisterOwnedSdkRuntime);
2944
- const providerFailures = await settle(currentLogProvider ? () => currentLogProvider.shutdown() : null, currentTraceProvider ? () => currentTraceProvider.shutdown() : null, currentMeterProvider ? () => currentMeterProvider.shutdown() : null);
2965
+ const failures = await settle(current.unregisterTracePropagationBridge, current.unsubscribe, current.stopActiveTrustedSpans, current.unregisterOwnedSdkRuntime);
2966
+ const providerFailures = await settle(() => current.logProvider?.shutdown(), () => current.traceProvider?.shutdown(), () => current.meterProvider?.shutdown());
2945
2967
  failures.push(...providerFailures);
2946
- if (!options?.preserveExporterRoutes) currentRetireExporterRoutes?.(providerFailures.length > 0);
2947
- if (providerFailures.length > 0) retireExporterRoutes = currentRetireExporterRoutes;
2948
- failures.push(...await settle(currentUnregisterUnhandledRejectionHandler));
2968
+ if (!options?.preserveExporterRoutes) current.retireExporterRoutes?.(providerFailures.length > 0);
2969
+ if (providerFailures.length > 0) state.retireExporterRoutes = current.retireExporterRoutes;
2970
+ failures.push(...await settle(current.unregisterUnhandledRejectionHandler));
2949
2971
  if (failures.length === 1) throw failures[0];
2950
2972
  if (failures.length > 1) throw new AggregateError(failures, `diagnostics-otel shutdown failed: ${failures.join("; ")}`);
2951
2973
  };
2952
2974
  return {
2953
2975
  id: "diagnostics-otel",
2976
+ reload: { configPrefixes: ["diagnostics.otel", "diagnostics.enabled"] },
2954
2977
  async start(ctx) {
2955
2978
  preserveExporterRoutesOnNextStop = false;
2956
2979
  await stopStarted();
2980
+ const active = state;
2957
2981
  const cfg = ctx.config.diagnostics;
2958
2982
  const otel = cfg?.otel;
2959
2983
  if (!cfg || cfg.enabled === false || !otel?.enabled) return;
2960
2984
  const sdkDisabled = isOtelSdkDisabled(ctx.logger);
2961
2985
  const sdkPreloaded = hasPreloadedOtelSdk();
2962
- if (!sdkPreloaded) unregisterOwnedSdkRuntime = registerOwnedSdkRuntime((message) => ctx.logger.warn(message));
2986
+ if (!sdkPreloaded) active.unregisterOwnedSdkRuntime = registerOwnedSdkRuntime((message) => ctx.logger.warn(message));
2963
2987
  if (!sdkPreloaded && sdkDisabled) return;
2964
2988
  const exporterRoutes = /* @__PURE__ */ new Map();
2965
2989
  const internalDiagnostics = ctx.internalDiagnostics;
@@ -3004,7 +3028,7 @@ function createDiagnosticsOtelService() {
3004
3028
  ctx.logger.error("diagnostics-otel: internal diagnostics capability unavailable");
3005
3029
  return;
3006
3030
  }
3007
- retireExporterRoutes = (preserveFailures = false) => {
3031
+ active.retireExporterRoutes = (preserveFailures = false) => {
3008
3032
  for (const route of exporterRoutes.values()) {
3009
3033
  if (preserveFailures && route.status === "failure") continue;
3010
3034
  emitExporterEvent({
@@ -3106,7 +3130,7 @@ function createDiagnosticsOtelService() {
3106
3130
  exportIntervalMillis: metricInterval,
3107
3131
  exportTimeoutMillis: metricTimeout
3108
3132
  }) : void 0;
3109
- if (metricReader) meterProvider = new MeterProvider({
3133
+ if (metricReader) active.meterProvider = new MeterProvider({
3110
3134
  resource: detectedResource,
3111
3135
  readers: [metricReader],
3112
3136
  sdkMetricsEnabled
@@ -3126,13 +3150,13 @@ function createDiagnosticsOtelService() {
3126
3150
  maxExportBatchSize: Math.min(readPositiveOtelNumber("OTEL_BSP_MAX_EXPORT_BATCH_SIZE", 512), maxQueueSize),
3127
3151
  scheduledDelayMillis: typeof otel.flushIntervalMs === "number" ? Math.max(1e3, otel.flushIntervalMs) : readPositiveOtelNumber("OTEL_BSP_SCHEDULE_DELAY", 5e3),
3128
3152
  exportTimeoutMillis: readPositiveOtelNumber("OTEL_BSP_EXPORT_TIMEOUT", 3e4),
3129
- ...sdkMetricsEnabled && meterProvider ? { selfObsMeterProvider: meterProvider } : {}
3153
+ ...sdkMetricsEnabled && active.meterProvider ? { selfObsMeterProvider: active.meterProvider } : {}
3130
3154
  });
3131
- traceProvider = new BasicTracerProvider({
3155
+ active.traceProvider = new BasicTracerProvider({
3132
3156
  resource: detectedResource,
3133
3157
  spanProcessors: [spanProcessor],
3134
3158
  ...sampleRate !== void 0 ? { sampler: new ParentBasedSampler({ root: new TraceIdRatioBasedSampler(sampleRate) }) } : {},
3135
- ...sdkMetricsEnabled && meterProvider ? { meterProvider } : {}
3159
+ ...sdkMetricsEnabled && active.meterProvider ? { meterProvider: active.meterProvider } : {}
3136
3160
  });
3137
3161
  }
3138
3162
  } catch (err) {
@@ -3156,9 +3180,9 @@ function createDiagnosticsOtelService() {
3156
3180
  throw err;
3157
3181
  }
3158
3182
  else if (sdkPreloaded && (tracesEnabled || metricsEnabled)) ctx.logger.info("diagnostics-otel: using preloaded OpenTelemetry SDK");
3159
- const meter = meterProvider ? meterProvider.getMeter("openclaw") : metrics.getMeter("openclaw");
3160
- const diagnosticsTrace = createDiagnosticsTraceRuntime(traceProvider ? traceProvider.getTracer("openclaw") : trace.getTracer("openclaw"));
3161
- stopActiveTrustedSpans = diagnosticsTrace.stopActiveTrustedSpans;
3183
+ const meter = !metricsActive ? createNoopMeter() : active.meterProvider ? active.meterProvider.getMeter("openclaw") : metrics.getMeter("openclaw");
3184
+ const diagnosticsTrace = createDiagnosticsTraceRuntime(active.traceProvider ? active.traceProvider.getTracer("openclaw") : trace.getTracer("openclaw"));
3185
+ active.stopActiveTrustedSpans = diagnosticsTrace.stopActiveTrustedSpans;
3162
3186
  const diagnosticMetrics = createDiagnosticsMetrics(meter, otel.metricNamePrefix);
3163
3187
  const diagnosticsLogs = createDiagnosticsLogExporter({
3164
3188
  contentCapturePolicy,
@@ -3174,7 +3198,7 @@ function createDiagnosticsOtelService() {
3174
3198
  resource,
3175
3199
  serviceName
3176
3200
  });
3177
- logProvider = diagnosticsLogs.logProvider;
3201
+ active.logProvider = diagnosticsLogs.logProvider;
3178
3202
  const { recordLogRecord, recordSecurityEvent } = diagnosticsLogs;
3179
3203
  const recorderRuntime = createDiagnosticsRecorderRuntime({
3180
3204
  contentCapturePolicy,
@@ -3189,13 +3213,13 @@ function createDiagnosticsOtelService() {
3189
3213
  ...createModelRecorders(recorderRuntime),
3190
3214
  ...createToolAndSystemRecorders(recorderRuntime)
3191
3215
  };
3192
- unsubscribe = subscribe(createDiagnosticsEventHandler({
3216
+ active.unsubscribe = subscribe(createDiagnosticsEventHandler({
3193
3217
  logger: ctx.logger,
3194
3218
  recorders,
3195
3219
  recordLogRecord,
3196
3220
  recordSecurityEvent
3197
- }));
3198
- if (tracesActive) unregisterTracePropagationBridge = ctx.internalDiagnostics?.registerTracePropagationBridge?.({
3221
+ }), metricsActive ? void 0 : tracesActive ? { exclude: ["gateway.event_loop.sample", "diagnostic.gc"] } : { include: ["log.record", "security.event"] });
3222
+ if (tracesActive) active.unregisterTracePropagationBridge = ctx.internalDiagnostics?.registerTracePropagationBridge?.({
3199
3223
  shouldPrepareEvent(event) {
3200
3224
  return event.type === "model.call.started" || event.type === "tool.execution.started";
3201
3225
  },
@@ -3208,7 +3232,7 @@ function createDiagnosticsOtelService() {
3208
3232
  return spanContext ? diagnosticTraceContextFromSpanContext(spanContext) : void 0;
3209
3233
  }
3210
3234
  }) ?? null;
3211
- if (hasOwnedOtlpSignal || sdkPreloaded && !sdkDisabled) unregisterUnhandledRejectionHandler = registerUnhandledRejectionHandler((reason) => {
3235
+ if (hasOwnedOtlpSignal || sdkPreloaded && !sdkDisabled) active.unregisterUnhandledRejectionHandler = registerUnhandledRejectionHandler((reason) => {
3212
3236
  const otlpError = findOtlpExporterError(reason);
3213
3237
  if (!otlpError) return false;
3214
3238
  const code = readErrorCode(otlpError) ?? "unknown";
@@ -1,2 +1,2 @@
1
- import { t as createDiagnosticsOtelService } from "./runtime-api-DaBnxBEO.js";
1
+ import { t as createDiagnosticsOtelService } from "./runtime-api-B9-DLHbi.js";
2
2
  export { createDiagnosticsOtelService };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openclaw/diagnostics-otel",
3
- "version": "2026.9.1",
3
+ "version": "2026.9.3",
4
4
  "description": "OpenClaw diagnostics OpenTelemetry exporter for metrics, traces, and logs.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -34,10 +34,10 @@
34
34
  "minHostVersion": ">=2026.4.25"
35
35
  },
36
36
  "compat": {
37
- "pluginApi": ">=2026.9.1"
37
+ "pluginApi": ">=2026.9.3"
38
38
  },
39
39
  "build": {
40
- "openclawVersion": "2026.9.1"
40
+ "openclawVersion": "2026.9.3"
41
41
  },
42
42
  "release": {
43
43
  "publishToClawHub": true,
@@ -53,7 +53,7 @@
53
53
  "README.md"
54
54
  ],
55
55
  "peerDependencies": {
56
- "openclaw": ">=2026.9.1"
56
+ "openclaw": ">=2026.9.3"
57
57
  },
58
58
  "peerDependenciesMeta": {
59
59
  "openclaw": {