@opengeni/observability 0.5.0 → 0.5.6
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.d.ts +1 -1
- package/dist/index.js +192 -24
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/src/index.ts +237 -41
package/dist/index.d.ts
CHANGED
|
@@ -37,7 +37,7 @@ export declare const OPENGENI_OBSERVABILITY_DISTRIBUTION: {
|
|
|
37
37
|
export type SandboxOperationMetricObservation = {
|
|
38
38
|
backend: string;
|
|
39
39
|
op: string;
|
|
40
|
-
outcome: "ok" | "failed";
|
|
40
|
+
outcome: "ok" | "not_found" | "failed";
|
|
41
41
|
durationMs: number;
|
|
42
42
|
};
|
|
43
43
|
export declare function createObservability(settings: ObservabilitySettings, options: ObservabilityOptions): Observability;
|
package/dist/index.js
CHANGED
|
@@ -46,6 +46,145 @@ var SANDBOX_OPERATION_NAMES = /* @__PURE__ */ new Set([
|
|
|
46
46
|
"resolveExposedPort",
|
|
47
47
|
"serializeSessionState"
|
|
48
48
|
]);
|
|
49
|
+
var PUBLIC_TELEMETRY_ATTRIBUTE_KEYS = /* @__PURE__ */ new Set([
|
|
50
|
+
"http.request.method",
|
|
51
|
+
"http.response.status_code",
|
|
52
|
+
"opengeni.route",
|
|
53
|
+
"opengeni.duration_ms",
|
|
54
|
+
"opengeni.finalization_duration_ms",
|
|
55
|
+
"opengeni.trigger_kind",
|
|
56
|
+
"opengeni.status",
|
|
57
|
+
"error.type",
|
|
58
|
+
"error.status_code",
|
|
59
|
+
"method",
|
|
60
|
+
"route",
|
|
61
|
+
"status",
|
|
62
|
+
"durationMs",
|
|
63
|
+
"attempt",
|
|
64
|
+
"attempts",
|
|
65
|
+
"delayMs",
|
|
66
|
+
"provider",
|
|
67
|
+
"providerApi",
|
|
68
|
+
"model",
|
|
69
|
+
"inputTokens",
|
|
70
|
+
"outputTokens",
|
|
71
|
+
"cachedTokens",
|
|
72
|
+
"cacheWriteTokens",
|
|
73
|
+
"reasoningTokens",
|
|
74
|
+
"accountChangedFromPrevCall",
|
|
75
|
+
"rejectedFields",
|
|
76
|
+
"dependency",
|
|
77
|
+
"activity",
|
|
78
|
+
"backend",
|
|
79
|
+
"op",
|
|
80
|
+
"outcome",
|
|
81
|
+
"eventType",
|
|
82
|
+
"surface",
|
|
83
|
+
"reason",
|
|
84
|
+
"originalBytes",
|
|
85
|
+
"deliveredBytes",
|
|
86
|
+
"estimatedOriginalTokens",
|
|
87
|
+
"estimatedDeliveredTokens",
|
|
88
|
+
"fullEvidenceAvailable",
|
|
89
|
+
"retainedOutputKind"
|
|
90
|
+
]);
|
|
91
|
+
var PUBLIC_TELEMETRY_ERROR_CLASSES = /* @__PURE__ */ new Set([
|
|
92
|
+
"OperationError",
|
|
93
|
+
"CodexCheckpointOperationError",
|
|
94
|
+
"CodexFleetShadowOperationError",
|
|
95
|
+
"ComputerActionTimeoutError",
|
|
96
|
+
"ComputerUnavailableError",
|
|
97
|
+
"CredentialRenewalOperationError",
|
|
98
|
+
"CredentialReadOperationError",
|
|
99
|
+
"EventPublishOperationError",
|
|
100
|
+
"GitCredentialRenewalOperationError",
|
|
101
|
+
"HostExportOperationError",
|
|
102
|
+
"HttpOperationError",
|
|
103
|
+
"McpLifecycleError",
|
|
104
|
+
"McpOperationError",
|
|
105
|
+
"MemoryEmbeddingOperationError",
|
|
106
|
+
"MemorySearchOperationError",
|
|
107
|
+
"NatsAuthCalloutOperationError",
|
|
108
|
+
"OAuthOperationError",
|
|
109
|
+
"RunCredentialRenewalOperationError",
|
|
110
|
+
"RunStateCompatibilityError",
|
|
111
|
+
"SnapshotOperationError",
|
|
112
|
+
"StartupDependencyError",
|
|
113
|
+
"TelemetryExportError",
|
|
114
|
+
"ToolspaceOperationError",
|
|
115
|
+
"ToolspaceTokenRenewalOperationError",
|
|
116
|
+
"WorkerLifecycleOperation",
|
|
117
|
+
"WorkerLifecycleOperationError",
|
|
118
|
+
"WorkerOperationError",
|
|
119
|
+
"WorkflowWakeOperationError"
|
|
120
|
+
]);
|
|
121
|
+
var PUBLIC_TELEMETRY_ERROR_CODES = /* @__PURE__ */ new Set([
|
|
122
|
+
"agent_command_wake_failed",
|
|
123
|
+
"cleared_goal_live_publish_failed",
|
|
124
|
+
"codex_failover_checkpoint_failed",
|
|
125
|
+
"codex_fleet_shadow_failed",
|
|
126
|
+
"codex_lease_loss_checkpoint_failed",
|
|
127
|
+
"codex_active_credential_read_failed",
|
|
128
|
+
"command_yield_timeout",
|
|
129
|
+
"conflict",
|
|
130
|
+
"control_wake_dispatch_failed",
|
|
131
|
+
"fenced_event_live_publish_failed",
|
|
132
|
+
"forbidden",
|
|
133
|
+
"host_export_batch_delivery_failed",
|
|
134
|
+
"host_export_failure_settlement_stale",
|
|
135
|
+
"host_export_pump_iteration_failed",
|
|
136
|
+
"host_export_retention_prune_failed",
|
|
137
|
+
"idempotency_conflict",
|
|
138
|
+
"incompatible_exposed_ports",
|
|
139
|
+
"internal_error",
|
|
140
|
+
"limit_exceeded",
|
|
141
|
+
"mcp_close_failed",
|
|
142
|
+
"mcp_connect_failed",
|
|
143
|
+
"mcp_tool_call_failed",
|
|
144
|
+
"mcp_tools_list_failed",
|
|
145
|
+
"mcp_transport_failed",
|
|
146
|
+
"memory_edit_embedding_failed",
|
|
147
|
+
"memory_hybrid_vector_failed",
|
|
148
|
+
"memory_save_embedding_failed",
|
|
149
|
+
"nats_auth_callout_start_failed",
|
|
150
|
+
"nested_agent_depth_exceeded",
|
|
151
|
+
"nested_agent_depth_override_forbidden",
|
|
152
|
+
"not_found",
|
|
153
|
+
"oauth_operation_failed",
|
|
154
|
+
"otlp_export_failed",
|
|
155
|
+
"payment_required",
|
|
156
|
+
"provider_verification_failed",
|
|
157
|
+
"screenshot_capture_failed",
|
|
158
|
+
"session_event_live_publish_failed",
|
|
159
|
+
"session_workflow_wake_failed",
|
|
160
|
+
"snapshot_operation_failed",
|
|
161
|
+
"startup_dependency_retry",
|
|
162
|
+
"tool_list_too_large",
|
|
163
|
+
"tool_result_too_large",
|
|
164
|
+
"toolspace_operation_failed",
|
|
165
|
+
"unauthenticated",
|
|
166
|
+
"upstream_unavailable",
|
|
167
|
+
"validation_failed",
|
|
168
|
+
"worker_draining",
|
|
169
|
+
"worker_operation_failed",
|
|
170
|
+
"worker_shutdown_request_failed",
|
|
171
|
+
"workspace_control_live_publish_failed"
|
|
172
|
+
]);
|
|
173
|
+
var PUBLIC_TELEMETRY_ERROR_ORIGINS = /* @__PURE__ */ new Set([
|
|
174
|
+
"api",
|
|
175
|
+
"core",
|
|
176
|
+
"db",
|
|
177
|
+
"events",
|
|
178
|
+
"host-export",
|
|
179
|
+
"oauth",
|
|
180
|
+
"observability",
|
|
181
|
+
"runtime",
|
|
182
|
+
"sandbox-computer",
|
|
183
|
+
"sandbox-resume",
|
|
184
|
+
"toolspace",
|
|
185
|
+
"worker",
|
|
186
|
+
"worker-lifecycle"
|
|
187
|
+
]);
|
|
49
188
|
function createObservability(settings, options) {
|
|
50
189
|
return new Observability(settings, options);
|
|
51
190
|
}
|
|
@@ -99,14 +238,14 @@ var Observability = class {
|
|
|
99
238
|
this.log("error", message, attributes);
|
|
100
239
|
}
|
|
101
240
|
log(level, message, attributes = {}) {
|
|
241
|
+
const publicAttributes = projectPublicTelemetryAttributes(attributes);
|
|
102
242
|
if (!this.settings.observabilityStructuredLogs) {
|
|
103
|
-
const line = attributes.error ? `${message}: ${String(attributes.error)}` : message;
|
|
104
243
|
if (level === "warn") {
|
|
105
|
-
console.warn(
|
|
244
|
+
console.warn(message);
|
|
106
245
|
} else if (level === "error") {
|
|
107
|
-
console.error(
|
|
246
|
+
console.error(message);
|
|
108
247
|
} else {
|
|
109
|
-
console.log(
|
|
248
|
+
console.log(message);
|
|
110
249
|
}
|
|
111
250
|
return;
|
|
112
251
|
}
|
|
@@ -117,7 +256,7 @@ var Observability = class {
|
|
|
117
256
|
service: this.settings.serviceName,
|
|
118
257
|
environment: this.settings.environment,
|
|
119
258
|
component: this.options.component,
|
|
120
|
-
...cleanAttributes(
|
|
259
|
+
...cleanAttributes(publicAttributes)
|
|
121
260
|
};
|
|
122
261
|
const serialized = JSON.stringify(record);
|
|
123
262
|
if (level === "warn") {
|
|
@@ -141,8 +280,8 @@ var Observability = class {
|
|
|
141
280
|
return;
|
|
142
281
|
}
|
|
143
282
|
ended = true;
|
|
144
|
-
const
|
|
145
|
-
const errorAttributes =
|
|
283
|
+
const telemetryError = input.error !== void 0 && input.error !== null ? projectSpanErrorForTelemetry(input.error) : void 0;
|
|
284
|
+
const errorAttributes = telemetryError ? errorToAttributes(telemetryError) : {};
|
|
146
285
|
this.exportSpan({
|
|
147
286
|
traceId,
|
|
148
287
|
spanId,
|
|
@@ -150,11 +289,13 @@ var Observability = class {
|
|
|
150
289
|
startMs,
|
|
151
290
|
endMs: this.now(),
|
|
152
291
|
attributes: {
|
|
153
|
-
...
|
|
154
|
-
|
|
155
|
-
|
|
292
|
+
...projectPublicTelemetryAttributes({
|
|
293
|
+
...attributes,
|
|
294
|
+
...input.attributes,
|
|
295
|
+
...errorAttributes
|
|
296
|
+
})
|
|
156
297
|
},
|
|
157
|
-
...
|
|
298
|
+
...telemetryError ? { error: telemetryError } : {}
|
|
158
299
|
});
|
|
159
300
|
}
|
|
160
301
|
};
|
|
@@ -331,8 +472,12 @@ var Observability = class {
|
|
|
331
472
|
]
|
|
332
473
|
};
|
|
333
474
|
void this.exporter(endpoint, body, parseHeaders(this.settings.observabilityOtlpHeaders)).catch(
|
|
334
|
-
(
|
|
335
|
-
this.warn("OTLP span export failed", {
|
|
475
|
+
() => {
|
|
476
|
+
this.warn("OTLP span export failed", {
|
|
477
|
+
errorClass: "TelemetryExportError",
|
|
478
|
+
errorCode: "otlp_export_failed",
|
|
479
|
+
origin: "observability"
|
|
480
|
+
});
|
|
336
481
|
}
|
|
337
482
|
);
|
|
338
483
|
}
|
|
@@ -366,13 +511,14 @@ function sandboxOperationMetricObserver(observability) {
|
|
|
366
511
|
};
|
|
367
512
|
}
|
|
368
513
|
function logStartupDependencyRetry(observability, event) {
|
|
369
|
-
const message = event.error instanceof Error ? event.error.message : String(event.error);
|
|
370
514
|
observability.warn("Startup dependency connection failed; retrying", {
|
|
371
515
|
dependency: event.label,
|
|
372
516
|
attempt: event.attempt,
|
|
373
517
|
attempts: event.attempts,
|
|
374
518
|
delayMs: event.delayMs,
|
|
375
|
-
|
|
519
|
+
errorClass: "StartupDependencyError",
|
|
520
|
+
errorCode: "startup_dependency_retry",
|
|
521
|
+
origin: "observability"
|
|
376
522
|
});
|
|
377
523
|
}
|
|
378
524
|
function normalizeLabels(labels = {}) {
|
|
@@ -388,20 +534,45 @@ function cleanAttributes(attributes) {
|
|
|
388
534
|
Object.entries(attributes).filter(([, value]) => value !== void 0)
|
|
389
535
|
);
|
|
390
536
|
}
|
|
391
|
-
function
|
|
392
|
-
|
|
537
|
+
function projectPublicTelemetryAttributes(attributes) {
|
|
538
|
+
if ("errorClass" in attributes || "errorCode" in attributes) {
|
|
539
|
+
return projectPublicDiagnosticAttributes(attributes);
|
|
540
|
+
}
|
|
541
|
+
return Object.fromEntries(
|
|
542
|
+
Object.entries(attributes).filter(
|
|
543
|
+
([key, value]) => value !== void 0 && PUBLIC_TELEMETRY_ATTRIBUTE_KEYS.has(key)
|
|
544
|
+
)
|
|
545
|
+
);
|
|
546
|
+
}
|
|
547
|
+
function projectPublicDiagnosticAttributes(attributes) {
|
|
548
|
+
const errorClass = attributes.errorClass;
|
|
549
|
+
const errorCode = attributes.errorCode;
|
|
550
|
+
const status = attributes.status;
|
|
551
|
+
const origin = attributes.origin;
|
|
552
|
+
return {
|
|
553
|
+
errorClass: typeof errorClass === "string" && PUBLIC_TELEMETRY_ERROR_CLASSES.has(errorClass) ? errorClass : "OperationError",
|
|
554
|
+
...typeof errorCode === "string" && PUBLIC_TELEMETRY_ERROR_CODES.has(errorCode) ? { errorCode } : {},
|
|
555
|
+
...typeof status === "number" && Number.isInteger(status) && status >= 100 && status <= 599 ? { status } : {},
|
|
556
|
+
...typeof origin === "string" && PUBLIC_TELEMETRY_ERROR_ORIGINS.has(origin) ? { origin } : {}
|
|
557
|
+
};
|
|
558
|
+
}
|
|
559
|
+
function projectSpanErrorForTelemetry(error) {
|
|
393
560
|
const statusCode = errorStatusCode(error);
|
|
394
561
|
return {
|
|
395
|
-
type,
|
|
562
|
+
type: "OperationError",
|
|
396
563
|
...statusCode === void 0 ? {} : { statusCode },
|
|
397
564
|
statusMessage: statusCode === void 0 ? "operation failed" : `HTTP ${statusCode}`
|
|
398
565
|
};
|
|
399
566
|
}
|
|
400
567
|
function errorStatusCode(error) {
|
|
401
568
|
if (typeof error !== "object" || error === null) return void 0;
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
569
|
+
try {
|
|
570
|
+
const value = error.status;
|
|
571
|
+
const statusCode = Number.isInteger(value) && typeof value === "number" ? value : error.statusCode;
|
|
572
|
+
return Number.isInteger(statusCode) && typeof statusCode === "number" && statusCode >= 100 && statusCode <= 599 ? statusCode : void 0;
|
|
573
|
+
} catch {
|
|
574
|
+
return void 0;
|
|
575
|
+
}
|
|
405
576
|
}
|
|
406
577
|
function errorToAttributes(error) {
|
|
407
578
|
return {
|
|
@@ -409,9 +580,6 @@ function errorToAttributes(error) {
|
|
|
409
580
|
...error.statusCode === void 0 ? {} : { "error.status_code": error.statusCode }
|
|
410
581
|
};
|
|
411
582
|
}
|
|
412
|
-
function errorMessage(error) {
|
|
413
|
-
return error instanceof Error ? error.message : String(error);
|
|
414
|
-
}
|
|
415
583
|
function otlpAttributes(attributes) {
|
|
416
584
|
return Object.entries(cleanAttributes(attributes)).map(([key, value]) => ({
|
|
417
585
|
key,
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["import { collectDefaultMetrics, Counter, Gauge, Histogram, Registry } from \"prom-client\";\nimport { SandboxBackend } from \"@opengeni/contracts\";\n\nexport type AttributeValue = string | number | boolean | null | undefined;\nexport type Attributes = Record<string, AttributeValue>;\n\nexport type ObservabilitySettings = {\n serviceName: string;\n environment: string;\n deploymentRevision?: string | undefined;\n observabilityStructuredLogs: boolean;\n observabilityMetricsEnabled: boolean;\n observabilityOtlpEndpoint?: string | undefined;\n observabilityOtlpHeaders: string;\n};\n\nexport type ObservabilityOptions = {\n component: string;\n now?: () => number;\n exporter?: (url: string, body: unknown, headers: Record<string, string>) => Promise<void>;\n};\n\nexport type Span = {\n traceId: string;\n spanId: string;\n end: (input?: { attributes?: Attributes; error?: unknown }) => void;\n};\n\nexport type MetricLabels = Record<string, AttributeValue>;\n\n/**\n * Stable selectors shared by OpenGeni's runtime metrics and optional\n * Prometheus/Grafana distribution. Operators can use these values for custom\n * namespaces and dashboard ConfigMaps without duplicating chart internals.\n */\nexport const OPENGENI_OBSERVABILITY_DISTRIBUTION = {\n monitoringNamespaceLabel: \"opengeni.ai/monitoring\",\n monitoringNamespaceLabelValue: \"enabled\",\n grafanaDashboardLabel: \"grafana_dashboard\",\n grafanaDashboardLabelValue: \"1\",\n} as const;\n\nconst httpHistogramBuckets = [0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10];\nconst durationHistogramBuckets = [\n 0.01, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10, 30, 60, 120, 300, 900, 1800, 3600,\n];\n\nconst SANDBOX_OPERATION_BACKENDS = new Set<string>([...SandboxBackend.options, \"unprovisioned\"]);\n\nconst SANDBOX_OPERATION_NAMES = new Set([\n \"desktopInput\",\n \"screenshot\",\n \"exec\",\n \"execCommand\",\n \"writeStdin\",\n \"cancelExecCommand\",\n \"readFile\",\n \"writeFile\",\n \"listDir\",\n \"pathExists\",\n \"viewImage\",\n \"materializeEntry\",\n \"editor.createFile\",\n \"editor.updateFile\",\n \"editor.deleteFile\",\n \"resolveExposedPort\",\n \"serializeSessionState\",\n]);\n\nexport type SandboxOperationMetricObservation = {\n backend: string;\n op: string;\n outcome: \"ok\" | \"failed\";\n durationMs: number;\n};\n\nexport function createObservability(\n settings: ObservabilitySettings,\n options: ObservabilityOptions,\n): Observability {\n return new Observability(settings, options);\n}\n\ntype MetricRegistration = {\n kind: \"counter\" | \"gauge\" | \"histogram\";\n labelNames: string[];\n};\n\nexport class Observability {\n private readonly registry = new Registry();\n private readonly counters = new Map<string, Counter<string>>();\n private readonly gauges = new Map<string, Gauge<string>>();\n private readonly histograms = new Map<string, Histogram<string>>();\n private readonly registrations = new Map<string, MetricRegistration>();\n private readonly now: () => number;\n private readonly exporter: (\n url: string,\n body: unknown,\n headers: Record<string, string>,\n ) => Promise<void>;\n private readonly resourceAttributes: Attributes;\n\n constructor(\n private readonly settings: ObservabilitySettings,\n private readonly options: ObservabilityOptions,\n ) {\n this.now = options.now ?? Date.now;\n this.exporter = options.exporter ?? defaultExporter;\n this.resourceAttributes = {\n \"service.name\": settings.serviceName,\n \"deployment.environment\": settings.environment,\n \"opengeni.component\": options.component,\n };\n this.registry.setDefaultLabels({\n service: settings.serviceName,\n environment: settings.environment,\n component: options.component,\n });\n if (settings.observabilityMetricsEnabled) {\n collectDefaultMetrics({ register: this.registry, prefix: \"opengeni_\" });\n this.setGauge({\n name: \"opengeni_build_info\",\n help: \"OpenGeni build information.\",\n labels: {\n version: buildVersion(),\n revision: settings.deploymentRevision ?? \"dev\",\n },\n value: 1,\n });\n }\n }\n\n debug(message: string, attributes: Attributes = {}): void {\n this.log(\"debug\", message, attributes);\n }\n\n info(message: string, attributes: Attributes = {}): void {\n this.log(\"info\", message, attributes);\n }\n\n warn(message: string, attributes: Attributes = {}): void {\n this.log(\"warn\", message, attributes);\n }\n\n error(message: string, attributes: Attributes = {}): void {\n this.log(\"error\", message, attributes);\n }\n\n log(\n level: \"debug\" | \"info\" | \"warn\" | \"error\",\n message: string,\n attributes: Attributes = {},\n ): void {\n if (!this.settings.observabilityStructuredLogs) {\n const line = attributes.error ? `${message}: ${String(attributes.error)}` : message;\n if (level === \"warn\") {\n console.warn(line);\n } else if (level === \"error\") {\n console.error(line);\n } else {\n console.log(line);\n }\n return;\n }\n const record = {\n timestamp: new Date(this.now()).toISOString(),\n level,\n message,\n service: this.settings.serviceName,\n environment: this.settings.environment,\n component: this.options.component,\n ...cleanAttributes(attributes),\n };\n const serialized = JSON.stringify(record);\n if (level === \"warn\") {\n console.warn(serialized);\n } else if (level === \"error\") {\n console.error(serialized);\n } else {\n console.log(serialized);\n }\n }\n\n startSpan(name: string, attributes: Attributes = {}): Span {\n const traceId = randomHex(16);\n const spanId = randomHex(8);\n const startMs = this.now();\n let ended = false;\n return {\n traceId,\n spanId,\n end: (input = {}) => {\n if (ended) {\n return;\n }\n ended = true;\n const sanitizedError =\n input.error !== undefined && input.error !== null\n ? sanitizeSpanError(input.error)\n : undefined;\n const errorAttributes = sanitizedError ? errorToAttributes(sanitizedError) : {};\n this.exportSpan({\n traceId,\n spanId,\n name,\n startMs,\n endMs: this.now(),\n attributes: {\n ...attributes,\n ...input.attributes,\n ...errorAttributes,\n },\n ...(sanitizedError ? { error: sanitizedError } : {}),\n });\n },\n };\n }\n\n recordHttpRequest(input: {\n method: string;\n route: string;\n status: number;\n durationSeconds: number;\n }): void {\n this.incrementCounter({\n name: \"opengeni_http_requests_total\",\n help: \"Total HTTP requests handled by OpenGeni.\",\n labels: {\n method: input.method,\n route: input.route,\n status: String(input.status),\n component: this.options.component,\n },\n });\n this.observeHistogram({\n name: \"opengeni_http_request_duration_seconds\",\n help: \"HTTP request duration in seconds.\",\n buckets: httpHistogramBuckets,\n value: input.durationSeconds,\n labels: {\n method: input.method,\n route: input.route,\n component: this.options.component,\n },\n });\n }\n\n recordWorkerActivity(input: { activity: string; status: string; durationSeconds: number }): void {\n this.incrementCounter({\n name: \"opengeni_worker_activity_runs_total\",\n help: \"Total worker activity executions.\",\n labels: {\n activity: input.activity,\n status: input.status,\n component: this.options.component,\n },\n });\n this.observeHistogram({\n name: \"opengeni_worker_activity_duration_seconds\",\n help: \"Worker activity duration in seconds.\",\n buckets: durationHistogramBuckets,\n value: input.durationSeconds,\n labels: {\n activity: input.activity,\n component: this.options.component,\n },\n });\n }\n\n incrementCounter(input: {\n name: string;\n help?: string;\n labels?: MetricLabels;\n amount?: number;\n }): void {\n if (!this.settings.observabilityMetricsEnabled) {\n return;\n }\n const labels = normalizeLabels(input.labels);\n const counter = this.counter(\n input.name,\n input.help ?? `${input.name} counter.`,\n Object.keys(labels),\n );\n counter.inc(labels as never, input.amount ?? 1);\n }\n\n setGauge(input: { name: string; help?: string; labels?: MetricLabels; value: number }): void {\n if (!this.settings.observabilityMetricsEnabled) {\n return;\n }\n const labels = normalizeLabels(input.labels);\n const gauge = this.gauge(input.name, input.help ?? `${input.name} gauge.`, Object.keys(labels));\n gauge.set(labels as never, input.value);\n }\n\n incrementGauge(input: {\n name: string;\n help?: string;\n labels?: MetricLabels;\n amount?: number;\n }): void {\n if (!this.settings.observabilityMetricsEnabled) {\n return;\n }\n const labels = normalizeLabels(input.labels);\n const gauge = this.gauge(input.name, input.help ?? `${input.name} gauge.`, Object.keys(labels));\n gauge.inc(labels as never, input.amount ?? 1);\n }\n\n observeHistogram(input: {\n name: string;\n help?: string;\n labels?: MetricLabels;\n value: number;\n buckets?: number[];\n }): void {\n if (!this.settings.observabilityMetricsEnabled) {\n return;\n }\n const labels = normalizeLabels(input.labels);\n const histogram = this.histogram(\n input.name,\n input.help ?? `${input.name} histogram.`,\n Object.keys(labels),\n input.buckets ?? durationHistogramBuckets,\n );\n histogram.observe(labels as never, input.value);\n }\n\n async prometheusMetrics(): Promise<string> {\n if (!this.settings.observabilityMetricsEnabled) {\n return \"\";\n }\n return await this.registry.metrics();\n }\n\n private counter(name: string, help: string, labelNames: string[]): Counter<string> {\n const existing = this.counters.get(name);\n if (existing) {\n this.assertRegistration(name, \"counter\", labelNames);\n return existing;\n }\n this.register(name, \"counter\", labelNames);\n const metric = new Counter({ name, help, labelNames, registers: [this.registry] });\n this.counters.set(name, metric);\n return metric;\n }\n\n private gauge(name: string, help: string, labelNames: string[]): Gauge<string> {\n const existing = this.gauges.get(name);\n if (existing) {\n this.assertRegistration(name, \"gauge\", labelNames);\n return existing;\n }\n this.register(name, \"gauge\", labelNames);\n const metric = new Gauge({ name, help, labelNames, registers: [this.registry] });\n this.gauges.set(name, metric);\n return metric;\n }\n\n private histogram(\n name: string,\n help: string,\n labelNames: string[],\n buckets: number[],\n ): Histogram<string> {\n const existing = this.histograms.get(name);\n if (existing) {\n this.assertRegistration(name, \"histogram\", labelNames);\n return existing;\n }\n this.register(name, \"histogram\", labelNames);\n const metric = new Histogram({ name, help, labelNames, buckets, registers: [this.registry] });\n this.histograms.set(name, metric);\n return metric;\n }\n\n private register(name: string, kind: MetricRegistration[\"kind\"], labelNames: string[]): void {\n const sorted = [...labelNames].sort();\n this.registrations.set(name, { kind, labelNames: sorted });\n }\n\n private assertRegistration(\n name: string,\n kind: MetricRegistration[\"kind\"],\n labelNames: string[],\n ): void {\n const registration = this.registrations.get(name);\n const sorted = [...labelNames].sort();\n if (\n !registration ||\n registration.kind !== kind ||\n registration.labelNames.length !== sorted.length ||\n registration.labelNames.some((label, index) => label !== sorted[index])\n ) {\n throw new Error(\n `Metric ${name} was already registered as ${registration?.kind ?? \"unknown\"} ` +\n `with labels [${registration?.labelNames.join(\",\") ?? \"\"}], not ${kind} [${sorted.join(\",\")}]`,\n );\n }\n }\n\n private exportSpan(span: {\n traceId: string;\n spanId: string;\n name: string;\n startMs: number;\n endMs: number;\n attributes: Attributes;\n error?: SanitizedSpanError;\n }): void {\n if (!this.settings.observabilityOtlpEndpoint) {\n return;\n }\n const endpoint = `${this.settings.observabilityOtlpEndpoint.replace(/\\/$/, \"\")}/v1/traces`;\n const body = {\n resourceSpans: [\n {\n resource: {\n attributes: otlpAttributes(this.resourceAttributes),\n },\n scopeSpans: [\n {\n scope: {\n name: \"@opengeni/observability\",\n version: \"0.1.0\",\n },\n spans: [\n {\n traceId: span.traceId,\n spanId: span.spanId,\n name: span.name,\n kind: 1,\n startTimeUnixNano: millisToNanos(span.startMs),\n endTimeUnixNano: millisToNanos(span.endMs),\n attributes: otlpAttributes(span.attributes),\n status: span.error ? { code: 2, message: span.error.statusMessage } : { code: 1 },\n },\n ],\n },\n ],\n },\n ],\n };\n void this.exporter(endpoint, body, parseHeaders(this.settings.observabilityOtlpHeaders)).catch(\n (error) => {\n this.warn(\"OTLP span export failed\", { error: errorMessage(error), endpoint });\n },\n );\n }\n}\n\n/**\n * Convert routed sandbox-provider observations into one bounded Prometheus\n * contract shared by API-direct and worker-turn execution. Provider/session\n * identifiers, commands, paths, and other request data can never become labels:\n * a future backend or operation is deliberately collapsed to `unknown` until\n * this allowlist is reviewed.\n *\n * The returned callback is fail-safe. Metrics must never change the provider\n * operation's result; a registration/exporter error is counted best-effort and\n * then swallowed at this telemetry boundary.\n */\nexport function sandboxOperationMetricObserver(\n observability: Observability,\n): (observation: SandboxOperationMetricObservation) => void {\n return (observation) => {\n const backend = SANDBOX_OPERATION_BACKENDS.has(observation.backend)\n ? observation.backend\n : \"unknown\";\n const op = SANDBOX_OPERATION_NAMES.has(observation.op) ? observation.op : \"unknown\";\n try {\n observability.incrementCounter({\n name: \"opengeni_sandbox_operations_total\",\n help: \"Physical routed sandbox provider operations by backend, operation, and outcome.\",\n labels: { backend, op, outcome: observation.outcome },\n });\n observability.observeHistogram({\n name: \"opengeni_sandbox_operation_duration_seconds\",\n help: \"Physical routed sandbox provider-operation duration in seconds.\",\n labels: { backend, op },\n value: Math.max(0, observation.durationMs) / 1_000,\n });\n } catch {\n try {\n observability.incrementCounter({\n name: \"opengeni_observability_observer_errors_total\",\n help: \"Observability observer failures isolated from product execution.\",\n labels: { observer: \"sandbox_operation\" },\n });\n } catch {\n // The metrics registry itself is unhealthy. Product execution remains\n // authoritative and must not inherit an observability failure.\n }\n }\n };\n}\n\nexport type StartupDependencyRetryEvent = {\n label: string;\n attempt: number;\n attempts: number;\n delayMs: number;\n error: unknown;\n};\n\nexport function logStartupDependencyRetry(\n observability: Observability,\n event: StartupDependencyRetryEvent,\n): void {\n const message = event.error instanceof Error ? event.error.message : String(event.error);\n observability.warn(\"Startup dependency connection failed; retrying\", {\n dependency: event.label,\n attempt: event.attempt,\n attempts: event.attempts,\n delayMs: event.delayMs,\n error: message,\n });\n}\n\nfunction normalizeLabels(labels: MetricLabels = {}): Record<string, string> {\n return Object.fromEntries(\n Object.entries(labels)\n .filter(([, value]) => value !== undefined && value !== null)\n .map(([key, value]): [string, string] => [key, String(value)])\n .sort((left, right) => left[0].localeCompare(right[0])),\n );\n}\n\nfunction buildVersion(): string {\n return process.env.OPENGENI_VERSION ?? process.env.npm_package_version ?? \"dev\";\n}\n\nfunction cleanAttributes(attributes: Attributes): Record<string, string | number | boolean | null> {\n return Object.fromEntries(\n Object.entries(attributes).filter(([, value]) => value !== undefined),\n ) as Record<string, string | number | boolean | null>;\n}\n\ntype SanitizedSpanError = {\n type: string;\n statusCode?: number;\n statusMessage: string;\n};\n\nfunction sanitizeSpanError(error: unknown): SanitizedSpanError {\n const type =\n error instanceof Error && /^[A-Za-z][A-Za-z0-9_.-]{0,63}$/.test(error.name)\n ? error.name\n : \"Error\";\n const statusCode = errorStatusCode(error);\n return {\n type,\n ...(statusCode === undefined ? {} : { statusCode }),\n statusMessage: statusCode === undefined ? \"operation failed\" : `HTTP ${statusCode}`,\n };\n}\n\nfunction errorStatusCode(error: unknown): number | undefined {\n if (typeof error !== \"object\" || error === null) return undefined;\n const value = (error as { status?: unknown; statusCode?: unknown }).status;\n const statusCode =\n Number.isInteger(value) && typeof value === \"number\"\n ? value\n : (error as { statusCode?: unknown }).statusCode;\n return Number.isInteger(statusCode) &&\n typeof statusCode === \"number\" &&\n statusCode >= 100 &&\n statusCode <= 599\n ? statusCode\n : undefined;\n}\n\nfunction errorToAttributes(error: SanitizedSpanError): Attributes {\n return {\n \"error.type\": error.type,\n ...(error.statusCode === undefined ? {} : { \"error.status_code\": error.statusCode }),\n };\n}\n\nfunction errorMessage(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n\nfunction otlpAttributes(\n attributes: Attributes,\n): Array<{ key: string; value: Record<string, string | number | boolean> }> {\n return Object.entries(cleanAttributes(attributes)).map(([key, value]) => ({\n key,\n value: otlpValue(value),\n }));\n}\n\nfunction otlpValue(\n value: string | number | boolean | null,\n): Record<string, string | number | boolean> {\n if (typeof value === \"number\") {\n return Number.isInteger(value) ? { intValue: value } : { doubleValue: value };\n }\n if (typeof value === \"boolean\") {\n return { boolValue: value };\n }\n return { stringValue: value === null ? \"\" : boundedOtlpString(value) };\n}\n\nfunction boundedOtlpString(value: string): string {\n const bytes = new TextEncoder().encode(value);\n if (bytes.byteLength <= 512) return value;\n return `${new TextDecoder().decode(bytes.slice(0, 509))}…`;\n}\n\nfunction millisToNanos(ms: number): string {\n return String(BigInt(Math.round(ms)) * 1_000_000n);\n}\n\nfunction randomHex(bytes: number): string {\n const values = crypto.getRandomValues(new Uint8Array(bytes));\n return Array.from(values, (value) => value.toString(16).padStart(2, \"0\")).join(\"\");\n}\n\nexport function parseHeaders(value: string): Record<string, string> {\n if (!value.trim()) {\n return {};\n }\n const entries: Array<[string, string]> = value\n .split(\",\")\n .map((pair): [string, string] => {\n const separator = pair.indexOf(\"=\");\n if (separator === -1) {\n return [pair.trim(), \"\"];\n }\n return [pair.slice(0, separator).trim(), pair.slice(separator + 1).trim()];\n })\n .filter(([key]) => key.length > 0);\n return Object.fromEntries(entries);\n}\n\nasync function defaultExporter(\n url: string,\n body: unknown,\n headers: Record<string, string>,\n): Promise<void> {\n const response = await fetch(url, {\n method: \"POST\",\n headers: {\n \"content-type\": \"application/json\",\n ...headers,\n },\n body: JSON.stringify(body),\n });\n if (!response.ok) {\n throw new Error(`OTLP endpoint returned HTTP ${response.status}`);\n }\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["import { collectDefaultMetrics, Counter, Gauge, Histogram, Registry } from \"prom-client\";\nimport { SandboxBackend } from \"@opengeni/contracts\";\n\nexport type AttributeValue = string | number | boolean | null | undefined;\nexport type Attributes = Record<string, AttributeValue>;\n\nexport type ObservabilitySettings = {\n serviceName: string;\n environment: string;\n deploymentRevision?: string | undefined;\n observabilityStructuredLogs: boolean;\n observabilityMetricsEnabled: boolean;\n observabilityOtlpEndpoint?: string | undefined;\n observabilityOtlpHeaders: string;\n};\n\nexport type ObservabilityOptions = {\n component: string;\n now?: () => number;\n exporter?: (url: string, body: unknown, headers: Record<string, string>) => Promise<void>;\n};\n\nexport type Span = {\n traceId: string;\n spanId: string;\n end: (input?: { attributes?: Attributes; error?: unknown }) => void;\n};\n\nexport type MetricLabels = Record<string, AttributeValue>;\n\n/**\n * Stable selectors shared by OpenGeni's runtime metrics and optional\n * Prometheus/Grafana distribution. Operators can use these values for custom\n * namespaces and dashboard ConfigMaps without duplicating chart internals.\n */\nexport const OPENGENI_OBSERVABILITY_DISTRIBUTION = {\n monitoringNamespaceLabel: \"opengeni.ai/monitoring\",\n monitoringNamespaceLabelValue: \"enabled\",\n grafanaDashboardLabel: \"grafana_dashboard\",\n grafanaDashboardLabelValue: \"1\",\n} as const;\n\nconst httpHistogramBuckets = [0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10];\nconst durationHistogramBuckets = [\n 0.01, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10, 30, 60, 120, 300, 900, 1800, 3600,\n];\n\nconst SANDBOX_OPERATION_BACKENDS = new Set<string>([...SandboxBackend.options, \"unprovisioned\"]);\n\nconst SANDBOX_OPERATION_NAMES = new Set([\n \"desktopInput\",\n \"screenshot\",\n \"exec\",\n \"execCommand\",\n \"writeStdin\",\n \"cancelExecCommand\",\n \"readFile\",\n \"writeFile\",\n \"listDir\",\n \"pathExists\",\n \"viewImage\",\n \"materializeEntry\",\n \"editor.createFile\",\n \"editor.updateFile\",\n \"editor.deleteFile\",\n \"resolveExposedPort\",\n \"serializeSessionState\",\n]);\n\n/**\n * External logs and OTLP are public/third-party projections, not canonical\n * OpenGeni storage. Only this reviewed closed set of operational fields may\n * cross that boundary. Unknown keys are omitted regardless of their value, so\n * a new diagnostic, identifier, command, response, or provider field cannot\n * become public by accident. This is schema projection, never value inspection\n * or rewriting.\n */\nconst PUBLIC_TELEMETRY_ATTRIBUTE_KEYS = new Set([\n \"http.request.method\",\n \"http.response.status_code\",\n \"opengeni.route\",\n \"opengeni.duration_ms\",\n \"opengeni.finalization_duration_ms\",\n \"opengeni.trigger_kind\",\n \"opengeni.status\",\n \"error.type\",\n \"error.status_code\",\n \"method\",\n \"route\",\n \"status\",\n \"durationMs\",\n \"attempt\",\n \"attempts\",\n \"delayMs\",\n \"provider\",\n \"providerApi\",\n \"model\",\n \"inputTokens\",\n \"outputTokens\",\n \"cachedTokens\",\n \"cacheWriteTokens\",\n \"reasoningTokens\",\n \"accountChangedFromPrevCall\",\n \"rejectedFields\",\n \"dependency\",\n \"activity\",\n \"backend\",\n \"op\",\n \"outcome\",\n \"eventType\",\n \"surface\",\n \"reason\",\n \"originalBytes\",\n \"deliveredBytes\",\n \"estimatedOriginalTokens\",\n \"estimatedDeliveredTokens\",\n \"fullEvidenceAvailable\",\n \"retainedOutputKind\",\n]);\n\n/**\n * Public diagnostic values are protocol constants, never values inferred from\n * an exception's name, constructor, code, message, or enumerable properties.\n * Unknown classes collapse to one fixed fallback; unknown codes and origins\n * are omitted rather than copied through based on syntax.\n */\nconst PUBLIC_TELEMETRY_ERROR_CLASSES = new Set([\n \"OperationError\",\n \"CodexCheckpointOperationError\",\n \"CodexFleetShadowOperationError\",\n \"ComputerActionTimeoutError\",\n \"ComputerUnavailableError\",\n \"CredentialRenewalOperationError\",\n \"CredentialReadOperationError\",\n \"EventPublishOperationError\",\n \"GitCredentialRenewalOperationError\",\n \"HostExportOperationError\",\n \"HttpOperationError\",\n \"McpLifecycleError\",\n \"McpOperationError\",\n \"MemoryEmbeddingOperationError\",\n \"MemorySearchOperationError\",\n \"NatsAuthCalloutOperationError\",\n \"OAuthOperationError\",\n \"RunCredentialRenewalOperationError\",\n \"RunStateCompatibilityError\",\n \"SnapshotOperationError\",\n \"StartupDependencyError\",\n \"TelemetryExportError\",\n \"ToolspaceOperationError\",\n \"ToolspaceTokenRenewalOperationError\",\n \"WorkerLifecycleOperation\",\n \"WorkerLifecycleOperationError\",\n \"WorkerOperationError\",\n \"WorkflowWakeOperationError\",\n]);\n\nconst PUBLIC_TELEMETRY_ERROR_CODES = new Set([\n \"agent_command_wake_failed\",\n \"cleared_goal_live_publish_failed\",\n \"codex_failover_checkpoint_failed\",\n \"codex_fleet_shadow_failed\",\n \"codex_lease_loss_checkpoint_failed\",\n \"codex_active_credential_read_failed\",\n \"command_yield_timeout\",\n \"conflict\",\n \"control_wake_dispatch_failed\",\n \"fenced_event_live_publish_failed\",\n \"forbidden\",\n \"host_export_batch_delivery_failed\",\n \"host_export_failure_settlement_stale\",\n \"host_export_pump_iteration_failed\",\n \"host_export_retention_prune_failed\",\n \"idempotency_conflict\",\n \"incompatible_exposed_ports\",\n \"internal_error\",\n \"limit_exceeded\",\n \"mcp_close_failed\",\n \"mcp_connect_failed\",\n \"mcp_tool_call_failed\",\n \"mcp_tools_list_failed\",\n \"mcp_transport_failed\",\n \"memory_edit_embedding_failed\",\n \"memory_hybrid_vector_failed\",\n \"memory_save_embedding_failed\",\n \"nats_auth_callout_start_failed\",\n \"nested_agent_depth_exceeded\",\n \"nested_agent_depth_override_forbidden\",\n \"not_found\",\n \"oauth_operation_failed\",\n \"otlp_export_failed\",\n \"payment_required\",\n \"provider_verification_failed\",\n \"screenshot_capture_failed\",\n \"session_event_live_publish_failed\",\n \"session_workflow_wake_failed\",\n \"snapshot_operation_failed\",\n \"startup_dependency_retry\",\n \"tool_list_too_large\",\n \"tool_result_too_large\",\n \"toolspace_operation_failed\",\n \"unauthenticated\",\n \"upstream_unavailable\",\n \"validation_failed\",\n \"worker_draining\",\n \"worker_operation_failed\",\n \"worker_shutdown_request_failed\",\n \"workspace_control_live_publish_failed\",\n]);\n\nconst PUBLIC_TELEMETRY_ERROR_ORIGINS = new Set([\n \"api\",\n \"core\",\n \"db\",\n \"events\",\n \"host-export\",\n \"oauth\",\n \"observability\",\n \"runtime\",\n \"sandbox-computer\",\n \"sandbox-resume\",\n \"toolspace\",\n \"worker\",\n \"worker-lifecycle\",\n]);\n\nexport type SandboxOperationMetricObservation = {\n backend: string;\n op: string;\n outcome: \"ok\" | \"not_found\" | \"failed\";\n durationMs: number;\n};\n\nexport function createObservability(\n settings: ObservabilitySettings,\n options: ObservabilityOptions,\n): Observability {\n return new Observability(settings, options);\n}\n\ntype MetricRegistration = {\n kind: \"counter\" | \"gauge\" | \"histogram\";\n labelNames: string[];\n};\n\nexport class Observability {\n private readonly registry = new Registry();\n private readonly counters = new Map<string, Counter<string>>();\n private readonly gauges = new Map<string, Gauge<string>>();\n private readonly histograms = new Map<string, Histogram<string>>();\n private readonly registrations = new Map<string, MetricRegistration>();\n private readonly now: () => number;\n private readonly exporter: (\n url: string,\n body: unknown,\n headers: Record<string, string>,\n ) => Promise<void>;\n private readonly resourceAttributes: Attributes;\n\n constructor(\n private readonly settings: ObservabilitySettings,\n private readonly options: ObservabilityOptions,\n ) {\n this.now = options.now ?? Date.now;\n this.exporter = options.exporter ?? defaultExporter;\n this.resourceAttributes = {\n \"service.name\": settings.serviceName,\n \"deployment.environment\": settings.environment,\n \"opengeni.component\": options.component,\n };\n this.registry.setDefaultLabels({\n service: settings.serviceName,\n environment: settings.environment,\n component: options.component,\n });\n if (settings.observabilityMetricsEnabled) {\n collectDefaultMetrics({ register: this.registry, prefix: \"opengeni_\" });\n this.setGauge({\n name: \"opengeni_build_info\",\n help: \"OpenGeni build information.\",\n labels: {\n version: buildVersion(),\n revision: settings.deploymentRevision ?? \"dev\",\n },\n value: 1,\n });\n }\n }\n\n debug(message: string, attributes: Attributes = {}): void {\n this.log(\"debug\", message, attributes);\n }\n\n info(message: string, attributes: Attributes = {}): void {\n this.log(\"info\", message, attributes);\n }\n\n warn(message: string, attributes: Attributes = {}): void {\n this.log(\"warn\", message, attributes);\n }\n\n error(message: string, attributes: Attributes = {}): void {\n this.log(\"error\", message, attributes);\n }\n\n log(\n level: \"debug\" | \"info\" | \"warn\" | \"error\",\n message: string,\n attributes: Attributes = {},\n ): void {\n const publicAttributes = projectPublicTelemetryAttributes(attributes);\n if (!this.settings.observabilityStructuredLogs) {\n if (level === \"warn\") {\n console.warn(message);\n } else if (level === \"error\") {\n console.error(message);\n } else {\n console.log(message);\n }\n return;\n }\n const record = {\n timestamp: new Date(this.now()).toISOString(),\n level,\n message,\n service: this.settings.serviceName,\n environment: this.settings.environment,\n component: this.options.component,\n ...cleanAttributes(publicAttributes),\n };\n const serialized = JSON.stringify(record);\n if (level === \"warn\") {\n console.warn(serialized);\n } else if (level === \"error\") {\n console.error(serialized);\n } else {\n console.log(serialized);\n }\n }\n\n startSpan(name: string, attributes: Attributes = {}): Span {\n const traceId = randomHex(16);\n const spanId = randomHex(8);\n const startMs = this.now();\n let ended = false;\n return {\n traceId,\n spanId,\n end: (input = {}) => {\n if (ended) {\n return;\n }\n ended = true;\n const telemetryError =\n input.error !== undefined && input.error !== null\n ? projectSpanErrorForTelemetry(input.error)\n : undefined;\n const errorAttributes = telemetryError ? errorToAttributes(telemetryError) : {};\n this.exportSpan({\n traceId,\n spanId,\n name,\n startMs,\n endMs: this.now(),\n attributes: {\n ...projectPublicTelemetryAttributes({\n ...attributes,\n ...input.attributes,\n ...errorAttributes,\n }),\n },\n ...(telemetryError ? { error: telemetryError } : {}),\n });\n },\n };\n }\n\n recordHttpRequest(input: {\n method: string;\n route: string;\n status: number;\n durationSeconds: number;\n }): void {\n this.incrementCounter({\n name: \"opengeni_http_requests_total\",\n help: \"Total HTTP requests handled by OpenGeni.\",\n labels: {\n method: input.method,\n route: input.route,\n status: String(input.status),\n component: this.options.component,\n },\n });\n this.observeHistogram({\n name: \"opengeni_http_request_duration_seconds\",\n help: \"HTTP request duration in seconds.\",\n buckets: httpHistogramBuckets,\n value: input.durationSeconds,\n labels: {\n method: input.method,\n route: input.route,\n component: this.options.component,\n },\n });\n }\n\n recordWorkerActivity(input: { activity: string; status: string; durationSeconds: number }): void {\n this.incrementCounter({\n name: \"opengeni_worker_activity_runs_total\",\n help: \"Total worker activity executions.\",\n labels: {\n activity: input.activity,\n status: input.status,\n component: this.options.component,\n },\n });\n this.observeHistogram({\n name: \"opengeni_worker_activity_duration_seconds\",\n help: \"Worker activity duration in seconds.\",\n buckets: durationHistogramBuckets,\n value: input.durationSeconds,\n labels: {\n activity: input.activity,\n component: this.options.component,\n },\n });\n }\n\n incrementCounter(input: {\n name: string;\n help?: string;\n labels?: MetricLabels;\n amount?: number;\n }): void {\n if (!this.settings.observabilityMetricsEnabled) {\n return;\n }\n const labels = normalizeLabels(input.labels);\n const counter = this.counter(\n input.name,\n input.help ?? `${input.name} counter.`,\n Object.keys(labels),\n );\n counter.inc(labels as never, input.amount ?? 1);\n }\n\n setGauge(input: { name: string; help?: string; labels?: MetricLabels; value: number }): void {\n if (!this.settings.observabilityMetricsEnabled) {\n return;\n }\n const labels = normalizeLabels(input.labels);\n const gauge = this.gauge(input.name, input.help ?? `${input.name} gauge.`, Object.keys(labels));\n gauge.set(labels as never, input.value);\n }\n\n incrementGauge(input: {\n name: string;\n help?: string;\n labels?: MetricLabels;\n amount?: number;\n }): void {\n if (!this.settings.observabilityMetricsEnabled) {\n return;\n }\n const labels = normalizeLabels(input.labels);\n const gauge = this.gauge(input.name, input.help ?? `${input.name} gauge.`, Object.keys(labels));\n gauge.inc(labels as never, input.amount ?? 1);\n }\n\n observeHistogram(input: {\n name: string;\n help?: string;\n labels?: MetricLabels;\n value: number;\n buckets?: number[];\n }): void {\n if (!this.settings.observabilityMetricsEnabled) {\n return;\n }\n const labels = normalizeLabels(input.labels);\n const histogram = this.histogram(\n input.name,\n input.help ?? `${input.name} histogram.`,\n Object.keys(labels),\n input.buckets ?? durationHistogramBuckets,\n );\n histogram.observe(labels as never, input.value);\n }\n\n async prometheusMetrics(): Promise<string> {\n if (!this.settings.observabilityMetricsEnabled) {\n return \"\";\n }\n return await this.registry.metrics();\n }\n\n private counter(name: string, help: string, labelNames: string[]): Counter<string> {\n const existing = this.counters.get(name);\n if (existing) {\n this.assertRegistration(name, \"counter\", labelNames);\n return existing;\n }\n this.register(name, \"counter\", labelNames);\n const metric = new Counter({ name, help, labelNames, registers: [this.registry] });\n this.counters.set(name, metric);\n return metric;\n }\n\n private gauge(name: string, help: string, labelNames: string[]): Gauge<string> {\n const existing = this.gauges.get(name);\n if (existing) {\n this.assertRegistration(name, \"gauge\", labelNames);\n return existing;\n }\n this.register(name, \"gauge\", labelNames);\n const metric = new Gauge({ name, help, labelNames, registers: [this.registry] });\n this.gauges.set(name, metric);\n return metric;\n }\n\n private histogram(\n name: string,\n help: string,\n labelNames: string[],\n buckets: number[],\n ): Histogram<string> {\n const existing = this.histograms.get(name);\n if (existing) {\n this.assertRegistration(name, \"histogram\", labelNames);\n return existing;\n }\n this.register(name, \"histogram\", labelNames);\n const metric = new Histogram({ name, help, labelNames, buckets, registers: [this.registry] });\n this.histograms.set(name, metric);\n return metric;\n }\n\n private register(name: string, kind: MetricRegistration[\"kind\"], labelNames: string[]): void {\n const sorted = [...labelNames].sort();\n this.registrations.set(name, { kind, labelNames: sorted });\n }\n\n private assertRegistration(\n name: string,\n kind: MetricRegistration[\"kind\"],\n labelNames: string[],\n ): void {\n const registration = this.registrations.get(name);\n const sorted = [...labelNames].sort();\n if (\n !registration ||\n registration.kind !== kind ||\n registration.labelNames.length !== sorted.length ||\n registration.labelNames.some((label, index) => label !== sorted[index])\n ) {\n throw new Error(\n `Metric ${name} was already registered as ${registration?.kind ?? \"unknown\"} ` +\n `with labels [${registration?.labelNames.join(\",\") ?? \"\"}], not ${kind} [${sorted.join(\",\")}]`,\n );\n }\n }\n\n private exportSpan(span: {\n traceId: string;\n spanId: string;\n name: string;\n startMs: number;\n endMs: number;\n attributes: Attributes;\n error?: TelemetrySpanError;\n }): void {\n if (!this.settings.observabilityOtlpEndpoint) {\n return;\n }\n const endpoint = `${this.settings.observabilityOtlpEndpoint.replace(/\\/$/, \"\")}/v1/traces`;\n const body = {\n resourceSpans: [\n {\n resource: {\n attributes: otlpAttributes(this.resourceAttributes),\n },\n scopeSpans: [\n {\n scope: {\n name: \"@opengeni/observability\",\n version: \"0.1.0\",\n },\n spans: [\n {\n traceId: span.traceId,\n spanId: span.spanId,\n name: span.name,\n kind: 1,\n startTimeUnixNano: millisToNanos(span.startMs),\n endTimeUnixNano: millisToNanos(span.endMs),\n attributes: otlpAttributes(span.attributes),\n status: span.error ? { code: 2, message: span.error.statusMessage } : { code: 1 },\n },\n ],\n },\n ],\n },\n ],\n };\n void this.exporter(endpoint, body, parseHeaders(this.settings.observabilityOtlpHeaders)).catch(\n () => {\n this.warn(\"OTLP span export failed\", {\n errorClass: \"TelemetryExportError\",\n errorCode: \"otlp_export_failed\",\n origin: \"observability\",\n });\n },\n );\n }\n}\n\n/**\n * Convert routed sandbox-provider observations into one bounded Prometheus\n * contract shared by API-direct and worker-turn execution. Provider/session\n * identifiers, commands, paths, and other request data can never become labels:\n * a future backend or operation is deliberately collapsed to `unknown` until\n * this allowlist is reviewed.\n *\n * The returned callback is fail-safe. Metrics must never change the provider\n * operation's result; a registration/exporter error is counted best-effort and\n * then swallowed at this telemetry boundary.\n */\nexport function sandboxOperationMetricObserver(\n observability: Observability,\n): (observation: SandboxOperationMetricObservation) => void {\n return (observation) => {\n const backend = SANDBOX_OPERATION_BACKENDS.has(observation.backend)\n ? observation.backend\n : \"unknown\";\n const op = SANDBOX_OPERATION_NAMES.has(observation.op) ? observation.op : \"unknown\";\n try {\n observability.incrementCounter({\n name: \"opengeni_sandbox_operations_total\",\n help: \"Physical routed sandbox provider operations by backend, operation, and outcome.\",\n labels: { backend, op, outcome: observation.outcome },\n });\n observability.observeHistogram({\n name: \"opengeni_sandbox_operation_duration_seconds\",\n help: \"Physical routed sandbox provider-operation duration in seconds.\",\n labels: { backend, op },\n value: Math.max(0, observation.durationMs) / 1_000,\n });\n } catch {\n try {\n observability.incrementCounter({\n name: \"opengeni_observability_observer_errors_total\",\n help: \"Observability observer failures isolated from product execution.\",\n labels: { observer: \"sandbox_operation\" },\n });\n } catch {\n // The metrics registry itself is unhealthy. Product execution remains\n // authoritative and must not inherit an observability failure.\n }\n }\n };\n}\n\nexport type StartupDependencyRetryEvent = {\n label: string;\n attempt: number;\n attempts: number;\n delayMs: number;\n error: unknown;\n};\n\nexport function logStartupDependencyRetry(\n observability: Observability,\n event: StartupDependencyRetryEvent,\n): void {\n observability.warn(\"Startup dependency connection failed; retrying\", {\n dependency: event.label,\n attempt: event.attempt,\n attempts: event.attempts,\n delayMs: event.delayMs,\n errorClass: \"StartupDependencyError\",\n errorCode: \"startup_dependency_retry\",\n origin: \"observability\",\n });\n}\n\nfunction normalizeLabels(labels: MetricLabels = {}): Record<string, string> {\n return Object.fromEntries(\n Object.entries(labels)\n .filter(([, value]) => value !== undefined && value !== null)\n .map(([key, value]): [string, string] => [key, String(value)])\n .sort((left, right) => left[0].localeCompare(right[0])),\n );\n}\n\nfunction buildVersion(): string {\n return process.env.OPENGENI_VERSION ?? process.env.npm_package_version ?? \"dev\";\n}\n\nfunction cleanAttributes(attributes: Attributes): Record<string, string | number | boolean | null> {\n return Object.fromEntries(\n Object.entries(attributes).filter(([, value]) => value !== undefined),\n ) as Record<string, string | number | boolean | null>;\n}\n\nfunction projectPublicTelemetryAttributes(attributes: Attributes): Attributes {\n if (\"errorClass\" in attributes || \"errorCode\" in attributes) {\n return projectPublicDiagnosticAttributes(attributes);\n }\n return Object.fromEntries(\n Object.entries(attributes).filter(\n ([key, value]) => value !== undefined && PUBLIC_TELEMETRY_ATTRIBUTE_KEYS.has(key),\n ),\n );\n}\n\nfunction projectPublicDiagnosticAttributes(attributes: Attributes): Attributes {\n const errorClass = attributes.errorClass;\n const errorCode = attributes.errorCode;\n const status = attributes.status;\n const origin = attributes.origin;\n return {\n errorClass:\n typeof errorClass === \"string\" && PUBLIC_TELEMETRY_ERROR_CLASSES.has(errorClass)\n ? errorClass\n : \"OperationError\",\n ...(typeof errorCode === \"string\" && PUBLIC_TELEMETRY_ERROR_CODES.has(errorCode)\n ? { errorCode }\n : {}),\n ...(typeof status === \"number\" && Number.isInteger(status) && status >= 100 && status <= 599\n ? { status }\n : {}),\n ...(typeof origin === \"string\" && PUBLIC_TELEMETRY_ERROR_ORIGINS.has(origin) ? { origin } : {}),\n };\n}\n\ntype TelemetrySpanError = {\n type: string;\n statusCode?: number;\n statusMessage: string;\n};\n\n/**\n * External OTLP projection. It intentionally exports only error class/status\n * metadata and never mutates canonical OpenGeni errors, events, or history.\n */\nfunction projectSpanErrorForTelemetry(error: unknown): TelemetrySpanError {\n const statusCode = errorStatusCode(error);\n return {\n type: \"OperationError\",\n ...(statusCode === undefined ? {} : { statusCode }),\n statusMessage: statusCode === undefined ? \"operation failed\" : `HTTP ${statusCode}`,\n };\n}\n\nfunction errorStatusCode(error: unknown): number | undefined {\n if (typeof error !== \"object\" || error === null) return undefined;\n try {\n const value = (error as { status?: unknown; statusCode?: unknown }).status;\n const statusCode =\n Number.isInteger(value) && typeof value === \"number\"\n ? value\n : (error as { statusCode?: unknown }).statusCode;\n return Number.isInteger(statusCode) &&\n typeof statusCode === \"number\" &&\n statusCode >= 100 &&\n statusCode <= 599\n ? statusCode\n : undefined;\n } catch {\n // OTLP projection must never mask the exact internal failure.\n return undefined;\n }\n}\n\nfunction errorToAttributes(error: TelemetrySpanError): Attributes {\n return {\n \"error.type\": error.type,\n ...(error.statusCode === undefined ? {} : { \"error.status_code\": error.statusCode }),\n };\n}\n\nfunction otlpAttributes(\n attributes: Attributes,\n): Array<{ key: string; value: Record<string, string | number | boolean> }> {\n return Object.entries(cleanAttributes(attributes)).map(([key, value]) => ({\n key,\n value: otlpValue(value),\n }));\n}\n\nfunction otlpValue(\n value: string | number | boolean | null,\n): Record<string, string | number | boolean> {\n if (typeof value === \"number\") {\n return Number.isInteger(value) ? { intValue: value } : { doubleValue: value };\n }\n if (typeof value === \"boolean\") {\n return { boolValue: value };\n }\n return { stringValue: value === null ? \"\" : boundedOtlpString(value) };\n}\n\nfunction boundedOtlpString(value: string): string {\n const bytes = new TextEncoder().encode(value);\n if (bytes.byteLength <= 512) return value;\n return `${new TextDecoder().decode(bytes.slice(0, 509))}…`;\n}\n\nfunction millisToNanos(ms: number): string {\n return String(BigInt(Math.round(ms)) * 1_000_000n);\n}\n\nfunction randomHex(bytes: number): string {\n const values = crypto.getRandomValues(new Uint8Array(bytes));\n return Array.from(values, (value) => value.toString(16).padStart(2, \"0\")).join(\"\");\n}\n\nexport function parseHeaders(value: string): Record<string, string> {\n if (!value.trim()) {\n return {};\n }\n const entries: Array<[string, string]> = value\n .split(\",\")\n .map((pair): [string, string] => {\n const separator = pair.indexOf(\"=\");\n if (separator === -1) {\n return [pair.trim(), \"\"];\n }\n return [pair.slice(0, separator).trim(), pair.slice(separator + 1).trim()];\n })\n .filter(([key]) => key.length > 0);\n return Object.fromEntries(entries);\n}\n\nasync function defaultExporter(\n url: string,\n body: unknown,\n headers: Record<string, string>,\n): Promise<void> {\n const response = await fetch(url, {\n method: \"POST\",\n headers: {\n \"content-type\": \"application/json\",\n ...headers,\n },\n body: JSON.stringify(body),\n });\n if (!response.ok) {\n throw new Error(`OTLP endpoint returned HTTP ${response.status}`);\n }\n}\n"],"mappings":"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","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@opengeni/observability",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.6",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"prepublishOnly": "bash ../../scripts/prepublish-guard"
|
|
32
32
|
},
|
|
33
33
|
"dependencies": {
|
|
34
|
-
"@opengeni/contracts": "^0.
|
|
34
|
+
"@opengeni/contracts": "^0.40.0",
|
|
35
35
|
"prom-client": "^15.1.3"
|
|
36
36
|
}
|
|
37
37
|
}
|
package/src/index.ts
CHANGED
|
@@ -67,10 +67,167 @@ const SANDBOX_OPERATION_NAMES = new Set([
|
|
|
67
67
|
"serializeSessionState",
|
|
68
68
|
]);
|
|
69
69
|
|
|
70
|
+
/**
|
|
71
|
+
* External logs and OTLP are public/third-party projections, not canonical
|
|
72
|
+
* OpenGeni storage. Only this reviewed closed set of operational fields may
|
|
73
|
+
* cross that boundary. Unknown keys are omitted regardless of their value, so
|
|
74
|
+
* a new diagnostic, identifier, command, response, or provider field cannot
|
|
75
|
+
* become public by accident. This is schema projection, never value inspection
|
|
76
|
+
* or rewriting.
|
|
77
|
+
*/
|
|
78
|
+
const PUBLIC_TELEMETRY_ATTRIBUTE_KEYS = new Set([
|
|
79
|
+
"http.request.method",
|
|
80
|
+
"http.response.status_code",
|
|
81
|
+
"opengeni.route",
|
|
82
|
+
"opengeni.duration_ms",
|
|
83
|
+
"opengeni.finalization_duration_ms",
|
|
84
|
+
"opengeni.trigger_kind",
|
|
85
|
+
"opengeni.status",
|
|
86
|
+
"error.type",
|
|
87
|
+
"error.status_code",
|
|
88
|
+
"method",
|
|
89
|
+
"route",
|
|
90
|
+
"status",
|
|
91
|
+
"durationMs",
|
|
92
|
+
"attempt",
|
|
93
|
+
"attempts",
|
|
94
|
+
"delayMs",
|
|
95
|
+
"provider",
|
|
96
|
+
"providerApi",
|
|
97
|
+
"model",
|
|
98
|
+
"inputTokens",
|
|
99
|
+
"outputTokens",
|
|
100
|
+
"cachedTokens",
|
|
101
|
+
"cacheWriteTokens",
|
|
102
|
+
"reasoningTokens",
|
|
103
|
+
"accountChangedFromPrevCall",
|
|
104
|
+
"rejectedFields",
|
|
105
|
+
"dependency",
|
|
106
|
+
"activity",
|
|
107
|
+
"backend",
|
|
108
|
+
"op",
|
|
109
|
+
"outcome",
|
|
110
|
+
"eventType",
|
|
111
|
+
"surface",
|
|
112
|
+
"reason",
|
|
113
|
+
"originalBytes",
|
|
114
|
+
"deliveredBytes",
|
|
115
|
+
"estimatedOriginalTokens",
|
|
116
|
+
"estimatedDeliveredTokens",
|
|
117
|
+
"fullEvidenceAvailable",
|
|
118
|
+
"retainedOutputKind",
|
|
119
|
+
]);
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Public diagnostic values are protocol constants, never values inferred from
|
|
123
|
+
* an exception's name, constructor, code, message, or enumerable properties.
|
|
124
|
+
* Unknown classes collapse to one fixed fallback; unknown codes and origins
|
|
125
|
+
* are omitted rather than copied through based on syntax.
|
|
126
|
+
*/
|
|
127
|
+
const PUBLIC_TELEMETRY_ERROR_CLASSES = new Set([
|
|
128
|
+
"OperationError",
|
|
129
|
+
"CodexCheckpointOperationError",
|
|
130
|
+
"CodexFleetShadowOperationError",
|
|
131
|
+
"ComputerActionTimeoutError",
|
|
132
|
+
"ComputerUnavailableError",
|
|
133
|
+
"CredentialRenewalOperationError",
|
|
134
|
+
"CredentialReadOperationError",
|
|
135
|
+
"EventPublishOperationError",
|
|
136
|
+
"GitCredentialRenewalOperationError",
|
|
137
|
+
"HostExportOperationError",
|
|
138
|
+
"HttpOperationError",
|
|
139
|
+
"McpLifecycleError",
|
|
140
|
+
"McpOperationError",
|
|
141
|
+
"MemoryEmbeddingOperationError",
|
|
142
|
+
"MemorySearchOperationError",
|
|
143
|
+
"NatsAuthCalloutOperationError",
|
|
144
|
+
"OAuthOperationError",
|
|
145
|
+
"RunCredentialRenewalOperationError",
|
|
146
|
+
"RunStateCompatibilityError",
|
|
147
|
+
"SnapshotOperationError",
|
|
148
|
+
"StartupDependencyError",
|
|
149
|
+
"TelemetryExportError",
|
|
150
|
+
"ToolspaceOperationError",
|
|
151
|
+
"ToolspaceTokenRenewalOperationError",
|
|
152
|
+
"WorkerLifecycleOperation",
|
|
153
|
+
"WorkerLifecycleOperationError",
|
|
154
|
+
"WorkerOperationError",
|
|
155
|
+
"WorkflowWakeOperationError",
|
|
156
|
+
]);
|
|
157
|
+
|
|
158
|
+
const PUBLIC_TELEMETRY_ERROR_CODES = new Set([
|
|
159
|
+
"agent_command_wake_failed",
|
|
160
|
+
"cleared_goal_live_publish_failed",
|
|
161
|
+
"codex_failover_checkpoint_failed",
|
|
162
|
+
"codex_fleet_shadow_failed",
|
|
163
|
+
"codex_lease_loss_checkpoint_failed",
|
|
164
|
+
"codex_active_credential_read_failed",
|
|
165
|
+
"command_yield_timeout",
|
|
166
|
+
"conflict",
|
|
167
|
+
"control_wake_dispatch_failed",
|
|
168
|
+
"fenced_event_live_publish_failed",
|
|
169
|
+
"forbidden",
|
|
170
|
+
"host_export_batch_delivery_failed",
|
|
171
|
+
"host_export_failure_settlement_stale",
|
|
172
|
+
"host_export_pump_iteration_failed",
|
|
173
|
+
"host_export_retention_prune_failed",
|
|
174
|
+
"idempotency_conflict",
|
|
175
|
+
"incompatible_exposed_ports",
|
|
176
|
+
"internal_error",
|
|
177
|
+
"limit_exceeded",
|
|
178
|
+
"mcp_close_failed",
|
|
179
|
+
"mcp_connect_failed",
|
|
180
|
+
"mcp_tool_call_failed",
|
|
181
|
+
"mcp_tools_list_failed",
|
|
182
|
+
"mcp_transport_failed",
|
|
183
|
+
"memory_edit_embedding_failed",
|
|
184
|
+
"memory_hybrid_vector_failed",
|
|
185
|
+
"memory_save_embedding_failed",
|
|
186
|
+
"nats_auth_callout_start_failed",
|
|
187
|
+
"nested_agent_depth_exceeded",
|
|
188
|
+
"nested_agent_depth_override_forbidden",
|
|
189
|
+
"not_found",
|
|
190
|
+
"oauth_operation_failed",
|
|
191
|
+
"otlp_export_failed",
|
|
192
|
+
"payment_required",
|
|
193
|
+
"provider_verification_failed",
|
|
194
|
+
"screenshot_capture_failed",
|
|
195
|
+
"session_event_live_publish_failed",
|
|
196
|
+
"session_workflow_wake_failed",
|
|
197
|
+
"snapshot_operation_failed",
|
|
198
|
+
"startup_dependency_retry",
|
|
199
|
+
"tool_list_too_large",
|
|
200
|
+
"tool_result_too_large",
|
|
201
|
+
"toolspace_operation_failed",
|
|
202
|
+
"unauthenticated",
|
|
203
|
+
"upstream_unavailable",
|
|
204
|
+
"validation_failed",
|
|
205
|
+
"worker_draining",
|
|
206
|
+
"worker_operation_failed",
|
|
207
|
+
"worker_shutdown_request_failed",
|
|
208
|
+
"workspace_control_live_publish_failed",
|
|
209
|
+
]);
|
|
210
|
+
|
|
211
|
+
const PUBLIC_TELEMETRY_ERROR_ORIGINS = new Set([
|
|
212
|
+
"api",
|
|
213
|
+
"core",
|
|
214
|
+
"db",
|
|
215
|
+
"events",
|
|
216
|
+
"host-export",
|
|
217
|
+
"oauth",
|
|
218
|
+
"observability",
|
|
219
|
+
"runtime",
|
|
220
|
+
"sandbox-computer",
|
|
221
|
+
"sandbox-resume",
|
|
222
|
+
"toolspace",
|
|
223
|
+
"worker",
|
|
224
|
+
"worker-lifecycle",
|
|
225
|
+
]);
|
|
226
|
+
|
|
70
227
|
export type SandboxOperationMetricObservation = {
|
|
71
228
|
backend: string;
|
|
72
229
|
op: string;
|
|
73
|
-
outcome: "ok" | "failed";
|
|
230
|
+
outcome: "ok" | "not_found" | "failed";
|
|
74
231
|
durationMs: number;
|
|
75
232
|
};
|
|
76
233
|
|
|
@@ -151,14 +308,14 @@ export class Observability {
|
|
|
151
308
|
message: string,
|
|
152
309
|
attributes: Attributes = {},
|
|
153
310
|
): void {
|
|
311
|
+
const publicAttributes = projectPublicTelemetryAttributes(attributes);
|
|
154
312
|
if (!this.settings.observabilityStructuredLogs) {
|
|
155
|
-
const line = attributes.error ? `${message}: ${String(attributes.error)}` : message;
|
|
156
313
|
if (level === "warn") {
|
|
157
|
-
console.warn(
|
|
314
|
+
console.warn(message);
|
|
158
315
|
} else if (level === "error") {
|
|
159
|
-
console.error(
|
|
316
|
+
console.error(message);
|
|
160
317
|
} else {
|
|
161
|
-
console.log(
|
|
318
|
+
console.log(message);
|
|
162
319
|
}
|
|
163
320
|
return;
|
|
164
321
|
}
|
|
@@ -169,7 +326,7 @@ export class Observability {
|
|
|
169
326
|
service: this.settings.serviceName,
|
|
170
327
|
environment: this.settings.environment,
|
|
171
328
|
component: this.options.component,
|
|
172
|
-
...cleanAttributes(
|
|
329
|
+
...cleanAttributes(publicAttributes),
|
|
173
330
|
};
|
|
174
331
|
const serialized = JSON.stringify(record);
|
|
175
332
|
if (level === "warn") {
|
|
@@ -194,11 +351,11 @@ export class Observability {
|
|
|
194
351
|
return;
|
|
195
352
|
}
|
|
196
353
|
ended = true;
|
|
197
|
-
const
|
|
354
|
+
const telemetryError =
|
|
198
355
|
input.error !== undefined && input.error !== null
|
|
199
|
-
?
|
|
356
|
+
? projectSpanErrorForTelemetry(input.error)
|
|
200
357
|
: undefined;
|
|
201
|
-
const errorAttributes =
|
|
358
|
+
const errorAttributes = telemetryError ? errorToAttributes(telemetryError) : {};
|
|
202
359
|
this.exportSpan({
|
|
203
360
|
traceId,
|
|
204
361
|
spanId,
|
|
@@ -206,11 +363,13 @@ export class Observability {
|
|
|
206
363
|
startMs,
|
|
207
364
|
endMs: this.now(),
|
|
208
365
|
attributes: {
|
|
209
|
-
...
|
|
210
|
-
|
|
211
|
-
|
|
366
|
+
...projectPublicTelemetryAttributes({
|
|
367
|
+
...attributes,
|
|
368
|
+
...input.attributes,
|
|
369
|
+
...errorAttributes,
|
|
370
|
+
}),
|
|
212
371
|
},
|
|
213
|
-
...(
|
|
372
|
+
...(telemetryError ? { error: telemetryError } : {}),
|
|
214
373
|
});
|
|
215
374
|
},
|
|
216
375
|
};
|
|
@@ -408,7 +567,7 @@ export class Observability {
|
|
|
408
567
|
startMs: number;
|
|
409
568
|
endMs: number;
|
|
410
569
|
attributes: Attributes;
|
|
411
|
-
error?:
|
|
570
|
+
error?: TelemetrySpanError;
|
|
412
571
|
}): void {
|
|
413
572
|
if (!this.settings.observabilityOtlpEndpoint) {
|
|
414
573
|
return;
|
|
@@ -444,8 +603,12 @@ export class Observability {
|
|
|
444
603
|
],
|
|
445
604
|
};
|
|
446
605
|
void this.exporter(endpoint, body, parseHeaders(this.settings.observabilityOtlpHeaders)).catch(
|
|
447
|
-
(
|
|
448
|
-
this.warn("OTLP span export failed", {
|
|
606
|
+
() => {
|
|
607
|
+
this.warn("OTLP span export failed", {
|
|
608
|
+
errorClass: "TelemetryExportError",
|
|
609
|
+
errorCode: "otlp_export_failed",
|
|
610
|
+
origin: "observability",
|
|
611
|
+
});
|
|
449
612
|
},
|
|
450
613
|
);
|
|
451
614
|
}
|
|
@@ -509,13 +672,14 @@ export function logStartupDependencyRetry(
|
|
|
509
672
|
observability: Observability,
|
|
510
673
|
event: StartupDependencyRetryEvent,
|
|
511
674
|
): void {
|
|
512
|
-
const message = event.error instanceof Error ? event.error.message : String(event.error);
|
|
513
675
|
observability.warn("Startup dependency connection failed; retrying", {
|
|
514
676
|
dependency: event.label,
|
|
515
677
|
attempt: event.attempt,
|
|
516
678
|
attempts: event.attempts,
|
|
517
679
|
delayMs: event.delayMs,
|
|
518
|
-
|
|
680
|
+
errorClass: "StartupDependencyError",
|
|
681
|
+
errorCode: "startup_dependency_retry",
|
|
682
|
+
origin: "observability",
|
|
519
683
|
});
|
|
520
684
|
}
|
|
521
685
|
|
|
@@ -538,20 +702,51 @@ function cleanAttributes(attributes: Attributes): Record<string, string | number
|
|
|
538
702
|
) as Record<string, string | number | boolean | null>;
|
|
539
703
|
}
|
|
540
704
|
|
|
541
|
-
|
|
705
|
+
function projectPublicTelemetryAttributes(attributes: Attributes): Attributes {
|
|
706
|
+
if ("errorClass" in attributes || "errorCode" in attributes) {
|
|
707
|
+
return projectPublicDiagnosticAttributes(attributes);
|
|
708
|
+
}
|
|
709
|
+
return Object.fromEntries(
|
|
710
|
+
Object.entries(attributes).filter(
|
|
711
|
+
([key, value]) => value !== undefined && PUBLIC_TELEMETRY_ATTRIBUTE_KEYS.has(key),
|
|
712
|
+
),
|
|
713
|
+
);
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
function projectPublicDiagnosticAttributes(attributes: Attributes): Attributes {
|
|
717
|
+
const errorClass = attributes.errorClass;
|
|
718
|
+
const errorCode = attributes.errorCode;
|
|
719
|
+
const status = attributes.status;
|
|
720
|
+
const origin = attributes.origin;
|
|
721
|
+
return {
|
|
722
|
+
errorClass:
|
|
723
|
+
typeof errorClass === "string" && PUBLIC_TELEMETRY_ERROR_CLASSES.has(errorClass)
|
|
724
|
+
? errorClass
|
|
725
|
+
: "OperationError",
|
|
726
|
+
...(typeof errorCode === "string" && PUBLIC_TELEMETRY_ERROR_CODES.has(errorCode)
|
|
727
|
+
? { errorCode }
|
|
728
|
+
: {}),
|
|
729
|
+
...(typeof status === "number" && Number.isInteger(status) && status >= 100 && status <= 599
|
|
730
|
+
? { status }
|
|
731
|
+
: {}),
|
|
732
|
+
...(typeof origin === "string" && PUBLIC_TELEMETRY_ERROR_ORIGINS.has(origin) ? { origin } : {}),
|
|
733
|
+
};
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
type TelemetrySpanError = {
|
|
542
737
|
type: string;
|
|
543
738
|
statusCode?: number;
|
|
544
739
|
statusMessage: string;
|
|
545
740
|
};
|
|
546
741
|
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
742
|
+
/**
|
|
743
|
+
* External OTLP projection. It intentionally exports only error class/status
|
|
744
|
+
* metadata and never mutates canonical OpenGeni errors, events, or history.
|
|
745
|
+
*/
|
|
746
|
+
function projectSpanErrorForTelemetry(error: unknown): TelemetrySpanError {
|
|
552
747
|
const statusCode = errorStatusCode(error);
|
|
553
748
|
return {
|
|
554
|
-
type,
|
|
749
|
+
type: "OperationError",
|
|
555
750
|
...(statusCode === undefined ? {} : { statusCode }),
|
|
556
751
|
statusMessage: statusCode === undefined ? "operation failed" : `HTTP ${statusCode}`,
|
|
557
752
|
};
|
|
@@ -559,30 +754,31 @@ function sanitizeSpanError(error: unknown): SanitizedSpanError {
|
|
|
559
754
|
|
|
560
755
|
function errorStatusCode(error: unknown): number | undefined {
|
|
561
756
|
if (typeof error !== "object" || error === null) return undefined;
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
757
|
+
try {
|
|
758
|
+
const value = (error as { status?: unknown; statusCode?: unknown }).status;
|
|
759
|
+
const statusCode =
|
|
760
|
+
Number.isInteger(value) && typeof value === "number"
|
|
761
|
+
? value
|
|
762
|
+
: (error as { statusCode?: unknown }).statusCode;
|
|
763
|
+
return Number.isInteger(statusCode) &&
|
|
764
|
+
typeof statusCode === "number" &&
|
|
765
|
+
statusCode >= 100 &&
|
|
766
|
+
statusCode <= 599
|
|
767
|
+
? statusCode
|
|
768
|
+
: undefined;
|
|
769
|
+
} catch {
|
|
770
|
+
// OTLP projection must never mask the exact internal failure.
|
|
771
|
+
return undefined;
|
|
772
|
+
}
|
|
573
773
|
}
|
|
574
774
|
|
|
575
|
-
function errorToAttributes(error:
|
|
775
|
+
function errorToAttributes(error: TelemetrySpanError): Attributes {
|
|
576
776
|
return {
|
|
577
777
|
"error.type": error.type,
|
|
578
778
|
...(error.statusCode === undefined ? {} : { "error.status_code": error.statusCode }),
|
|
579
779
|
};
|
|
580
780
|
}
|
|
581
781
|
|
|
582
|
-
function errorMessage(error: unknown): string {
|
|
583
|
-
return error instanceof Error ? error.message : String(error);
|
|
584
|
-
}
|
|
585
|
-
|
|
586
782
|
function otlpAttributes(
|
|
587
783
|
attributes: Attributes,
|
|
588
784
|
): Array<{ key: string; value: Record<string, string | number | boolean> }> {
|