@telemetry-dev/tanstack-ai 0.1.0 → 0.1.1
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.mts +1 -1
- package/dist/index.mjs +28 -22
- package/package.json +1 -1
- package/src/config.ts +3 -6
- package/src/middleware.ts +44 -26
- package/src/otel.ts +12 -7
package/dist/index.d.mts
CHANGED
|
@@ -17,7 +17,7 @@ interface TelemetryDevOptions {
|
|
|
17
17
|
/** Serverless extender (e.g. Cloudflare `ctx.waitUntil`). When provided, `onFinish` does not await the POST. */
|
|
18
18
|
waitUntil?: (p: Promise<unknown>) => void;
|
|
19
19
|
/** Receives any error raised while emitting telemetry; the integration never throws into the SDK. */
|
|
20
|
-
onError?: (
|
|
20
|
+
onError?: (cause: unknown) => void;
|
|
21
21
|
}
|
|
22
22
|
//#endregion
|
|
23
23
|
//#region src/otel.d.ts
|
package/dist/index.mjs
CHANGED
|
@@ -6,7 +6,7 @@ import { BasicTracerProvider } from "@opentelemetry/sdk-trace-base";
|
|
|
6
6
|
//#region src/config.ts
|
|
7
7
|
const DEFAULT_BASE_URL = "https://ingest.telemetry.dev";
|
|
8
8
|
function resolveConfig(options = {}) {
|
|
9
|
-
const env =
|
|
9
|
+
const env = globalThis.process?.env ?? {};
|
|
10
10
|
const baseUrl = (options.baseUrl ?? env.TELEMETRY_DEV_BASE_URL ?? DEFAULT_BASE_URL).replace(/\/+$/, "");
|
|
11
11
|
return {
|
|
12
12
|
apiKey: options.apiKey ?? env.TELEMETRY_DEV_API_KEY,
|
|
@@ -86,6 +86,7 @@ const postOtlp = async ({ fetchImpl, url, headers, body }) => {
|
|
|
86
86
|
await delay(RETRY_DELAYS_MS[attempt]);
|
|
87
87
|
}
|
|
88
88
|
};
|
|
89
|
+
const isReadableSpan = (span) => "resource" in span && "instrumentationScope" in span && "events" in span;
|
|
89
90
|
const cache = /* @__PURE__ */ new Map();
|
|
90
91
|
const buildCore = (config) => {
|
|
91
92
|
const { apiKey, baseUrl, environment, serviceName } = config;
|
|
@@ -95,7 +96,8 @@ const buildCore = (config) => {
|
|
|
95
96
|
});
|
|
96
97
|
const headers = {
|
|
97
98
|
"content-type": "application/x-protobuf",
|
|
98
|
-
authorization: `Bearer ${apiKey}
|
|
99
|
+
authorization: `Bearer ${apiKey}`,
|
|
100
|
+
"x-telemetry-dev-sdk": "@telemetry-dev/tanstack-ai"
|
|
99
101
|
};
|
|
100
102
|
const tracer = new BasicTracerProvider({ resource }).getTracer(SCOPE_NAME, SCOPE_VERSION);
|
|
101
103
|
const sendSpans = async (spans, transport) => {
|
|
@@ -137,11 +139,11 @@ const buildCore = (config) => {
|
|
|
137
139
|
return;
|
|
138
140
|
}
|
|
139
141
|
resultCallback({ code: 0 });
|
|
140
|
-
}).catch((
|
|
141
|
-
onError?.(
|
|
142
|
+
}).catch((cause) => {
|
|
143
|
+
onError?.(cause);
|
|
142
144
|
resultCallback({
|
|
143
145
|
code: 1,
|
|
144
|
-
error:
|
|
146
|
+
error: cause instanceof Error ? cause : void 0
|
|
145
147
|
});
|
|
146
148
|
});
|
|
147
149
|
},
|
|
@@ -199,14 +201,14 @@ const createEmitter = (config, overrides) => {
|
|
|
199
201
|
metrics = void 0;
|
|
200
202
|
const p = (async () => {
|
|
201
203
|
try {
|
|
202
|
-
await sendSpans(spans);
|
|
204
|
+
await sendSpans(spans.filter(isReadableSpan));
|
|
203
205
|
} catch (e) {
|
|
204
|
-
onError?.(e);
|
|
206
|
+
onError?.(e instanceof Error ? e : new Error(String(e)));
|
|
205
207
|
}
|
|
206
208
|
try {
|
|
207
209
|
if (pipeline) await pipeline.shutdown();
|
|
208
210
|
} catch (e) {
|
|
209
|
-
onError?.(e);
|
|
211
|
+
onError?.(e instanceof Error ? e : new Error(String(e)));
|
|
210
212
|
}
|
|
211
213
|
})();
|
|
212
214
|
if (config.waitUntil) {
|
|
@@ -224,6 +226,10 @@ const createEmitter = (config, overrides) => {
|
|
|
224
226
|
};
|
|
225
227
|
//#endregion
|
|
226
228
|
//#region src/middleware.ts
|
|
229
|
+
const isString = (value) => typeof value === "string";
|
|
230
|
+
const isNumber = (value) => typeof value === "number";
|
|
231
|
+
const isBigInt = (value) => typeof value === "bigint";
|
|
232
|
+
const isObject = (value) => value !== null && typeof value === "object";
|
|
227
233
|
function omitUndefined(attributes) {
|
|
228
234
|
const out = {};
|
|
229
235
|
for (const key of Object.keys(attributes)) {
|
|
@@ -233,13 +239,13 @@ function omitUndefined(attributes) {
|
|
|
233
239
|
return out;
|
|
234
240
|
}
|
|
235
241
|
function readId(value) {
|
|
236
|
-
if (
|
|
237
|
-
if (
|
|
242
|
+
if (isString(value)) return value.length > 0 ? value : null;
|
|
243
|
+
if (isNumber(value) || isBigInt(value)) return value.toString();
|
|
238
244
|
return null;
|
|
239
245
|
}
|
|
240
246
|
function jsonAttr(value) {
|
|
241
247
|
if (value === void 0) return void 0;
|
|
242
|
-
if (
|
|
248
|
+
if (isString(value)) return value;
|
|
243
249
|
try {
|
|
244
250
|
return JSON.stringify(value);
|
|
245
251
|
} catch {
|
|
@@ -247,22 +253,22 @@ function jsonAttr(value) {
|
|
|
247
253
|
}
|
|
248
254
|
}
|
|
249
255
|
function firstNumber(...candidates) {
|
|
250
|
-
for (const candidate of candidates) if (
|
|
256
|
+
for (const candidate of candidates) if (isNumber(candidate) && Number.isFinite(candidate)) return candidate;
|
|
251
257
|
}
|
|
252
258
|
function errorTypeName(err) {
|
|
253
259
|
if (err instanceof Error) return err.name || "Error";
|
|
254
|
-
if (err &&
|
|
260
|
+
if (err && isObject(err) && "name" in err) {
|
|
255
261
|
const n = err.name;
|
|
256
|
-
if (
|
|
262
|
+
if (isString(n) && n.length > 0) return n;
|
|
257
263
|
}
|
|
258
264
|
return "Error";
|
|
259
265
|
}
|
|
260
266
|
function errorMessage(err) {
|
|
261
267
|
if (err instanceof Error) return err.message;
|
|
262
|
-
if (
|
|
263
|
-
if (err &&
|
|
268
|
+
if (isString(err)) return err;
|
|
269
|
+
if (err && isObject(err) && "message" in err) {
|
|
264
270
|
const m = err.message;
|
|
265
|
-
if (
|
|
271
|
+
if (isString(m)) return m;
|
|
266
272
|
}
|
|
267
273
|
return String(err);
|
|
268
274
|
}
|
|
@@ -278,7 +284,7 @@ const MAX_TOKENS_KEYS = [
|
|
|
278
284
|
];
|
|
279
285
|
function samplingAttributes(modelOptions) {
|
|
280
286
|
const sampling = modelOptions ?? {};
|
|
281
|
-
const nested = sampling["options"] &&
|
|
287
|
+
const nested = sampling["options"] && isObject(sampling["options"]) ? sampling["options"] : void 0;
|
|
282
288
|
return omitUndefined({
|
|
283
289
|
"gen_ai.request.temperature": firstNumber(sampling["temperature"], nested?.["temperature"]),
|
|
284
290
|
"gen_ai.request.top_p": firstNumber(sampling["top_p"], sampling["topP"], nested?.["top_p"]),
|
|
@@ -337,7 +343,7 @@ function telemetryDev(options, overrides) {
|
|
|
337
343
|
...state.rootSampling
|
|
338
344
|
}));
|
|
339
345
|
if (state.restMetadata) for (const [key, value] of Object.entries(state.restMetadata)) {
|
|
340
|
-
const attr =
|
|
346
|
+
const attr = isString(value) ? value : jsonAttr(value);
|
|
341
347
|
if (attr !== void 0) state.rootSpan.setAttribute(`td.metadata.${key}`, attr);
|
|
342
348
|
}
|
|
343
349
|
};
|
|
@@ -398,7 +404,7 @@ function telemetryDev(options, overrides) {
|
|
|
398
404
|
onStart(ctx) {
|
|
399
405
|
try {
|
|
400
406
|
const rawMetadata = ctx.options?.["metadata"];
|
|
401
|
-
const metadata = rawMetadata &&
|
|
407
|
+
const metadata = rawMetadata && isObject(rawMetadata) ? rawMetadata : void 0;
|
|
402
408
|
const userId = readId(metadata?.["userId"]);
|
|
403
409
|
const sessionId = readId(metadata?.["sessionId"]) ?? ctx.threadId;
|
|
404
410
|
let restMetadata;
|
|
@@ -444,7 +450,7 @@ function telemetryDev(options, overrides) {
|
|
|
444
450
|
const inputMessages = [];
|
|
445
451
|
for (const prompt of chatConfig.systemPrompts) inputMessages.push({
|
|
446
452
|
role: "system",
|
|
447
|
-
content:
|
|
453
|
+
content: isString(prompt) ? prompt : prompt.content
|
|
448
454
|
});
|
|
449
455
|
for (const message of chatConfig.messages) inputMessages.push({
|
|
450
456
|
role: message.role,
|
|
@@ -493,7 +499,7 @@ function telemetryDev(options, overrides) {
|
|
|
493
499
|
if (chunk.type === "CUSTOM") {
|
|
494
500
|
if (iteration.structured && chunk.name === "structured-output.complete") {
|
|
495
501
|
const raw = chunk.value?.raw;
|
|
496
|
-
if (
|
|
502
|
+
if (isString(raw)) iteration.outputText = raw;
|
|
497
503
|
}
|
|
498
504
|
return;
|
|
499
505
|
}
|
package/package.json
CHANGED
package/src/config.ts
CHANGED
|
@@ -12,7 +12,7 @@ export interface TelemetryDevOptions {
|
|
|
12
12
|
/** Serverless extender (e.g. Cloudflare `ctx.waitUntil`). When provided, `onFinish` does not await the POST. */
|
|
13
13
|
waitUntil?: (p: Promise<unknown>) => void;
|
|
14
14
|
/** Receives any error raised while emitting telemetry; the integration never throws into the SDK. */
|
|
15
|
-
onError?: (
|
|
15
|
+
onError?: (cause: unknown) => void;
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
export interface ResolvedConfig {
|
|
@@ -22,16 +22,13 @@ export interface ResolvedConfig {
|
|
|
22
22
|
serviceName: string;
|
|
23
23
|
fetchImpl: typeof fetch;
|
|
24
24
|
waitUntil?: (p: Promise<unknown>) => void;
|
|
25
|
-
onError?: (
|
|
25
|
+
onError?: (cause: unknown) => void;
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
const DEFAULT_BASE_URL = "https://ingest.telemetry.dev";
|
|
29
29
|
|
|
30
30
|
export function resolveConfig(options: TelemetryDevOptions = {}): ResolvedConfig {
|
|
31
|
-
const env =
|
|
32
|
-
typeof process !== "undefined" && process.env
|
|
33
|
-
? process.env
|
|
34
|
-
: ({} as Record<string, string | undefined>);
|
|
31
|
+
const env = globalThis.process?.env ?? {};
|
|
35
32
|
const baseUrl = (options.baseUrl ?? env.TELEMETRY_DEV_BASE_URL ?? DEFAULT_BASE_URL).replace(
|
|
36
33
|
/\/+$/,
|
|
37
34
|
"",
|
package/src/middleware.ts
CHANGED
|
@@ -24,6 +24,22 @@ import type {
|
|
|
24
24
|
import { resolveConfig, type TelemetryDevOptions } from "./config.ts";
|
|
25
25
|
import { createEmitter, type EmitterOverrides } from "./otel.ts";
|
|
26
26
|
|
|
27
|
+
type JsonValue =
|
|
28
|
+
| string
|
|
29
|
+
| number
|
|
30
|
+
| bigint
|
|
31
|
+
| boolean
|
|
32
|
+
| null
|
|
33
|
+
| undefined
|
|
34
|
+
| JsonValue[]
|
|
35
|
+
| { [key: string]: JsonValue };
|
|
36
|
+
|
|
37
|
+
const isString = (value: unknown): value is string => typeof value === "string";
|
|
38
|
+
const isNumber = (value: unknown): value is number => typeof value === "number";
|
|
39
|
+
const isBigInt = (value: unknown): value is bigint => typeof value === "bigint";
|
|
40
|
+
const isObject = <T>(value: T): value is T & { [key: string]: JsonValue } =>
|
|
41
|
+
value !== null && typeof value === "object";
|
|
42
|
+
|
|
27
43
|
function omitUndefined(attributes: Attributes): Attributes {
|
|
28
44
|
const out: Attributes = {};
|
|
29
45
|
for (const key of Object.keys(attributes)) {
|
|
@@ -35,11 +51,11 @@ function omitUndefined(attributes: Attributes): Attributes {
|
|
|
35
51
|
return out;
|
|
36
52
|
}
|
|
37
53
|
|
|
38
|
-
function readId(value:
|
|
39
|
-
if (
|
|
54
|
+
function readId<T>(value: T): string | null {
|
|
55
|
+
if (isString(value)) {
|
|
40
56
|
return value.length > 0 ? value : null;
|
|
41
57
|
}
|
|
42
|
-
if (
|
|
58
|
+
if (isNumber(value) || isBigInt(value)) {
|
|
43
59
|
return value.toString();
|
|
44
60
|
}
|
|
45
61
|
return null;
|
|
@@ -47,9 +63,9 @@ function readId(value: unknown): string | null {
|
|
|
47
63
|
|
|
48
64
|
// Stringify structured content (messages / tool args) for the gen_ai.* string attributes the
|
|
49
65
|
// ingest parses back into JSON. Returns undefined so omitUndefined drops absent content.
|
|
50
|
-
function jsonAttr(value:
|
|
66
|
+
function jsonAttr<T>(value: T): string | undefined {
|
|
51
67
|
if (value === undefined) return undefined;
|
|
52
|
-
if (
|
|
68
|
+
if (isString(value)) return value;
|
|
53
69
|
try {
|
|
54
70
|
return JSON.stringify(value);
|
|
55
71
|
} catch {
|
|
@@ -57,30 +73,30 @@ function jsonAttr(value: unknown): string | undefined {
|
|
|
57
73
|
}
|
|
58
74
|
}
|
|
59
75
|
|
|
60
|
-
function firstNumber(...candidates:
|
|
76
|
+
function firstNumber<T>(...candidates: T[]): number | undefined {
|
|
61
77
|
for (const candidate of candidates) {
|
|
62
|
-
if (
|
|
78
|
+
if (isNumber(candidate) && Number.isFinite(candidate)) {
|
|
63
79
|
return candidate;
|
|
64
80
|
}
|
|
65
81
|
}
|
|
66
82
|
return undefined;
|
|
67
83
|
}
|
|
68
84
|
|
|
69
|
-
function errorTypeName(err:
|
|
85
|
+
function errorTypeName<T>(err: T): string {
|
|
70
86
|
if (err instanceof Error) return err.name || "Error";
|
|
71
|
-
if (err &&
|
|
87
|
+
if (err && isObject(err) && "name" in err) {
|
|
72
88
|
const n = (err as { name?: unknown }).name;
|
|
73
|
-
if (
|
|
89
|
+
if (isString(n) && n.length > 0) return n;
|
|
74
90
|
}
|
|
75
91
|
return "Error";
|
|
76
92
|
}
|
|
77
93
|
|
|
78
|
-
function errorMessage(err:
|
|
94
|
+
function errorMessage<T>(err: T): string {
|
|
79
95
|
if (err instanceof Error) return err.message;
|
|
80
|
-
if (
|
|
81
|
-
if (err &&
|
|
96
|
+
if (isString(err)) return err;
|
|
97
|
+
if (err && isObject(err) && "message" in err) {
|
|
82
98
|
const m = (err as { message?: unknown }).message;
|
|
83
|
-
if (
|
|
99
|
+
if (isString(m)) return m;
|
|
84
100
|
}
|
|
85
101
|
return String(err);
|
|
86
102
|
}
|
|
@@ -101,11 +117,11 @@ const MAX_TOKENS_KEYS = [
|
|
|
101
117
|
|
|
102
118
|
// Sampling options live in opaque provider-native `modelOptions`; pick the first numeric value
|
|
103
119
|
// among the known spellings (including Ollama's nested `options`) for the gen_ai.request.* attrs.
|
|
104
|
-
function samplingAttributes(modelOptions: Record<string,
|
|
120
|
+
function samplingAttributes(modelOptions: Record<string, JsonValue> | undefined): Attributes {
|
|
105
121
|
const sampling = modelOptions ?? {};
|
|
106
122
|
const nested =
|
|
107
|
-
sampling["options"] &&
|
|
108
|
-
? (sampling["options"] as Record<string,
|
|
123
|
+
sampling["options"] && isObject(sampling["options"])
|
|
124
|
+
? (sampling["options"] as Record<string, JsonValue>)
|
|
109
125
|
: undefined;
|
|
110
126
|
return omitUndefined({
|
|
111
127
|
"gen_ai.request.temperature": firstNumber(sampling["temperature"], nested?.["temperature"]),
|
|
@@ -136,7 +152,7 @@ interface RunState {
|
|
|
136
152
|
responseModel: string | null;
|
|
137
153
|
userId: string | null;
|
|
138
154
|
sessionId: string;
|
|
139
|
-
restMetadata: Record<string,
|
|
155
|
+
restMetadata: Record<string, JsonValue> | undefined;
|
|
140
156
|
rootInput: string | undefined;
|
|
141
157
|
rootSampling: Attributes;
|
|
142
158
|
rootCaptured: boolean;
|
|
@@ -229,7 +245,7 @@ export function telemetryDev(
|
|
|
229
245
|
);
|
|
230
246
|
if (state.restMetadata) {
|
|
231
247
|
for (const [key, value] of Object.entries(state.restMetadata)) {
|
|
232
|
-
const attr =
|
|
248
|
+
const attr = isString(value) ? value : jsonAttr(value);
|
|
233
249
|
if (attr !== undefined) {
|
|
234
250
|
state.rootSpan.setAttribute(`td.metadata.${key}`, attr);
|
|
235
251
|
}
|
|
@@ -297,14 +313,14 @@ export function telemetryDev(
|
|
|
297
313
|
try {
|
|
298
314
|
const rawMetadata = ctx.options?.["metadata"];
|
|
299
315
|
const metadata =
|
|
300
|
-
rawMetadata &&
|
|
301
|
-
? (rawMetadata as Record<string,
|
|
316
|
+
rawMetadata && isObject(rawMetadata)
|
|
317
|
+
? (rawMetadata as Record<string, JsonValue>)
|
|
302
318
|
: undefined;
|
|
303
319
|
const userId = readId(metadata?.["userId"]);
|
|
304
320
|
const sessionId = readId(metadata?.["sessionId"]) ?? ctx.threadId;
|
|
305
|
-
let restMetadata: Record<string,
|
|
321
|
+
let restMetadata: Record<string, JsonValue> | undefined;
|
|
306
322
|
if (metadata) {
|
|
307
|
-
const rest: Record<string,
|
|
323
|
+
const rest: Record<string, JsonValue> = {};
|
|
308
324
|
for (const [key, value] of Object.entries(metadata)) {
|
|
309
325
|
if (key !== "userId" && key !== "sessionId") {
|
|
310
326
|
rest[key] = value;
|
|
@@ -360,14 +376,16 @@ export function telemetryDev(
|
|
|
360
376
|
for (const prompt of chatConfig.systemPrompts) {
|
|
361
377
|
inputMessages.push({
|
|
362
378
|
role: "system",
|
|
363
|
-
content:
|
|
379
|
+
content: isString(prompt) ? prompt : prompt.content,
|
|
364
380
|
});
|
|
365
381
|
}
|
|
366
382
|
for (const message of chatConfig.messages) {
|
|
367
383
|
inputMessages.push({ role: message.role, content: message.content });
|
|
368
384
|
}
|
|
369
385
|
const inputJson = jsonAttr(inputMessages);
|
|
370
|
-
const sampling = samplingAttributes(
|
|
386
|
+
const sampling = samplingAttributes(
|
|
387
|
+
(chatConfig.modelOptions ?? ctx.modelOptions) as Record<string, JsonValue> | undefined,
|
|
388
|
+
);
|
|
371
389
|
if (!state.rootCaptured) {
|
|
372
390
|
state.rootCaptured = true;
|
|
373
391
|
state.rootInput = inputJson;
|
|
@@ -418,7 +436,7 @@ export function telemetryDev(
|
|
|
418
436
|
// still holds the agent loop's text, so this is the structured span's only output.
|
|
419
437
|
if (iteration.structured && chunk.name === "structured-output.complete") {
|
|
420
438
|
const raw = (chunk.value as { raw?: unknown } | null | undefined)?.raw;
|
|
421
|
-
if (
|
|
439
|
+
if (isString(raw)) iteration.outputText = raw;
|
|
422
440
|
}
|
|
423
441
|
return undefined;
|
|
424
442
|
}
|
package/src/otel.ts
CHANGED
|
@@ -44,7 +44,7 @@ export interface EmitterOverrides {
|
|
|
44
44
|
recordTokens?: (tokenType: "input" | "output", count: number, attributes: Attributes) => void;
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
-
type Transport = { fetchImpl: typeof fetch; onError?: (
|
|
47
|
+
type Transport = { fetchImpl: typeof fetch; onError?: (cause: unknown) => void };
|
|
48
48
|
|
|
49
49
|
const RETRY_DELAYS_MS = [100, 500] as const;
|
|
50
50
|
|
|
@@ -97,6 +97,9 @@ interface EmitterCore {
|
|
|
97
97
|
buildMetrics: (transport: Transport) => MetricsPipeline;
|
|
98
98
|
}
|
|
99
99
|
|
|
100
|
+
const isReadableSpan = (span: Span): span is Span & ReadableSpan =>
|
|
101
|
+
"resource" in span && "instrumentationScope" in span && "events" in span;
|
|
102
|
+
|
|
100
103
|
const cache = new Map<string, EmitterCore>();
|
|
101
104
|
|
|
102
105
|
const buildCore = (config: ResolvedConfig): EmitterCore => {
|
|
@@ -108,6 +111,7 @@ const buildCore = (config: ResolvedConfig): EmitterCore => {
|
|
|
108
111
|
const headers = {
|
|
109
112
|
"content-type": "application/x-protobuf",
|
|
110
113
|
authorization: `Bearer ${apiKey}`,
|
|
114
|
+
"x-telemetry-dev-sdk": "@telemetry-dev/tanstack-ai",
|
|
111
115
|
};
|
|
112
116
|
|
|
113
117
|
const provider = new BasicTracerProvider({ resource });
|
|
@@ -148,9 +152,9 @@ const buildCore = (config: ResolvedConfig): EmitterCore => {
|
|
|
148
152
|
}
|
|
149
153
|
resultCallback({ code: 0 });
|
|
150
154
|
})
|
|
151
|
-
.catch((
|
|
152
|
-
onError?.(
|
|
153
|
-
resultCallback({ code: 1, error:
|
|
155
|
+
.catch((cause: unknown) => {
|
|
156
|
+
onError?.(cause);
|
|
157
|
+
resultCallback({ code: 1, error: cause instanceof Error ? cause : undefined });
|
|
154
158
|
});
|
|
155
159
|
},
|
|
156
160
|
selectAggregationTemporality: () => AggregationTemporality.DELTA,
|
|
@@ -226,14 +230,15 @@ export const createEmitter = (config: ResolvedConfig, overrides?: EmitterOverrid
|
|
|
226
230
|
metrics = undefined;
|
|
227
231
|
const p = (async () => {
|
|
228
232
|
try {
|
|
229
|
-
|
|
233
|
+
const readableSpans = spans.filter(isReadableSpan);
|
|
234
|
+
await sendSpans(readableSpans);
|
|
230
235
|
} catch (e) {
|
|
231
|
-
onError?.(e);
|
|
236
|
+
onError?.(e instanceof Error ? e : new Error(String(e)));
|
|
232
237
|
}
|
|
233
238
|
try {
|
|
234
239
|
if (pipeline) await pipeline.shutdown();
|
|
235
240
|
} catch (e) {
|
|
236
|
-
onError?.(e);
|
|
241
|
+
onError?.(e instanceof Error ? e : new Error(String(e)));
|
|
237
242
|
}
|
|
238
243
|
})();
|
|
239
244
|
if (config.waitUntil) {
|