@lunora/container 1.0.0-alpha.1 → 1.0.0-alpha.10
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/LICENSE.md +26 -0
- package/README.md +97 -1
- package/__assets__/package-og.svg +1 -1
- package/dist/bridge.d.mts +3 -3
- package/dist/bridge.d.ts +3 -3
- package/dist/bridge.mjs +10 -7
- package/dist/do/index.d.mts +558 -4
- package/dist/do/index.d.ts +558 -4
- package/dist/do/index.mjs +213 -11
- package/dist/index.d.mts +105 -17
- package/dist/index.d.ts +105 -17
- package/dist/index.mjs +2 -2
- package/dist/otel.d.mts +95 -0
- package/dist/otel.d.ts +95 -0
- package/dist/otel.mjs +203 -0
- package/dist/packem_shared/ContainerProxy-DWqUX_re.mjs +1474 -0
- package/dist/packem_shared/containerBindingName-BiTrAF1J.mjs +224 -0
- package/dist/packem_shared/createContainerContext-CIVzsY5m.mjs +220 -0
- package/dist/packem_shared/jurisdiction-CuPNcLDt.mjs +13 -0
- package/dist/packem_shared/jurisdiction.d-TwTGkgTg.d.mts +266 -0
- package/dist/packem_shared/jurisdiction.d-TwTGkgTg.d.ts +266 -0
- package/package.json +7 -3
- package/dist/packem_shared/containerBindingName-BGdSdFNA.mjs +0 -116
- package/dist/packem_shared/createContainerContext-ChDD53ys.mjs +0 -122
- package/dist/packem_shared/types.d-D2l2SYol.d.mts +0 -140
- package/dist/packem_shared/types.d-D2l2SYol.d.ts +0 -140
package/dist/index.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { createContainerContext, createContainerTestContext } from './packem_shared/createContainerContext-
|
|
2
|
-
export { containerBindingName, containerBuildTag, containerClassName, defineContainer, isContainerDefinition, normalizeContainerImage, resolveContainerEnvVars } from './packem_shared/containerBindingName-
|
|
1
|
+
export { createContainerContext, createContainerTestContext } from './packem_shared/createContainerContext-CIVzsY5m.mjs';
|
|
2
|
+
export { containerBindingName, containerBuildTag, containerClassName, defineContainer, isContainerDefinition, normalizeContainerImage, resolveContainerEnvVars } from './packem_shared/containerBindingName-BiTrAF1J.mjs';
|
package/dist/otel.d.mts
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/** An attribute value carried on a span or log. */
|
|
2
|
+
type ContainerAttributeValue = boolean | number | string;
|
|
3
|
+
/**
|
|
4
|
+
* A `fetch` implementation — defaults to the runtime global. The exporter passes
|
|
5
|
+
* an abort `signal` (for the per-request timeout) and, once the promise settles,
|
|
6
|
+
* cancels the response `body` so Node/undici can release the socket for
|
|
7
|
+
* keep-alive reuse instead of leaving it occupied by an unread stream. It reads
|
|
8
|
+
* `ok`/`status` to detect a rejected export and nothing else from the response.
|
|
9
|
+
*/
|
|
10
|
+
type OtelFetchLike = (input: string, init: {
|
|
11
|
+
body: string;
|
|
12
|
+
headers: Record<string, string>;
|
|
13
|
+
method: string;
|
|
14
|
+
signal?: AbortSignal;
|
|
15
|
+
}) => Promise<{
|
|
16
|
+
body?: {
|
|
17
|
+
cancel: () => Promise<void>;
|
|
18
|
+
} | null;
|
|
19
|
+
ok: boolean;
|
|
20
|
+
status: number;
|
|
21
|
+
}>;
|
|
22
|
+
/** A single span the container process asks the exporter to record. */
|
|
23
|
+
interface ContainerSpanInput {
|
|
24
|
+
/** Attributes attached to the span (rendered under the OTLP `attributes` list). */
|
|
25
|
+
attributes?: Record<string, ContainerAttributeValue>;
|
|
26
|
+
/** Wall-clock millis when the operation ended. */
|
|
27
|
+
endMs: number;
|
|
28
|
+
/** When set, the span is marked errored with this message (and optional `error.type`). */
|
|
29
|
+
error?: {
|
|
30
|
+
message: string;
|
|
31
|
+
type?: string;
|
|
32
|
+
};
|
|
33
|
+
/** Span name — the operation being timed, e.g. `"transcode"`. */
|
|
34
|
+
name: string;
|
|
35
|
+
/** Wall-clock millis when the operation started. */
|
|
36
|
+
startMs: number;
|
|
37
|
+
}
|
|
38
|
+
/** A single log line the container process asks the exporter to record. */
|
|
39
|
+
interface ContainerLogInput {
|
|
40
|
+
/** Attributes attached to the log record. */
|
|
41
|
+
attributes?: Record<string, ContainerAttributeValue>;
|
|
42
|
+
/** Severity — defaults to `"info"`. */
|
|
43
|
+
level?: "debug" | "error" | "info" | "warn";
|
|
44
|
+
/** The log message body. */
|
|
45
|
+
message: string;
|
|
46
|
+
/** Wall-clock millis the line was emitted; defaults to now. */
|
|
47
|
+
ts?: number;
|
|
48
|
+
}
|
|
49
|
+
/** Options for {@link createContainerTelemetry}. */
|
|
50
|
+
interface ContainerTelemetryOptions {
|
|
51
|
+
/** Base OTLP collector endpoint; defaults to the `LUNORA_OTLP_ENDPOINT` env var. */
|
|
52
|
+
endpoint?: string;
|
|
53
|
+
/** Injectable `fetch` (tests / non-global runtimes). Defaults to `globalThis.fetch`. */
|
|
54
|
+
fetch?: OtelFetchLike;
|
|
55
|
+
/** Extra headers merged onto every POST — e.g. deployment/org correlation. `content-type` is set by default. */
|
|
56
|
+
headers?: Record<string, string>;
|
|
57
|
+
/** Called with any send failure so the caller can surface it; the export itself always swallows. */
|
|
58
|
+
onError?: (error: unknown) => void;
|
|
59
|
+
/** `service.name` resource attribute; defaults to the `LUNORA_SERVICE_NAME` env var then `"lunora-container"`. */
|
|
60
|
+
serviceName?: string;
|
|
61
|
+
/** Per-POST timeout in ms; a collector that never responds aborts after this so a stuck send can't stall `flush()`. Defaults to {@link DEFAULT_TIMEOUT_MS} (10s). */
|
|
62
|
+
timeoutMs?: number;
|
|
63
|
+
/** Bearer token sent as an `Authorization: Bearer` header; defaults to the `LUNORA_OTLP_TOKEN` env var. */
|
|
64
|
+
token?: string;
|
|
65
|
+
}
|
|
66
|
+
/** The exporter handle {@link createContainerTelemetry} returns. */
|
|
67
|
+
interface ContainerTelemetry {
|
|
68
|
+
/** Record one log line (no-op when disabled). */
|
|
69
|
+
emitLog: (log: ContainerLogInput) => void;
|
|
70
|
+
/** Record one span (no-op when disabled). */
|
|
71
|
+
emitSpan: (span: ContainerSpanInput) => void;
|
|
72
|
+
/** True when an endpoint resolved and exports are actually sent. */
|
|
73
|
+
readonly enabled: boolean;
|
|
74
|
+
/** Await all in-flight sends — call before the process exits. */
|
|
75
|
+
flush: () => Promise<void>;
|
|
76
|
+
/** Time `run()`, recording a span named `name` (ok, or errored if it throws). Always runs `run()`, even when disabled. */
|
|
77
|
+
trace: <T>(name: string, run: () => Promise<T>, attributes?: Record<string, ContainerAttributeValue>) => Promise<T>;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Create a zero-config OTLP exporter for the container process.
|
|
81
|
+
*
|
|
82
|
+
* ```ts
|
|
83
|
+
* const telemetry = createContainerTelemetry(); // reads LUNORA_OTLP_ENDPOINT / _TOKEN
|
|
84
|
+
* await telemetry.trace("transcode", () => transcode(job), { jobId: job.id });
|
|
85
|
+
* telemetry.emitLog({ level: "info", message: "done", attributes: { jobId: job.id } });
|
|
86
|
+
* await telemetry.flush(); // before the process exits
|
|
87
|
+
* ```
|
|
88
|
+
*
|
|
89
|
+
* With no endpoint resolvable the returned exporter is disabled (`enabled ===
|
|
90
|
+
* false`): `emitSpan`/`emitLog` no-op and `trace` still runs its work but records
|
|
91
|
+
* nothing — so the same code runs unchanged locally and in the cloud.
|
|
92
|
+
* @param options Exporter options; every field falls back to a `LUNORA_*` env var.
|
|
93
|
+
*/
|
|
94
|
+
declare const createContainerTelemetry: (options?: ContainerTelemetryOptions) => ContainerTelemetry;
|
|
95
|
+
export { type ContainerAttributeValue, type ContainerLogInput, type ContainerSpanInput, type ContainerTelemetry, type ContainerTelemetryOptions, type OtelFetchLike, createContainerTelemetry };
|
package/dist/otel.d.ts
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/** An attribute value carried on a span or log. */
|
|
2
|
+
type ContainerAttributeValue = boolean | number | string;
|
|
3
|
+
/**
|
|
4
|
+
* A `fetch` implementation — defaults to the runtime global. The exporter passes
|
|
5
|
+
* an abort `signal` (for the per-request timeout) and, once the promise settles,
|
|
6
|
+
* cancels the response `body` so Node/undici can release the socket for
|
|
7
|
+
* keep-alive reuse instead of leaving it occupied by an unread stream. It reads
|
|
8
|
+
* `ok`/`status` to detect a rejected export and nothing else from the response.
|
|
9
|
+
*/
|
|
10
|
+
type OtelFetchLike = (input: string, init: {
|
|
11
|
+
body: string;
|
|
12
|
+
headers: Record<string, string>;
|
|
13
|
+
method: string;
|
|
14
|
+
signal?: AbortSignal;
|
|
15
|
+
}) => Promise<{
|
|
16
|
+
body?: {
|
|
17
|
+
cancel: () => Promise<void>;
|
|
18
|
+
} | null;
|
|
19
|
+
ok: boolean;
|
|
20
|
+
status: number;
|
|
21
|
+
}>;
|
|
22
|
+
/** A single span the container process asks the exporter to record. */
|
|
23
|
+
interface ContainerSpanInput {
|
|
24
|
+
/** Attributes attached to the span (rendered under the OTLP `attributes` list). */
|
|
25
|
+
attributes?: Record<string, ContainerAttributeValue>;
|
|
26
|
+
/** Wall-clock millis when the operation ended. */
|
|
27
|
+
endMs: number;
|
|
28
|
+
/** When set, the span is marked errored with this message (and optional `error.type`). */
|
|
29
|
+
error?: {
|
|
30
|
+
message: string;
|
|
31
|
+
type?: string;
|
|
32
|
+
};
|
|
33
|
+
/** Span name — the operation being timed, e.g. `"transcode"`. */
|
|
34
|
+
name: string;
|
|
35
|
+
/** Wall-clock millis when the operation started. */
|
|
36
|
+
startMs: number;
|
|
37
|
+
}
|
|
38
|
+
/** A single log line the container process asks the exporter to record. */
|
|
39
|
+
interface ContainerLogInput {
|
|
40
|
+
/** Attributes attached to the log record. */
|
|
41
|
+
attributes?: Record<string, ContainerAttributeValue>;
|
|
42
|
+
/** Severity — defaults to `"info"`. */
|
|
43
|
+
level?: "debug" | "error" | "info" | "warn";
|
|
44
|
+
/** The log message body. */
|
|
45
|
+
message: string;
|
|
46
|
+
/** Wall-clock millis the line was emitted; defaults to now. */
|
|
47
|
+
ts?: number;
|
|
48
|
+
}
|
|
49
|
+
/** Options for {@link createContainerTelemetry}. */
|
|
50
|
+
interface ContainerTelemetryOptions {
|
|
51
|
+
/** Base OTLP collector endpoint; defaults to the `LUNORA_OTLP_ENDPOINT` env var. */
|
|
52
|
+
endpoint?: string;
|
|
53
|
+
/** Injectable `fetch` (tests / non-global runtimes). Defaults to `globalThis.fetch`. */
|
|
54
|
+
fetch?: OtelFetchLike;
|
|
55
|
+
/** Extra headers merged onto every POST — e.g. deployment/org correlation. `content-type` is set by default. */
|
|
56
|
+
headers?: Record<string, string>;
|
|
57
|
+
/** Called with any send failure so the caller can surface it; the export itself always swallows. */
|
|
58
|
+
onError?: (error: unknown) => void;
|
|
59
|
+
/** `service.name` resource attribute; defaults to the `LUNORA_SERVICE_NAME` env var then `"lunora-container"`. */
|
|
60
|
+
serviceName?: string;
|
|
61
|
+
/** Per-POST timeout in ms; a collector that never responds aborts after this so a stuck send can't stall `flush()`. Defaults to {@link DEFAULT_TIMEOUT_MS} (10s). */
|
|
62
|
+
timeoutMs?: number;
|
|
63
|
+
/** Bearer token sent as an `Authorization: Bearer` header; defaults to the `LUNORA_OTLP_TOKEN` env var. */
|
|
64
|
+
token?: string;
|
|
65
|
+
}
|
|
66
|
+
/** The exporter handle {@link createContainerTelemetry} returns. */
|
|
67
|
+
interface ContainerTelemetry {
|
|
68
|
+
/** Record one log line (no-op when disabled). */
|
|
69
|
+
emitLog: (log: ContainerLogInput) => void;
|
|
70
|
+
/** Record one span (no-op when disabled). */
|
|
71
|
+
emitSpan: (span: ContainerSpanInput) => void;
|
|
72
|
+
/** True when an endpoint resolved and exports are actually sent. */
|
|
73
|
+
readonly enabled: boolean;
|
|
74
|
+
/** Await all in-flight sends — call before the process exits. */
|
|
75
|
+
flush: () => Promise<void>;
|
|
76
|
+
/** Time `run()`, recording a span named `name` (ok, or errored if it throws). Always runs `run()`, even when disabled. */
|
|
77
|
+
trace: <T>(name: string, run: () => Promise<T>, attributes?: Record<string, ContainerAttributeValue>) => Promise<T>;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Create a zero-config OTLP exporter for the container process.
|
|
81
|
+
*
|
|
82
|
+
* ```ts
|
|
83
|
+
* const telemetry = createContainerTelemetry(); // reads LUNORA_OTLP_ENDPOINT / _TOKEN
|
|
84
|
+
* await telemetry.trace("transcode", () => transcode(job), { jobId: job.id });
|
|
85
|
+
* telemetry.emitLog({ level: "info", message: "done", attributes: { jobId: job.id } });
|
|
86
|
+
* await telemetry.flush(); // before the process exits
|
|
87
|
+
* ```
|
|
88
|
+
*
|
|
89
|
+
* With no endpoint resolvable the returned exporter is disabled (`enabled ===
|
|
90
|
+
* false`): `emitSpan`/`emitLog` no-op and `trace` still runs its work but records
|
|
91
|
+
* nothing — so the same code runs unchanged locally and in the cloud.
|
|
92
|
+
* @param options Exporter options; every field falls back to a `LUNORA_*` env var.
|
|
93
|
+
*/
|
|
94
|
+
declare const createContainerTelemetry: (options?: ContainerTelemetryOptions) => ContainerTelemetry;
|
|
95
|
+
export { type ContainerAttributeValue, type ContainerLogInput, type ContainerSpanInput, type ContainerTelemetry, type ContainerTelemetryOptions, type OtelFetchLike, createContainerTelemetry };
|
package/dist/otel.mjs
ADDED
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
const OTLP_SEVERITY = {
|
|
2
|
+
debug: 5,
|
|
3
|
+
// DEBUG
|
|
4
|
+
error: 17,
|
|
5
|
+
// ERROR
|
|
6
|
+
info: 9,
|
|
7
|
+
// INFO
|
|
8
|
+
log: 9,
|
|
9
|
+
// INFO
|
|
10
|
+
warn: 13
|
|
11
|
+
// WARN
|
|
12
|
+
};
|
|
13
|
+
const otlpUnixNano = (ms) => `${String(Math.round(ms))}000000`;
|
|
14
|
+
const otlpRandomHex = (bytes) => {
|
|
15
|
+
const buffer = new Uint8Array(bytes);
|
|
16
|
+
crypto.getRandomValues(buffer);
|
|
17
|
+
let hex = "";
|
|
18
|
+
for (const byte of buffer) {
|
|
19
|
+
hex += byte.toString(16).padStart(2, "0");
|
|
20
|
+
}
|
|
21
|
+
return hex;
|
|
22
|
+
};
|
|
23
|
+
const encodeAttribute = (key, value) => {
|
|
24
|
+
if (typeof value === "boolean") {
|
|
25
|
+
return { key, value: { boolValue: value } };
|
|
26
|
+
}
|
|
27
|
+
if (typeof value === "number") {
|
|
28
|
+
if (!Number.isFinite(value)) {
|
|
29
|
+
return { key, value: { stringValue: String(value) } };
|
|
30
|
+
}
|
|
31
|
+
return Number.isSafeInteger(value) ? { key, value: { intValue: String(value) } } : { key, value: { doubleValue: value } };
|
|
32
|
+
}
|
|
33
|
+
return { key, value: { stringValue: value } };
|
|
34
|
+
};
|
|
35
|
+
const encodeAttributes = (attributes) => {
|
|
36
|
+
if (attributes === void 0) {
|
|
37
|
+
return [];
|
|
38
|
+
}
|
|
39
|
+
return Object.entries(attributes).map(([key, value]) => encodeAttribute(key, value));
|
|
40
|
+
};
|
|
41
|
+
const mergeHeaders = (defaults, overrides, token) => {
|
|
42
|
+
const merged = {};
|
|
43
|
+
const seen = /* @__PURE__ */ new Map();
|
|
44
|
+
const put = (name, value) => {
|
|
45
|
+
const lower = name.toLowerCase();
|
|
46
|
+
const existing = seen.get(lower);
|
|
47
|
+
if (existing === void 0) {
|
|
48
|
+
seen.set(lower, name);
|
|
49
|
+
merged[name] = value;
|
|
50
|
+
} else {
|
|
51
|
+
merged[existing] = value;
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
for (const [name, value] of Object.entries(defaults)) {
|
|
55
|
+
put(name, value);
|
|
56
|
+
}
|
|
57
|
+
for (const [name, value] of Object.entries(overrides ?? {})) {
|
|
58
|
+
put(name, value);
|
|
59
|
+
}
|
|
60
|
+
if (token !== void 0 && token.length > 0) {
|
|
61
|
+
put("authorization", `Bearer ${token}`);
|
|
62
|
+
}
|
|
63
|
+
return merged;
|
|
64
|
+
};
|
|
65
|
+
const wrapResourceSpans = (span, scopeName, serviceName) => {
|
|
66
|
+
return {
|
|
67
|
+
resourceSpans: [
|
|
68
|
+
{
|
|
69
|
+
resource: { attributes: [encodeAttribute("service.name", serviceName)] },
|
|
70
|
+
scopeSpans: [{ scope: { name: scopeName }, spans: [span] }]
|
|
71
|
+
}
|
|
72
|
+
]
|
|
73
|
+
};
|
|
74
|
+
};
|
|
75
|
+
const wrapResourceLogs = (logRecord, scopeName, serviceName) => {
|
|
76
|
+
return {
|
|
77
|
+
resourceLogs: [
|
|
78
|
+
{
|
|
79
|
+
resource: { attributes: [encodeAttribute("service.name", serviceName)] },
|
|
80
|
+
scopeLogs: [{ logRecords: [logRecord], scope: { name: scopeName } }]
|
|
81
|
+
}
|
|
82
|
+
]
|
|
83
|
+
};
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
const DEFAULT_TIMEOUT_MS = 1e4;
|
|
87
|
+
const readEnv = (name) => {
|
|
88
|
+
return process.env[name];
|
|
89
|
+
};
|
|
90
|
+
const resolveFetch = (injected) => {
|
|
91
|
+
if (injected !== void 0) {
|
|
92
|
+
return injected;
|
|
93
|
+
}
|
|
94
|
+
if (typeof globalThis.fetch === "function") {
|
|
95
|
+
return globalThis.fetch;
|
|
96
|
+
}
|
|
97
|
+
return void 0;
|
|
98
|
+
};
|
|
99
|
+
const traceBody = (span, serviceName) => {
|
|
100
|
+
const attributes = encodeAttributes(span.attributes);
|
|
101
|
+
if (span.error?.type !== void 0) {
|
|
102
|
+
attributes.push(encodeAttribute("error.type", span.error.type));
|
|
103
|
+
}
|
|
104
|
+
const otlpSpan = {
|
|
105
|
+
attributes,
|
|
106
|
+
endTimeUnixNano: otlpUnixNano(span.endMs),
|
|
107
|
+
// SPAN_KIND_INTERNAL — the container's own work, not a server/client edge.
|
|
108
|
+
kind: 1,
|
|
109
|
+
name: span.name,
|
|
110
|
+
spanId: otlpRandomHex(8),
|
|
111
|
+
startTimeUnixNano: otlpUnixNano(span.startMs),
|
|
112
|
+
// STATUS_CODE_OK (1) / STATUS_CODE_ERROR (2).
|
|
113
|
+
status: span.error === void 0 ? { code: 1 } : { code: 2, message: span.error.message },
|
|
114
|
+
traceId: otlpRandomHex(16)
|
|
115
|
+
};
|
|
116
|
+
return wrapResourceSpans(otlpSpan, "@lunora/container", serviceName);
|
|
117
|
+
};
|
|
118
|
+
const logBody = (log, serviceName, nowMs) => {
|
|
119
|
+
const level = log.level ?? "info";
|
|
120
|
+
const record = {
|
|
121
|
+
attributes: encodeAttributes(log.attributes),
|
|
122
|
+
body: { stringValue: log.message },
|
|
123
|
+
severityNumber: OTLP_SEVERITY[level],
|
|
124
|
+
severityText: level.toUpperCase(),
|
|
125
|
+
timeUnixNano: otlpUnixNano(log.ts ?? nowMs)
|
|
126
|
+
};
|
|
127
|
+
return wrapResourceLogs(record, "@lunora/container", serviceName);
|
|
128
|
+
};
|
|
129
|
+
const createContainerTelemetry = (options = {}) => {
|
|
130
|
+
const endpoint = options.endpoint ?? readEnv("LUNORA_OTLP_ENDPOINT");
|
|
131
|
+
const enabled = endpoint !== void 0 && endpoint.length > 0;
|
|
132
|
+
const token = options.token ?? readEnv("LUNORA_OTLP_TOKEN");
|
|
133
|
+
const serviceName = options.serviceName ?? readEnv("LUNORA_SERVICE_NAME") ?? "lunora-container";
|
|
134
|
+
const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
135
|
+
const fetchImpl = resolveFetch(options.fetch);
|
|
136
|
+
const headers = mergeHeaders({ "content-type": "application/json" }, options.headers, token);
|
|
137
|
+
let base = endpoint ?? "";
|
|
138
|
+
while (base.endsWith("/")) {
|
|
139
|
+
base = base.slice(0, -1);
|
|
140
|
+
}
|
|
141
|
+
const tracesUrl = `${base}/v1/traces`;
|
|
142
|
+
const logsUrl = `${base}/v1/logs`;
|
|
143
|
+
const inflight = /* @__PURE__ */ new Set();
|
|
144
|
+
const send = (url, body) => {
|
|
145
|
+
if (fetchImpl === void 0) {
|
|
146
|
+
options.onError?.(new TypeError("createContainerTelemetry: no `fetch` available — pass `fetch` in options for this runtime."));
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
const dispatch = async () => {
|
|
150
|
+
try {
|
|
151
|
+
const response = await fetchImpl(url, { body: JSON.stringify(body), headers, method: "POST", signal: AbortSignal.timeout(timeoutMs) });
|
|
152
|
+
if (!response.ok) {
|
|
153
|
+
options.onError?.(new Error(`createContainerTelemetry: OTLP export to ${url} failed with status ${String(response.status)}.`));
|
|
154
|
+
}
|
|
155
|
+
try {
|
|
156
|
+
await response.body?.cancel();
|
|
157
|
+
} catch {
|
|
158
|
+
}
|
|
159
|
+
} catch (error) {
|
|
160
|
+
options.onError?.(error);
|
|
161
|
+
}
|
|
162
|
+
};
|
|
163
|
+
const settled = dispatch().finally(() => {
|
|
164
|
+
inflight.delete(settled);
|
|
165
|
+
});
|
|
166
|
+
inflight.add(settled);
|
|
167
|
+
};
|
|
168
|
+
const emitSpan = (span) => {
|
|
169
|
+
if (!enabled) {
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
send(tracesUrl, traceBody(span, serviceName));
|
|
173
|
+
};
|
|
174
|
+
const emitLog = (log) => {
|
|
175
|
+
if (!enabled) {
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
send(logsUrl, logBody(log, serviceName, Date.now()));
|
|
179
|
+
};
|
|
180
|
+
const trace = async (name, run, attributes) => {
|
|
181
|
+
const startMs = Date.now();
|
|
182
|
+
try {
|
|
183
|
+
const result = await run();
|
|
184
|
+
emitSpan({ attributes, endMs: Date.now(), name, startMs });
|
|
185
|
+
return result;
|
|
186
|
+
} catch (error) {
|
|
187
|
+
emitSpan({
|
|
188
|
+
attributes,
|
|
189
|
+
endMs: Date.now(),
|
|
190
|
+
error: { message: error instanceof Error ? error.message : String(error), type: error instanceof Error ? error.name : void 0 },
|
|
191
|
+
name,
|
|
192
|
+
startMs
|
|
193
|
+
});
|
|
194
|
+
throw error;
|
|
195
|
+
}
|
|
196
|
+
};
|
|
197
|
+
const flush = async () => {
|
|
198
|
+
await Promise.allSettled(inflight);
|
|
199
|
+
};
|
|
200
|
+
return { emitLog, emitSpan, enabled, flush, trace };
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
export { createContainerTelemetry };
|