@opengeni/observability 0.3.0 → 0.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +29 -13
- package/dist/index.js +135 -41
- package/dist/index.js.map +1 -1
- package/package.json +13 -12
- package/src/index.ts +256 -70
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
type AttributeValue = string | number | boolean | null | undefined;
|
|
2
|
-
type Attributes = Record<string, AttributeValue>;
|
|
3
|
-
type ObservabilitySettings = {
|
|
1
|
+
export type AttributeValue = string | number | boolean | null | undefined;
|
|
2
|
+
export type Attributes = Record<string, AttributeValue>;
|
|
3
|
+
export type ObservabilitySettings = {
|
|
4
4
|
serviceName: string;
|
|
5
5
|
environment: string;
|
|
6
6
|
deploymentRevision?: string | undefined;
|
|
@@ -9,12 +9,12 @@ type ObservabilitySettings = {
|
|
|
9
9
|
observabilityOtlpEndpoint?: string | undefined;
|
|
10
10
|
observabilityOtlpHeaders: string;
|
|
11
11
|
};
|
|
12
|
-
type ObservabilityOptions = {
|
|
12
|
+
export type ObservabilityOptions = {
|
|
13
13
|
component: string;
|
|
14
14
|
now?: () => number;
|
|
15
15
|
exporter?: (url: string, body: unknown, headers: Record<string, string>) => Promise<void>;
|
|
16
16
|
};
|
|
17
|
-
type Span = {
|
|
17
|
+
export type Span = {
|
|
18
18
|
traceId: string;
|
|
19
19
|
spanId: string;
|
|
20
20
|
end: (input?: {
|
|
@@ -22,9 +22,15 @@ type Span = {
|
|
|
22
22
|
error?: unknown;
|
|
23
23
|
}) => void;
|
|
24
24
|
};
|
|
25
|
-
type MetricLabels = Record<string, AttributeValue>;
|
|
26
|
-
|
|
27
|
-
|
|
25
|
+
export type MetricLabels = Record<string, AttributeValue>;
|
|
26
|
+
export type SandboxOperationMetricObservation = {
|
|
27
|
+
backend: string;
|
|
28
|
+
op: string;
|
|
29
|
+
outcome: "ok" | "failed";
|
|
30
|
+
durationMs: number;
|
|
31
|
+
};
|
|
32
|
+
export declare function createObservability(settings: ObservabilitySettings, options: ObservabilityOptions): Observability;
|
|
33
|
+
export declare class Observability {
|
|
28
34
|
private readonly settings;
|
|
29
35
|
private readonly options;
|
|
30
36
|
private readonly registry;
|
|
@@ -86,14 +92,24 @@ declare class Observability {
|
|
|
86
92
|
private assertRegistration;
|
|
87
93
|
private exportSpan;
|
|
88
94
|
}
|
|
89
|
-
|
|
95
|
+
/**
|
|
96
|
+
* Convert routed sandbox-provider observations into one bounded Prometheus
|
|
97
|
+
* contract shared by API-direct and worker-turn execution. Provider/session
|
|
98
|
+
* identifiers, commands, paths, and other request data can never become labels:
|
|
99
|
+
* a future backend or operation is deliberately collapsed to `unknown` until
|
|
100
|
+
* this allowlist is reviewed.
|
|
101
|
+
*
|
|
102
|
+
* The returned callback is fail-safe. Metrics must never change the provider
|
|
103
|
+
* operation's result; a registration/exporter error is counted best-effort and
|
|
104
|
+
* then swallowed at this telemetry boundary.
|
|
105
|
+
*/
|
|
106
|
+
export declare function sandboxOperationMetricObserver(observability: Observability): (observation: SandboxOperationMetricObservation) => void;
|
|
107
|
+
export type StartupDependencyRetryEvent = {
|
|
90
108
|
label: string;
|
|
91
109
|
attempt: number;
|
|
92
110
|
attempts: number;
|
|
93
111
|
delayMs: number;
|
|
94
112
|
error: unknown;
|
|
95
113
|
};
|
|
96
|
-
declare function logStartupDependencyRetry(observability: Observability, event: StartupDependencyRetryEvent): void;
|
|
97
|
-
declare function parseHeaders(value: string): Record<string, string>;
|
|
98
|
-
|
|
99
|
-
export { type AttributeValue, type Attributes, type MetricLabels, Observability, type ObservabilityOptions, type ObservabilitySettings, type Span, type StartupDependencyRetryEvent, createObservability, logStartupDependencyRetry, parseHeaders };
|
|
114
|
+
export declare function logStartupDependencyRetry(observability: Observability, event: StartupDependencyRetryEvent): void;
|
|
115
|
+
export declare function parseHeaders(value: string): Record<string, string>;
|
package/dist/index.js
CHANGED
|
@@ -1,13 +1,45 @@
|
|
|
1
1
|
// src/index.ts
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
Counter,
|
|
5
|
-
Gauge,
|
|
6
|
-
Histogram,
|
|
7
|
-
Registry
|
|
8
|
-
} from "prom-client";
|
|
2
|
+
import { collectDefaultMetrics, Counter, Gauge, Histogram, Registry } from "prom-client";
|
|
3
|
+
import { SandboxBackend } from "@opengeni/contracts";
|
|
9
4
|
var httpHistogramBuckets = [5e-3, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10];
|
|
10
|
-
var durationHistogramBuckets = [
|
|
5
|
+
var durationHistogramBuckets = [
|
|
6
|
+
0.01,
|
|
7
|
+
0.05,
|
|
8
|
+
0.1,
|
|
9
|
+
0.25,
|
|
10
|
+
0.5,
|
|
11
|
+
1,
|
|
12
|
+
2.5,
|
|
13
|
+
5,
|
|
14
|
+
10,
|
|
15
|
+
30,
|
|
16
|
+
60,
|
|
17
|
+
120,
|
|
18
|
+
300,
|
|
19
|
+
900,
|
|
20
|
+
1800,
|
|
21
|
+
3600
|
|
22
|
+
];
|
|
23
|
+
var SANDBOX_OPERATION_BACKENDS = /* @__PURE__ */ new Set([...SandboxBackend.options, "unprovisioned"]);
|
|
24
|
+
var SANDBOX_OPERATION_NAMES = /* @__PURE__ */ new Set([
|
|
25
|
+
"desktopInput",
|
|
26
|
+
"screenshot",
|
|
27
|
+
"exec",
|
|
28
|
+
"execCommand",
|
|
29
|
+
"writeStdin",
|
|
30
|
+
"cancelExecCommand",
|
|
31
|
+
"readFile",
|
|
32
|
+
"writeFile",
|
|
33
|
+
"listDir",
|
|
34
|
+
"pathExists",
|
|
35
|
+
"viewImage",
|
|
36
|
+
"materializeEntry",
|
|
37
|
+
"editor.createFile",
|
|
38
|
+
"editor.updateFile",
|
|
39
|
+
"editor.deleteFile",
|
|
40
|
+
"resolveExposedPort",
|
|
41
|
+
"serializeSessionState"
|
|
42
|
+
]);
|
|
11
43
|
function createObservability(settings, options) {
|
|
12
44
|
return new Observability(settings, options);
|
|
13
45
|
}
|
|
@@ -40,8 +72,6 @@ var Observability = class {
|
|
|
40
72
|
});
|
|
41
73
|
}
|
|
42
74
|
}
|
|
43
|
-
settings;
|
|
44
|
-
options;
|
|
45
75
|
registry = new Registry();
|
|
46
76
|
counters = /* @__PURE__ */ new Map();
|
|
47
77
|
gauges = /* @__PURE__ */ new Map();
|
|
@@ -105,7 +135,8 @@ var Observability = class {
|
|
|
105
135
|
return;
|
|
106
136
|
}
|
|
107
137
|
ended = true;
|
|
108
|
-
const
|
|
138
|
+
const sanitizedError = input.error !== void 0 && input.error !== null ? sanitizeSpanError(input.error) : void 0;
|
|
139
|
+
const errorAttributes = sanitizedError ? errorToAttributes(sanitizedError) : {};
|
|
109
140
|
this.exportSpan({
|
|
110
141
|
traceId,
|
|
111
142
|
spanId,
|
|
@@ -117,7 +148,7 @@ var Observability = class {
|
|
|
117
148
|
...input.attributes,
|
|
118
149
|
...errorAttributes
|
|
119
150
|
},
|
|
120
|
-
error:
|
|
151
|
+
...sanitizedError ? { error: sanitizedError } : {}
|
|
121
152
|
});
|
|
122
153
|
}
|
|
123
154
|
};
|
|
@@ -171,7 +202,11 @@ var Observability = class {
|
|
|
171
202
|
return;
|
|
172
203
|
}
|
|
173
204
|
const labels = normalizeLabels(input.labels);
|
|
174
|
-
const counter = this.counter(
|
|
205
|
+
const counter = this.counter(
|
|
206
|
+
input.name,
|
|
207
|
+
input.help ?? `${input.name} counter.`,
|
|
208
|
+
Object.keys(labels)
|
|
209
|
+
);
|
|
175
210
|
counter.inc(labels, input.amount ?? 1);
|
|
176
211
|
}
|
|
177
212
|
setGauge(input) {
|
|
@@ -261,33 +296,69 @@ var Observability = class {
|
|
|
261
296
|
}
|
|
262
297
|
const endpoint = `${this.settings.observabilityOtlpEndpoint.replace(/\/$/, "")}/v1/traces`;
|
|
263
298
|
const body = {
|
|
264
|
-
resourceSpans: [
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
scopeSpans: [{
|
|
269
|
-
scope: {
|
|
270
|
-
name: "@opengeni/observability",
|
|
271
|
-
version: "0.1.0"
|
|
299
|
+
resourceSpans: [
|
|
300
|
+
{
|
|
301
|
+
resource: {
|
|
302
|
+
attributes: otlpAttributes(this.resourceAttributes)
|
|
272
303
|
},
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
304
|
+
scopeSpans: [
|
|
305
|
+
{
|
|
306
|
+
scope: {
|
|
307
|
+
name: "@opengeni/observability",
|
|
308
|
+
version: "0.1.0"
|
|
309
|
+
},
|
|
310
|
+
spans: [
|
|
311
|
+
{
|
|
312
|
+
traceId: span.traceId,
|
|
313
|
+
spanId: span.spanId,
|
|
314
|
+
name: span.name,
|
|
315
|
+
kind: 1,
|
|
316
|
+
startTimeUnixNano: millisToNanos(span.startMs),
|
|
317
|
+
endTimeUnixNano: millisToNanos(span.endMs),
|
|
318
|
+
attributes: otlpAttributes(span.attributes),
|
|
319
|
+
status: span.error ? { code: 2, message: span.error.statusMessage } : { code: 1 }
|
|
320
|
+
}
|
|
321
|
+
]
|
|
322
|
+
}
|
|
323
|
+
]
|
|
324
|
+
}
|
|
325
|
+
]
|
|
285
326
|
};
|
|
286
|
-
void this.exporter(endpoint, body, parseHeaders(this.settings.observabilityOtlpHeaders)).catch(
|
|
287
|
-
|
|
288
|
-
|
|
327
|
+
void this.exporter(endpoint, body, parseHeaders(this.settings.observabilityOtlpHeaders)).catch(
|
|
328
|
+
(error) => {
|
|
329
|
+
this.warn("OTLP span export failed", { error: errorMessage(error), endpoint });
|
|
330
|
+
}
|
|
331
|
+
);
|
|
289
332
|
}
|
|
290
333
|
};
|
|
334
|
+
function sandboxOperationMetricObserver(observability) {
|
|
335
|
+
return (observation) => {
|
|
336
|
+
const backend = SANDBOX_OPERATION_BACKENDS.has(observation.backend) ? observation.backend : "unknown";
|
|
337
|
+
const op = SANDBOX_OPERATION_NAMES.has(observation.op) ? observation.op : "unknown";
|
|
338
|
+
try {
|
|
339
|
+
observability.incrementCounter({
|
|
340
|
+
name: "opengeni_sandbox_operations_total",
|
|
341
|
+
help: "Physical routed sandbox provider operations by backend, operation, and outcome.",
|
|
342
|
+
labels: { backend, op, outcome: observation.outcome }
|
|
343
|
+
});
|
|
344
|
+
observability.observeHistogram({
|
|
345
|
+
name: "opengeni_sandbox_operation_duration_seconds",
|
|
346
|
+
help: "Physical routed sandbox provider-operation duration in seconds.",
|
|
347
|
+
labels: { backend, op },
|
|
348
|
+
value: Math.max(0, observation.durationMs) / 1e3
|
|
349
|
+
});
|
|
350
|
+
} catch {
|
|
351
|
+
try {
|
|
352
|
+
observability.incrementCounter({
|
|
353
|
+
name: "opengeni_observability_observer_errors_total",
|
|
354
|
+
help: "Observability observer failures isolated from product execution.",
|
|
355
|
+
labels: { observer: "sandbox_operation" }
|
|
356
|
+
});
|
|
357
|
+
} catch {
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
};
|
|
361
|
+
}
|
|
291
362
|
function logStartupDependencyRetry(observability, event) {
|
|
292
363
|
const message = event.error instanceof Error ? event.error.message : String(event.error);
|
|
293
364
|
observability.warn("Startup dependency connection failed; retrying", {
|
|
@@ -307,12 +378,29 @@ function buildVersion() {
|
|
|
307
378
|
return process.env.OPENGENI_VERSION ?? process.env.npm_package_version ?? "dev";
|
|
308
379
|
}
|
|
309
380
|
function cleanAttributes(attributes) {
|
|
310
|
-
return Object.fromEntries(
|
|
381
|
+
return Object.fromEntries(
|
|
382
|
+
Object.entries(attributes).filter(([, value]) => value !== void 0)
|
|
383
|
+
);
|
|
384
|
+
}
|
|
385
|
+
function sanitizeSpanError(error) {
|
|
386
|
+
const type = error instanceof Error && /^[A-Za-z][A-Za-z0-9_.-]{0,63}$/.test(error.name) ? error.name : "Error";
|
|
387
|
+
const statusCode = errorStatusCode(error);
|
|
388
|
+
return {
|
|
389
|
+
type,
|
|
390
|
+
...statusCode === void 0 ? {} : { statusCode },
|
|
391
|
+
statusMessage: statusCode === void 0 ? "operation failed" : `HTTP ${statusCode}`
|
|
392
|
+
};
|
|
393
|
+
}
|
|
394
|
+
function errorStatusCode(error) {
|
|
395
|
+
if (typeof error !== "object" || error === null) return void 0;
|
|
396
|
+
const value = error.status;
|
|
397
|
+
const statusCode = Number.isInteger(value) && typeof value === "number" ? value : error.statusCode;
|
|
398
|
+
return Number.isInteger(statusCode) && typeof statusCode === "number" && statusCode >= 100 && statusCode <= 599 ? statusCode : void 0;
|
|
311
399
|
}
|
|
312
400
|
function errorToAttributes(error) {
|
|
313
401
|
return {
|
|
314
|
-
"error.type": error
|
|
315
|
-
"error.
|
|
402
|
+
"error.type": error.type,
|
|
403
|
+
...error.statusCode === void 0 ? {} : { "error.status_code": error.statusCode }
|
|
316
404
|
};
|
|
317
405
|
}
|
|
318
406
|
function errorMessage(error) {
|
|
@@ -331,7 +419,12 @@ function otlpValue(value) {
|
|
|
331
419
|
if (typeof value === "boolean") {
|
|
332
420
|
return { boolValue: value };
|
|
333
421
|
}
|
|
334
|
-
return { stringValue: value === null ? "" : value };
|
|
422
|
+
return { stringValue: value === null ? "" : boundedOtlpString(value) };
|
|
423
|
+
}
|
|
424
|
+
function boundedOtlpString(value) {
|
|
425
|
+
const bytes = new TextEncoder().encode(value);
|
|
426
|
+
if (bytes.byteLength <= 512) return value;
|
|
427
|
+
return `${new TextDecoder().decode(bytes.slice(0, 509))}\u2026`;
|
|
335
428
|
}
|
|
336
429
|
function millisToNanos(ms) {
|
|
337
430
|
return String(BigInt(Math.round(ms)) * 1000000n);
|
|
@@ -370,6 +463,7 @@ export {
|
|
|
370
463
|
Observability,
|
|
371
464
|
createObservability,
|
|
372
465
|
logStartupDependencyRetry,
|
|
373
|
-
parseHeaders
|
|
466
|
+
parseHeaders,
|
|
467
|
+
sandboxOperationMetricObserver
|
|
374
468
|
};
|
|
375
469
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["import {\n collectDefaultMetrics,\n Counter,\n Gauge,\n Histogram,\n Registry,\n} from \"prom-client\";\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\nconst httpHistogramBuckets = [0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10];\nconst durationHistogramBuckets = [0.01, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10, 30, 60, 120, 300, 900, 1800, 3600];\n\nexport function createObservability(settings: ObservabilitySettings, options: ObservabilityOptions): 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: (url: string, body: unknown, headers: Record<string, string>) => Promise<void>;\n private readonly resourceAttributes: Attributes;\n\n constructor(private readonly settings: ObservabilitySettings, private readonly options: ObservabilityOptions) {\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(level: \"debug\" | \"info\" | \"warn\" | \"error\", message: string, attributes: Attributes = {}): 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 errorAttributes = input.error ? errorToAttributes(input.error) : {};\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 error: input.error,\n });\n },\n };\n }\n\n recordHttpRequest(input: { method: string; route: string; status: number; durationSeconds: number }): 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: { name: string; help?: string; labels?: MetricLabels; amount?: number }): void {\n if (!this.settings.observabilityMetricsEnabled) {\n return;\n }\n const labels = normalizeLabels(input.labels);\n const counter = this.counter(input.name, input.help ?? `${input.name} counter.`, Object.keys(labels));\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: { name: string; help?: string; labels?: MetricLabels; amount?: 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.inc(labels as never, input.amount ?? 1);\n }\n\n observeHistogram(input: { name: string; help?: string; labels?: MetricLabels; value: number; buckets?: number[] }): 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(name: string, help: string, labelNames: string[], buckets: number[]): 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(name: string, kind: MetricRegistration[\"kind\"], labelNames: string[]): 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?: unknown;\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 resource: {\n attributes: otlpAttributes(this.resourceAttributes),\n },\n scopeSpans: [{\n scope: {\n name: \"@opengeni/observability\",\n version: \"0.1.0\",\n },\n spans: [{\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: errorMessage(span.error) } : { code: 1 },\n }],\n }],\n }],\n };\n void this.exporter(endpoint, body, parseHeaders(this.settings.observabilityOtlpHeaders)).catch((error) => {\n this.warn(\"OTLP span export failed\", { error: errorMessage(error), endpoint });\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(observability: Observability, event: StartupDependencyRetryEvent): 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\n ?? process.env.npm_package_version\n ?? \"dev\";\n}\n\nfunction cleanAttributes(attributes: Attributes): Record<string, string | number | boolean | null> {\n return Object.fromEntries(Object.entries(attributes).filter(([, value]) => value !== undefined)) as Record<string, string | number | boolean | null>;\n}\n\nfunction errorToAttributes(error: unknown): Attributes {\n return {\n \"error.type\": error instanceof Error ? error.name : \"Error\",\n \"error.message\": errorMessage(error),\n };\n}\n\nfunction errorMessage(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n\nfunction otlpAttributes(attributes: Attributes): 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(value: string | number | boolean | null): 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 ? \"\" : value };\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.split(\",\").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 }).filter(([key]) => key.length > 0);\n return Object.fromEntries(entries);\n}\n\nasync function defaultExporter(url: string, body: unknown, headers: Record<string, string>): 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\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":[]}
|
package/package.json
CHANGED
|
@@ -1,8 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@opengeni/observability",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"type": "module",
|
|
3
|
+
"version": "0.4.2",
|
|
5
4
|
"license": "Apache-2.0",
|
|
5
|
+
"repository": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"url": "git+https://github.com/Cloudgeni-ai/opengeni.git",
|
|
8
|
+
"directory": "packages/observability"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"dist",
|
|
12
|
+
"src"
|
|
13
|
+
],
|
|
14
|
+
"type": "module",
|
|
6
15
|
"main": "./dist/index.js",
|
|
7
16
|
"module": "./dist/index.js",
|
|
8
17
|
"types": "./dist/index.d.ts",
|
|
@@ -12,25 +21,17 @@
|
|
|
12
21
|
"import": "./dist/index.js"
|
|
13
22
|
}
|
|
14
23
|
},
|
|
15
|
-
"files": [
|
|
16
|
-
"dist",
|
|
17
|
-
"src"
|
|
18
|
-
],
|
|
19
24
|
"publishConfig": {
|
|
20
25
|
"access": "public",
|
|
21
26
|
"provenance": true
|
|
22
27
|
},
|
|
23
28
|
"scripts": {
|
|
24
|
-
"build": "
|
|
29
|
+
"build": "bun ../../scripts/build-typescript-package.ts",
|
|
25
30
|
"typecheck": "tsc --noEmit",
|
|
26
31
|
"prepublishOnly": "bash ../../scripts/prepublish-guard"
|
|
27
32
|
},
|
|
28
|
-
"repository": {
|
|
29
|
-
"type": "git",
|
|
30
|
-
"url": "git+https://github.com/Cloudgeni-ai/opengeni.git",
|
|
31
|
-
"directory": "packages/observability"
|
|
32
|
-
},
|
|
33
33
|
"dependencies": {
|
|
34
|
+
"@opengeni/contracts": "^0.30.0",
|
|
34
35
|
"prom-client": "^15.1.3"
|
|
35
36
|
}
|
|
36
37
|
}
|
package/src/index.ts
CHANGED
|
@@ -1,10 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
Counter,
|
|
4
|
-
Gauge,
|
|
5
|
-
Histogram,
|
|
6
|
-
Registry,
|
|
7
|
-
} from "prom-client";
|
|
1
|
+
import { collectDefaultMetrics, Counter, Gauge, Histogram, Registry } from "prom-client";
|
|
2
|
+
import { SandboxBackend } from "@opengeni/contracts";
|
|
8
3
|
|
|
9
4
|
export type AttributeValue = string | number | boolean | null | undefined;
|
|
10
5
|
export type Attributes = Record<string, AttributeValue>;
|
|
@@ -34,9 +29,43 @@ export type Span = {
|
|
|
34
29
|
export type MetricLabels = Record<string, AttributeValue>;
|
|
35
30
|
|
|
36
31
|
const httpHistogramBuckets = [0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10];
|
|
37
|
-
const durationHistogramBuckets = [
|
|
32
|
+
const durationHistogramBuckets = [
|
|
33
|
+
0.01, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10, 30, 60, 120, 300, 900, 1800, 3600,
|
|
34
|
+
];
|
|
35
|
+
|
|
36
|
+
const SANDBOX_OPERATION_BACKENDS = new Set<string>([...SandboxBackend.options, "unprovisioned"]);
|
|
37
|
+
|
|
38
|
+
const SANDBOX_OPERATION_NAMES = new Set([
|
|
39
|
+
"desktopInput",
|
|
40
|
+
"screenshot",
|
|
41
|
+
"exec",
|
|
42
|
+
"execCommand",
|
|
43
|
+
"writeStdin",
|
|
44
|
+
"cancelExecCommand",
|
|
45
|
+
"readFile",
|
|
46
|
+
"writeFile",
|
|
47
|
+
"listDir",
|
|
48
|
+
"pathExists",
|
|
49
|
+
"viewImage",
|
|
50
|
+
"materializeEntry",
|
|
51
|
+
"editor.createFile",
|
|
52
|
+
"editor.updateFile",
|
|
53
|
+
"editor.deleteFile",
|
|
54
|
+
"resolveExposedPort",
|
|
55
|
+
"serializeSessionState",
|
|
56
|
+
]);
|
|
57
|
+
|
|
58
|
+
export type SandboxOperationMetricObservation = {
|
|
59
|
+
backend: string;
|
|
60
|
+
op: string;
|
|
61
|
+
outcome: "ok" | "failed";
|
|
62
|
+
durationMs: number;
|
|
63
|
+
};
|
|
38
64
|
|
|
39
|
-
export function createObservability(
|
|
65
|
+
export function createObservability(
|
|
66
|
+
settings: ObservabilitySettings,
|
|
67
|
+
options: ObservabilityOptions,
|
|
68
|
+
): Observability {
|
|
40
69
|
return new Observability(settings, options);
|
|
41
70
|
}
|
|
42
71
|
|
|
@@ -52,10 +81,17 @@ export class Observability {
|
|
|
52
81
|
private readonly histograms = new Map<string, Histogram<string>>();
|
|
53
82
|
private readonly registrations = new Map<string, MetricRegistration>();
|
|
54
83
|
private readonly now: () => number;
|
|
55
|
-
private readonly exporter: (
|
|
84
|
+
private readonly exporter: (
|
|
85
|
+
url: string,
|
|
86
|
+
body: unknown,
|
|
87
|
+
headers: Record<string, string>,
|
|
88
|
+
) => Promise<void>;
|
|
56
89
|
private readonly resourceAttributes: Attributes;
|
|
57
90
|
|
|
58
|
-
constructor(
|
|
91
|
+
constructor(
|
|
92
|
+
private readonly settings: ObservabilitySettings,
|
|
93
|
+
private readonly options: ObservabilityOptions,
|
|
94
|
+
) {
|
|
59
95
|
this.now = options.now ?? Date.now;
|
|
60
96
|
this.exporter = options.exporter ?? defaultExporter;
|
|
61
97
|
this.resourceAttributes = {
|
|
@@ -98,7 +134,11 @@ export class Observability {
|
|
|
98
134
|
this.log("error", message, attributes);
|
|
99
135
|
}
|
|
100
136
|
|
|
101
|
-
log(
|
|
137
|
+
log(
|
|
138
|
+
level: "debug" | "info" | "warn" | "error",
|
|
139
|
+
message: string,
|
|
140
|
+
attributes: Attributes = {},
|
|
141
|
+
): void {
|
|
102
142
|
if (!this.settings.observabilityStructuredLogs) {
|
|
103
143
|
const line = attributes.error ? `${message}: ${String(attributes.error)}` : message;
|
|
104
144
|
if (level === "warn") {
|
|
@@ -142,7 +182,11 @@ export class Observability {
|
|
|
142
182
|
return;
|
|
143
183
|
}
|
|
144
184
|
ended = true;
|
|
145
|
-
const
|
|
185
|
+
const sanitizedError =
|
|
186
|
+
input.error !== undefined && input.error !== null
|
|
187
|
+
? sanitizeSpanError(input.error)
|
|
188
|
+
: undefined;
|
|
189
|
+
const errorAttributes = sanitizedError ? errorToAttributes(sanitizedError) : {};
|
|
146
190
|
this.exportSpan({
|
|
147
191
|
traceId,
|
|
148
192
|
spanId,
|
|
@@ -154,13 +198,18 @@ export class Observability {
|
|
|
154
198
|
...input.attributes,
|
|
155
199
|
...errorAttributes,
|
|
156
200
|
},
|
|
157
|
-
error:
|
|
201
|
+
...(sanitizedError ? { error: sanitizedError } : {}),
|
|
158
202
|
});
|
|
159
203
|
},
|
|
160
204
|
};
|
|
161
205
|
}
|
|
162
206
|
|
|
163
|
-
recordHttpRequest(input: {
|
|
207
|
+
recordHttpRequest(input: {
|
|
208
|
+
method: string;
|
|
209
|
+
route: string;
|
|
210
|
+
status: number;
|
|
211
|
+
durationSeconds: number;
|
|
212
|
+
}): void {
|
|
164
213
|
this.incrementCounter({
|
|
165
214
|
name: "opengeni_http_requests_total",
|
|
166
215
|
help: "Total HTTP requests handled by OpenGeni.",
|
|
@@ -206,12 +255,21 @@ export class Observability {
|
|
|
206
255
|
});
|
|
207
256
|
}
|
|
208
257
|
|
|
209
|
-
incrementCounter(input: {
|
|
258
|
+
incrementCounter(input: {
|
|
259
|
+
name: string;
|
|
260
|
+
help?: string;
|
|
261
|
+
labels?: MetricLabels;
|
|
262
|
+
amount?: number;
|
|
263
|
+
}): void {
|
|
210
264
|
if (!this.settings.observabilityMetricsEnabled) {
|
|
211
265
|
return;
|
|
212
266
|
}
|
|
213
267
|
const labels = normalizeLabels(input.labels);
|
|
214
|
-
const counter = this.counter(
|
|
268
|
+
const counter = this.counter(
|
|
269
|
+
input.name,
|
|
270
|
+
input.help ?? `${input.name} counter.`,
|
|
271
|
+
Object.keys(labels),
|
|
272
|
+
);
|
|
215
273
|
counter.inc(labels as never, input.amount ?? 1);
|
|
216
274
|
}
|
|
217
275
|
|
|
@@ -224,7 +282,12 @@ export class Observability {
|
|
|
224
282
|
gauge.set(labels as never, input.value);
|
|
225
283
|
}
|
|
226
284
|
|
|
227
|
-
incrementGauge(input: {
|
|
285
|
+
incrementGauge(input: {
|
|
286
|
+
name: string;
|
|
287
|
+
help?: string;
|
|
288
|
+
labels?: MetricLabels;
|
|
289
|
+
amount?: number;
|
|
290
|
+
}): void {
|
|
228
291
|
if (!this.settings.observabilityMetricsEnabled) {
|
|
229
292
|
return;
|
|
230
293
|
}
|
|
@@ -233,7 +296,13 @@ export class Observability {
|
|
|
233
296
|
gauge.inc(labels as never, input.amount ?? 1);
|
|
234
297
|
}
|
|
235
298
|
|
|
236
|
-
observeHistogram(input: {
|
|
299
|
+
observeHistogram(input: {
|
|
300
|
+
name: string;
|
|
301
|
+
help?: string;
|
|
302
|
+
labels?: MetricLabels;
|
|
303
|
+
value: number;
|
|
304
|
+
buckets?: number[];
|
|
305
|
+
}): void {
|
|
237
306
|
if (!this.settings.observabilityMetricsEnabled) {
|
|
238
307
|
return;
|
|
239
308
|
}
|
|
@@ -278,7 +347,12 @@ export class Observability {
|
|
|
278
347
|
return metric;
|
|
279
348
|
}
|
|
280
349
|
|
|
281
|
-
private histogram(
|
|
350
|
+
private histogram(
|
|
351
|
+
name: string,
|
|
352
|
+
help: string,
|
|
353
|
+
labelNames: string[],
|
|
354
|
+
buckets: number[],
|
|
355
|
+
): Histogram<string> {
|
|
282
356
|
const existing = this.histograms.get(name);
|
|
283
357
|
if (existing) {
|
|
284
358
|
this.assertRegistration(name, "histogram", labelNames);
|
|
@@ -295,18 +369,22 @@ export class Observability {
|
|
|
295
369
|
this.registrations.set(name, { kind, labelNames: sorted });
|
|
296
370
|
}
|
|
297
371
|
|
|
298
|
-
private assertRegistration(
|
|
372
|
+
private assertRegistration(
|
|
373
|
+
name: string,
|
|
374
|
+
kind: MetricRegistration["kind"],
|
|
375
|
+
labelNames: string[],
|
|
376
|
+
): void {
|
|
299
377
|
const registration = this.registrations.get(name);
|
|
300
378
|
const sorted = [...labelNames].sort();
|
|
301
379
|
if (
|
|
302
|
-
!registration
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
380
|
+
!registration ||
|
|
381
|
+
registration.kind !== kind ||
|
|
382
|
+
registration.labelNames.length !== sorted.length ||
|
|
383
|
+
registration.labelNames.some((label, index) => label !== sorted[index])
|
|
306
384
|
) {
|
|
307
385
|
throw new Error(
|
|
308
|
-
`Metric ${name} was already registered as ${registration?.kind ?? "unknown"} `
|
|
309
|
-
|
|
386
|
+
`Metric ${name} was already registered as ${registration?.kind ?? "unknown"} ` +
|
|
387
|
+
`with labels [${registration?.labelNames.join(",") ?? ""}], not ${kind} [${sorted.join(",")}]`,
|
|
310
388
|
);
|
|
311
389
|
}
|
|
312
390
|
}
|
|
@@ -318,41 +396,95 @@ export class Observability {
|
|
|
318
396
|
startMs: number;
|
|
319
397
|
endMs: number;
|
|
320
398
|
attributes: Attributes;
|
|
321
|
-
error?:
|
|
399
|
+
error?: SanitizedSpanError;
|
|
322
400
|
}): void {
|
|
323
401
|
if (!this.settings.observabilityOtlpEndpoint) {
|
|
324
402
|
return;
|
|
325
403
|
}
|
|
326
404
|
const endpoint = `${this.settings.observabilityOtlpEndpoint.replace(/\/$/, "")}/v1/traces`;
|
|
327
405
|
const body = {
|
|
328
|
-
resourceSpans: [
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
scopeSpans: [{
|
|
333
|
-
scope: {
|
|
334
|
-
name: "@opengeni/observability",
|
|
335
|
-
version: "0.1.0",
|
|
406
|
+
resourceSpans: [
|
|
407
|
+
{
|
|
408
|
+
resource: {
|
|
409
|
+
attributes: otlpAttributes(this.resourceAttributes),
|
|
336
410
|
},
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
411
|
+
scopeSpans: [
|
|
412
|
+
{
|
|
413
|
+
scope: {
|
|
414
|
+
name: "@opengeni/observability",
|
|
415
|
+
version: "0.1.0",
|
|
416
|
+
},
|
|
417
|
+
spans: [
|
|
418
|
+
{
|
|
419
|
+
traceId: span.traceId,
|
|
420
|
+
spanId: span.spanId,
|
|
421
|
+
name: span.name,
|
|
422
|
+
kind: 1,
|
|
423
|
+
startTimeUnixNano: millisToNanos(span.startMs),
|
|
424
|
+
endTimeUnixNano: millisToNanos(span.endMs),
|
|
425
|
+
attributes: otlpAttributes(span.attributes),
|
|
426
|
+
status: span.error ? { code: 2, message: span.error.statusMessage } : { code: 1 },
|
|
427
|
+
},
|
|
428
|
+
],
|
|
429
|
+
},
|
|
430
|
+
],
|
|
431
|
+
},
|
|
432
|
+
],
|
|
349
433
|
};
|
|
350
|
-
void this.exporter(endpoint, body, parseHeaders(this.settings.observabilityOtlpHeaders)).catch(
|
|
351
|
-
|
|
352
|
-
|
|
434
|
+
void this.exporter(endpoint, body, parseHeaders(this.settings.observabilityOtlpHeaders)).catch(
|
|
435
|
+
(error) => {
|
|
436
|
+
this.warn("OTLP span export failed", { error: errorMessage(error), endpoint });
|
|
437
|
+
},
|
|
438
|
+
);
|
|
353
439
|
}
|
|
354
440
|
}
|
|
355
441
|
|
|
442
|
+
/**
|
|
443
|
+
* Convert routed sandbox-provider observations into one bounded Prometheus
|
|
444
|
+
* contract shared by API-direct and worker-turn execution. Provider/session
|
|
445
|
+
* identifiers, commands, paths, and other request data can never become labels:
|
|
446
|
+
* a future backend or operation is deliberately collapsed to `unknown` until
|
|
447
|
+
* this allowlist is reviewed.
|
|
448
|
+
*
|
|
449
|
+
* The returned callback is fail-safe. Metrics must never change the provider
|
|
450
|
+
* operation's result; a registration/exporter error is counted best-effort and
|
|
451
|
+
* then swallowed at this telemetry boundary.
|
|
452
|
+
*/
|
|
453
|
+
export function sandboxOperationMetricObserver(
|
|
454
|
+
observability: Observability,
|
|
455
|
+
): (observation: SandboxOperationMetricObservation) => void {
|
|
456
|
+
return (observation) => {
|
|
457
|
+
const backend = SANDBOX_OPERATION_BACKENDS.has(observation.backend)
|
|
458
|
+
? observation.backend
|
|
459
|
+
: "unknown";
|
|
460
|
+
const op = SANDBOX_OPERATION_NAMES.has(observation.op) ? observation.op : "unknown";
|
|
461
|
+
try {
|
|
462
|
+
observability.incrementCounter({
|
|
463
|
+
name: "opengeni_sandbox_operations_total",
|
|
464
|
+
help: "Physical routed sandbox provider operations by backend, operation, and outcome.",
|
|
465
|
+
labels: { backend, op, outcome: observation.outcome },
|
|
466
|
+
});
|
|
467
|
+
observability.observeHistogram({
|
|
468
|
+
name: "opengeni_sandbox_operation_duration_seconds",
|
|
469
|
+
help: "Physical routed sandbox provider-operation duration in seconds.",
|
|
470
|
+
labels: { backend, op },
|
|
471
|
+
value: Math.max(0, observation.durationMs) / 1_000,
|
|
472
|
+
});
|
|
473
|
+
} catch {
|
|
474
|
+
try {
|
|
475
|
+
observability.incrementCounter({
|
|
476
|
+
name: "opengeni_observability_observer_errors_total",
|
|
477
|
+
help: "Observability observer failures isolated from product execution.",
|
|
478
|
+
labels: { observer: "sandbox_operation" },
|
|
479
|
+
});
|
|
480
|
+
} catch {
|
|
481
|
+
// The metrics registry itself is unhealthy. Product execution remains
|
|
482
|
+
// authoritative and must not inherit an observability failure.
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
};
|
|
486
|
+
}
|
|
487
|
+
|
|
356
488
|
export type StartupDependencyRetryEvent = {
|
|
357
489
|
label: string;
|
|
358
490
|
attempt: number;
|
|
@@ -361,7 +493,10 @@ export type StartupDependencyRetryEvent = {
|
|
|
361
493
|
error: unknown;
|
|
362
494
|
};
|
|
363
495
|
|
|
364
|
-
export function logStartupDependencyRetry(
|
|
496
|
+
export function logStartupDependencyRetry(
|
|
497
|
+
observability: Observability,
|
|
498
|
+
event: StartupDependencyRetryEvent,
|
|
499
|
+
): void {
|
|
365
500
|
const message = event.error instanceof Error ? event.error.message : String(event.error);
|
|
366
501
|
observability.warn("Startup dependency connection failed; retrying", {
|
|
367
502
|
dependency: event.label,
|
|
@@ -382,19 +517,53 @@ function normalizeLabels(labels: MetricLabels = {}): Record<string, string> {
|
|
|
382
517
|
}
|
|
383
518
|
|
|
384
519
|
function buildVersion(): string {
|
|
385
|
-
return process.env.OPENGENI_VERSION
|
|
386
|
-
?? process.env.npm_package_version
|
|
387
|
-
?? "dev";
|
|
520
|
+
return process.env.OPENGENI_VERSION ?? process.env.npm_package_version ?? "dev";
|
|
388
521
|
}
|
|
389
522
|
|
|
390
523
|
function cleanAttributes(attributes: Attributes): Record<string, string | number | boolean | null> {
|
|
391
|
-
return Object.fromEntries(
|
|
524
|
+
return Object.fromEntries(
|
|
525
|
+
Object.entries(attributes).filter(([, value]) => value !== undefined),
|
|
526
|
+
) as Record<string, string | number | boolean | null>;
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
type SanitizedSpanError = {
|
|
530
|
+
type: string;
|
|
531
|
+
statusCode?: number;
|
|
532
|
+
statusMessage: string;
|
|
533
|
+
};
|
|
534
|
+
|
|
535
|
+
function sanitizeSpanError(error: unknown): SanitizedSpanError {
|
|
536
|
+
const type =
|
|
537
|
+
error instanceof Error && /^[A-Za-z][A-Za-z0-9_.-]{0,63}$/.test(error.name)
|
|
538
|
+
? error.name
|
|
539
|
+
: "Error";
|
|
540
|
+
const statusCode = errorStatusCode(error);
|
|
541
|
+
return {
|
|
542
|
+
type,
|
|
543
|
+
...(statusCode === undefined ? {} : { statusCode }),
|
|
544
|
+
statusMessage: statusCode === undefined ? "operation failed" : `HTTP ${statusCode}`,
|
|
545
|
+
};
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
function errorStatusCode(error: unknown): number | undefined {
|
|
549
|
+
if (typeof error !== "object" || error === null) return undefined;
|
|
550
|
+
const value = (error as { status?: unknown; statusCode?: unknown }).status;
|
|
551
|
+
const statusCode =
|
|
552
|
+
Number.isInteger(value) && typeof value === "number"
|
|
553
|
+
? value
|
|
554
|
+
: (error as { statusCode?: unknown }).statusCode;
|
|
555
|
+
return Number.isInteger(statusCode) &&
|
|
556
|
+
typeof statusCode === "number" &&
|
|
557
|
+
statusCode >= 100 &&
|
|
558
|
+
statusCode <= 599
|
|
559
|
+
? statusCode
|
|
560
|
+
: undefined;
|
|
392
561
|
}
|
|
393
562
|
|
|
394
|
-
function errorToAttributes(error:
|
|
563
|
+
function errorToAttributes(error: SanitizedSpanError): Attributes {
|
|
395
564
|
return {
|
|
396
|
-
"error.type": error
|
|
397
|
-
"error.
|
|
565
|
+
"error.type": error.type,
|
|
566
|
+
...(error.statusCode === undefined ? {} : { "error.status_code": error.statusCode }),
|
|
398
567
|
};
|
|
399
568
|
}
|
|
400
569
|
|
|
@@ -402,21 +571,31 @@ function errorMessage(error: unknown): string {
|
|
|
402
571
|
return error instanceof Error ? error.message : String(error);
|
|
403
572
|
}
|
|
404
573
|
|
|
405
|
-
function otlpAttributes(
|
|
574
|
+
function otlpAttributes(
|
|
575
|
+
attributes: Attributes,
|
|
576
|
+
): Array<{ key: string; value: Record<string, string | number | boolean> }> {
|
|
406
577
|
return Object.entries(cleanAttributes(attributes)).map(([key, value]) => ({
|
|
407
578
|
key,
|
|
408
579
|
value: otlpValue(value),
|
|
409
580
|
}));
|
|
410
581
|
}
|
|
411
582
|
|
|
412
|
-
function otlpValue(
|
|
583
|
+
function otlpValue(
|
|
584
|
+
value: string | number | boolean | null,
|
|
585
|
+
): Record<string, string | number | boolean> {
|
|
413
586
|
if (typeof value === "number") {
|
|
414
587
|
return Number.isInteger(value) ? { intValue: value } : { doubleValue: value };
|
|
415
588
|
}
|
|
416
589
|
if (typeof value === "boolean") {
|
|
417
590
|
return { boolValue: value };
|
|
418
591
|
}
|
|
419
|
-
return { stringValue: value === null ? "" : value };
|
|
592
|
+
return { stringValue: value === null ? "" : boundedOtlpString(value) };
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
function boundedOtlpString(value: string): string {
|
|
596
|
+
const bytes = new TextEncoder().encode(value);
|
|
597
|
+
if (bytes.byteLength <= 512) return value;
|
|
598
|
+
return `${new TextDecoder().decode(bytes.slice(0, 509))}…`;
|
|
420
599
|
}
|
|
421
600
|
|
|
422
601
|
function millisToNanos(ms: number): string {
|
|
@@ -432,17 +611,24 @@ export function parseHeaders(value: string): Record<string, string> {
|
|
|
432
611
|
if (!value.trim()) {
|
|
433
612
|
return {};
|
|
434
613
|
}
|
|
435
|
-
const entries: Array<[string, string]> = value
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
614
|
+
const entries: Array<[string, string]> = value
|
|
615
|
+
.split(",")
|
|
616
|
+
.map((pair): [string, string] => {
|
|
617
|
+
const separator = pair.indexOf("=");
|
|
618
|
+
if (separator === -1) {
|
|
619
|
+
return [pair.trim(), ""];
|
|
620
|
+
}
|
|
621
|
+
return [pair.slice(0, separator).trim(), pair.slice(separator + 1).trim()];
|
|
622
|
+
})
|
|
623
|
+
.filter(([key]) => key.length > 0);
|
|
442
624
|
return Object.fromEntries(entries);
|
|
443
625
|
}
|
|
444
626
|
|
|
445
|
-
async function defaultExporter(
|
|
627
|
+
async function defaultExporter(
|
|
628
|
+
url: string,
|
|
629
|
+
body: unknown,
|
|
630
|
+
headers: Record<string, string>,
|
|
631
|
+
): Promise<void> {
|
|
446
632
|
const response = await fetch(url, {
|
|
447
633
|
method: "POST",
|
|
448
634
|
headers: {
|