@observyze/sdk 0.1.0 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chunk-FQBYUOJB.mjs +316 -0
- package/dist/chunk-YSYKPKKX.mjs +357 -0
- package/dist/index--b41_E-1.d.mts +440 -0
- package/dist/index--b41_E-1.d.ts +440 -0
- package/dist/index-Cr-FN-y5.d.mts +405 -0
- package/dist/index-Cr-FN-y5.d.ts +405 -0
- package/dist/index-D4UXMom5.d.mts +429 -0
- package/dist/index-D4UXMom5.d.ts +429 -0
- package/dist/index.d.mts +27 -0
- package/dist/index.d.ts +27 -0
- package/dist/index.js +1282 -0
- package/dist/index.mjs +936 -0
- package/dist/opentelemetry/index.d.mts +2 -0
- package/dist/opentelemetry/index.d.ts +2 -0
- package/dist/opentelemetry/index.js +338 -0
- package/dist/opentelemetry/index.mjs +6 -0
- package/package.json +17 -3
- package/INSTRUMENTATION_SUMMARY.md +0 -184
- package/examples/auto-instrumentation.ts +0 -210
- package/src/client.ts +0 -578
- package/src/index.ts +0 -21
- package/src/instrumentation/README.md +0 -227
- package/src/instrumentation/anthropic.ts +0 -233
- package/src/instrumentation/index.ts +0 -43
- package/src/instrumentation/openai.ts +0 -193
- package/src/trace.ts +0 -242
- package/src/types.ts +0 -102
- package/tsconfig.json +0 -14
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
// src/types.ts
|
|
2
|
+
import { SpanType, TraceStatus } from "@observyze/types";
|
|
3
|
+
|
|
4
|
+
// src/trace.ts
|
|
5
|
+
import { randomUUID } from "crypto";
|
|
6
|
+
function generateId() {
|
|
7
|
+
return `${Date.now()}-${randomUUID().substring(0, 8)}`;
|
|
8
|
+
}
|
|
9
|
+
var Span = class {
|
|
10
|
+
data;
|
|
11
|
+
startTime;
|
|
12
|
+
constructor(name, type, parentSpanId) {
|
|
13
|
+
this.startTime = Date.now();
|
|
14
|
+
this.data = {
|
|
15
|
+
span_id: generateId(),
|
|
16
|
+
parent_span_id: parentSpanId,
|
|
17
|
+
name,
|
|
18
|
+
type,
|
|
19
|
+
start_time: new Date(this.startTime),
|
|
20
|
+
end_time: new Date(this.startTime),
|
|
21
|
+
// Will be updated on end()
|
|
22
|
+
duration_ms: 0,
|
|
23
|
+
input: null,
|
|
24
|
+
output: null,
|
|
25
|
+
metadata: {}
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Set the input data for this span
|
|
30
|
+
*/
|
|
31
|
+
setInput(input) {
|
|
32
|
+
this.data.input = input;
|
|
33
|
+
return this;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Set the output data for this span
|
|
37
|
+
*/
|
|
38
|
+
setOutput(output) {
|
|
39
|
+
this.data.output = output;
|
|
40
|
+
return this;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Record an error that occurred during span execution
|
|
44
|
+
*/
|
|
45
|
+
setError(error) {
|
|
46
|
+
this.data.error = {
|
|
47
|
+
message: error.message,
|
|
48
|
+
stack: error.stack,
|
|
49
|
+
code: error.code
|
|
50
|
+
};
|
|
51
|
+
return this;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Set metadata for this span
|
|
55
|
+
*/
|
|
56
|
+
setMetadata(key, value) {
|
|
57
|
+
this.data.metadata[key] = value;
|
|
58
|
+
return this;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Set multiple metadata fields at once
|
|
62
|
+
*/
|
|
63
|
+
setMetadataAll(metadata) {
|
|
64
|
+
this.data.metadata = { ...this.data.metadata, ...metadata };
|
|
65
|
+
return this;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Set token usage information
|
|
69
|
+
*/
|
|
70
|
+
setTokens(tokens) {
|
|
71
|
+
this.data.tokens = tokens;
|
|
72
|
+
return this;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* End the span and calculate duration
|
|
76
|
+
*/
|
|
77
|
+
end() {
|
|
78
|
+
const endTime = Date.now();
|
|
79
|
+
this.data.end_time = new Date(endTime);
|
|
80
|
+
this.data.duration_ms = endTime - this.startTime;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Get the span ID
|
|
84
|
+
*/
|
|
85
|
+
get id() {
|
|
86
|
+
return this.data.span_id;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Get the span data for serialization
|
|
90
|
+
*/
|
|
91
|
+
toJSON() {
|
|
92
|
+
return { ...this.data };
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
var Trace = class {
|
|
96
|
+
data;
|
|
97
|
+
startTime;
|
|
98
|
+
spans = [];
|
|
99
|
+
ended = false;
|
|
100
|
+
constructor(name, organizationId, projectId) {
|
|
101
|
+
this.startTime = Date.now();
|
|
102
|
+
this.data = {
|
|
103
|
+
trace_id: generateId(),
|
|
104
|
+
organization_id: organizationId,
|
|
105
|
+
project_id: projectId,
|
|
106
|
+
name,
|
|
107
|
+
status: TraceStatus.RUNNING,
|
|
108
|
+
start_time: new Date(this.startTime),
|
|
109
|
+
end_time: new Date(this.startTime),
|
|
110
|
+
// Will be updated on end()
|
|
111
|
+
duration_ms: 0,
|
|
112
|
+
metadata: {},
|
|
113
|
+
spans: [],
|
|
114
|
+
tags: []
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Start a new span within this trace
|
|
119
|
+
*/
|
|
120
|
+
startSpan(name, type, parentSpanId) {
|
|
121
|
+
if (this.ended) {
|
|
122
|
+
throw new Error("Cannot start span on an ended trace");
|
|
123
|
+
}
|
|
124
|
+
const span = new Span(name, type, parentSpanId);
|
|
125
|
+
this.spans.push(span);
|
|
126
|
+
return span;
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Add metadata to the trace
|
|
130
|
+
*/
|
|
131
|
+
setMetadata(key, value) {
|
|
132
|
+
this.data.metadata[key] = value;
|
|
133
|
+
return this;
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Set multiple metadata fields at once
|
|
137
|
+
*/
|
|
138
|
+
setMetadataAll(metadata) {
|
|
139
|
+
this.data.metadata = { ...this.data.metadata, ...metadata };
|
|
140
|
+
return this;
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Add tags to the trace
|
|
144
|
+
*/
|
|
145
|
+
addTag(tag) {
|
|
146
|
+
if (!this.data.tags.includes(tag)) {
|
|
147
|
+
this.data.tags.push(tag);
|
|
148
|
+
}
|
|
149
|
+
return this;
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Add multiple tags at once
|
|
153
|
+
*/
|
|
154
|
+
addTags(tags) {
|
|
155
|
+
tags.forEach((tag) => this.addTag(tag));
|
|
156
|
+
return this;
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Set the user ID associated with this trace
|
|
160
|
+
*/
|
|
161
|
+
setUserId(userId) {
|
|
162
|
+
this.data.user_id = userId;
|
|
163
|
+
return this;
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Set the session ID associated with this trace
|
|
167
|
+
*/
|
|
168
|
+
setSessionId(sessionId) {
|
|
169
|
+
this.data.session_id = sessionId;
|
|
170
|
+
return this;
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* End the trace with a final status
|
|
174
|
+
*/
|
|
175
|
+
end(status = TraceStatus.SUCCESS) {
|
|
176
|
+
if (this.ended) {
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
const endTime = Date.now();
|
|
180
|
+
this.data.end_time = new Date(endTime);
|
|
181
|
+
this.data.duration_ms = endTime - this.startTime;
|
|
182
|
+
this.data.status = status;
|
|
183
|
+
this.data.spans = this.spans.map((span) => span.toJSON());
|
|
184
|
+
this.ended = true;
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Get the trace ID
|
|
188
|
+
*/
|
|
189
|
+
get id() {
|
|
190
|
+
return this.data.trace_id;
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Check if the trace has ended
|
|
194
|
+
*/
|
|
195
|
+
get isEnded() {
|
|
196
|
+
return this.ended;
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Get the trace data for serialization
|
|
200
|
+
*/
|
|
201
|
+
toJSON() {
|
|
202
|
+
return { ...this.data };
|
|
203
|
+
}
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
// src/opentelemetry/exporter.ts
|
|
207
|
+
var ObservyzeSpanExporter = class {
|
|
208
|
+
client;
|
|
209
|
+
config;
|
|
210
|
+
constructor(client, config) {
|
|
211
|
+
this.client = client;
|
|
212
|
+
this.config = {
|
|
213
|
+
serviceName: config?.serviceName || "unknown-service",
|
|
214
|
+
projectId: config?.projectId || "",
|
|
215
|
+
defaultSpanType: config?.defaultSpanType || "llm",
|
|
216
|
+
headers: config?.headers || {}
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Export spans — called by OTel SDK when spans are ready.
|
|
221
|
+
* Converts OTel spans to Observyze traces and buffers them.
|
|
222
|
+
*/
|
|
223
|
+
async export(spans, resultCallback) {
|
|
224
|
+
if (!spans || spans.length === 0) {
|
|
225
|
+
resultCallback({ code: 0 });
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
try {
|
|
229
|
+
const organizationId = this.client.config?.organizationId || "";
|
|
230
|
+
const projectId = this.config.projectId || this.client.config?.projectId || "";
|
|
231
|
+
for (const span of spans) {
|
|
232
|
+
const spanContext = span.spanContext();
|
|
233
|
+
const traceId = spanContext?.traceId || span.spanId();
|
|
234
|
+
const trace = new Trace(
|
|
235
|
+
span.name || "otel-span",
|
|
236
|
+
organizationId,
|
|
237
|
+
projectId
|
|
238
|
+
);
|
|
239
|
+
trace.setMetadata("source", "opentelemetry");
|
|
240
|
+
trace.setMetadata("otel.trace_id", traceId);
|
|
241
|
+
trace.setMetadata("otel.span_id", spanContext?.spanId || "");
|
|
242
|
+
trace.setMetadata("service.name", this.config.serviceName);
|
|
243
|
+
trace.setMetadataAll(span.attributes || {});
|
|
244
|
+
if (span.resource?.attributes) {
|
|
245
|
+
trace.setMetadataAll(span.resource.attributes);
|
|
246
|
+
}
|
|
247
|
+
const input = span.attributes?.["gen_ai.prompt.0.content"] || span.attributes?.["gen_ai.completion.0.content"] || span.attributes?.["llm.input"] || void 0;
|
|
248
|
+
const output = span.attributes?.["gen_ai.completion.0.content"] || span.attributes?.["llm.output"] || void 0;
|
|
249
|
+
const model = span.attributes?.["gen_ai.request.model"] || span.attributes?.["llm.model"] || void 0;
|
|
250
|
+
const provider = span.attributes?.["gen_ai.request.provider"] || span.attributes?.["llm.provider"] || void 0;
|
|
251
|
+
const inputTokens = typeof span.attributes?.["gen_ai.usage.input_tokens"] === "number" ? span.attributes["gen_ai.usage.input_tokens"] : void 0;
|
|
252
|
+
const outputTokens = typeof span.attributes?.["gen_ai.usage.output_tokens"] === "number" ? span.attributes["gen_ai.usage.output_tokens"] : void 0;
|
|
253
|
+
trace.setMetadata("provider", provider || "unknown");
|
|
254
|
+
trace.setMetadata("model", model || "unknown");
|
|
255
|
+
const nsDuration = span.duration;
|
|
256
|
+
const durationMs = nsDuration ? Math.round(nsDuration / 1e6) : 0;
|
|
257
|
+
trace.setMetadata("latency_ms", durationMs);
|
|
258
|
+
trace.setMetadata("otel.duration_ns", nsDuration);
|
|
259
|
+
const oSpan = trace.startSpan(span.name || "otel-operation", this.config.defaultSpanType);
|
|
260
|
+
if (input) oSpan.setInput(input);
|
|
261
|
+
if (output) oSpan.setOutput(output);
|
|
262
|
+
if (inputTokens || outputTokens) {
|
|
263
|
+
oSpan.setTokens({
|
|
264
|
+
input: inputTokens || 0,
|
|
265
|
+
output: outputTokens || 0,
|
|
266
|
+
total: (inputTokens || 0) + (outputTokens || 0)
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
if (model) oSpan.setMetadata("model", model);
|
|
270
|
+
if (provider) oSpan.setMetadata("provider", provider);
|
|
271
|
+
if (span.attributes) oSpan.setMetadataAll(span.attributes);
|
|
272
|
+
const status = span.status;
|
|
273
|
+
const statusCode = status?.code;
|
|
274
|
+
if (statusCode === 2) {
|
|
275
|
+
oSpan.setError(new Error(status?.message || "OTel span error"));
|
|
276
|
+
trace.end(TraceStatus.ERROR);
|
|
277
|
+
} else {
|
|
278
|
+
trace.end(TraceStatus.SUCCESS);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
resultCallback({ code: 0 });
|
|
282
|
+
} catch (error) {
|
|
283
|
+
resultCallback({
|
|
284
|
+
code: 1,
|
|
285
|
+
error: error instanceof Error ? error : new Error(String(error))
|
|
286
|
+
});
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
/**
|
|
290
|
+
* Called when the exporter is shut down.
|
|
291
|
+
* Flushes any remaining buffered traces via the SDK client.
|
|
292
|
+
*/
|
|
293
|
+
async shutdown() {
|
|
294
|
+
try {
|
|
295
|
+
await this.client.flush();
|
|
296
|
+
} catch {
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
/**
|
|
300
|
+
* Called by the OTel SDK to force-export buffered spans.
|
|
301
|
+
*/
|
|
302
|
+
async forceFlush() {
|
|
303
|
+
try {
|
|
304
|
+
await this.client.flush();
|
|
305
|
+
} catch {
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
};
|
|
309
|
+
|
|
310
|
+
export {
|
|
311
|
+
SpanType,
|
|
312
|
+
TraceStatus,
|
|
313
|
+
Span,
|
|
314
|
+
Trace,
|
|
315
|
+
ObservyzeSpanExporter
|
|
316
|
+
};
|
|
@@ -0,0 +1,357 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
3
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
5
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
6
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
7
|
+
}) : x)(function(x) {
|
|
8
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
9
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
10
|
+
});
|
|
11
|
+
var __esm = (fn, res) => function __init() {
|
|
12
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
13
|
+
};
|
|
14
|
+
var __export = (target, all) => {
|
|
15
|
+
for (var name in all)
|
|
16
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
17
|
+
};
|
|
18
|
+
var __copyProps = (to, from, except, desc) => {
|
|
19
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
20
|
+
for (let key of __getOwnPropNames(from))
|
|
21
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
22
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
23
|
+
}
|
|
24
|
+
return to;
|
|
25
|
+
};
|
|
26
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
27
|
+
|
|
28
|
+
// src/types.ts
|
|
29
|
+
import { SpanType, TraceStatus } from "@observyze/types";
|
|
30
|
+
var init_types = __esm({
|
|
31
|
+
"src/types.ts"() {
|
|
32
|
+
"use strict";
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
// src/opentelemetry/exporter.ts
|
|
37
|
+
init_types();
|
|
38
|
+
|
|
39
|
+
// src/trace.ts
|
|
40
|
+
init_types();
|
|
41
|
+
import { randomUUID } from "crypto";
|
|
42
|
+
function generateId() {
|
|
43
|
+
return `${Date.now()}-${randomUUID().substring(0, 8)}`;
|
|
44
|
+
}
|
|
45
|
+
var Span = class {
|
|
46
|
+
data;
|
|
47
|
+
startTime;
|
|
48
|
+
constructor(name, type, parentSpanId) {
|
|
49
|
+
this.startTime = Date.now();
|
|
50
|
+
this.data = {
|
|
51
|
+
span_id: generateId(),
|
|
52
|
+
parent_span_id: parentSpanId,
|
|
53
|
+
name,
|
|
54
|
+
type,
|
|
55
|
+
start_time: new Date(this.startTime),
|
|
56
|
+
end_time: new Date(this.startTime),
|
|
57
|
+
// Will be updated on end()
|
|
58
|
+
duration_ms: 0,
|
|
59
|
+
input: null,
|
|
60
|
+
output: null,
|
|
61
|
+
metadata: {}
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Set the input data for this span
|
|
66
|
+
*/
|
|
67
|
+
setInput(input) {
|
|
68
|
+
this.data.input = input;
|
|
69
|
+
return this;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Set the output data for this span
|
|
73
|
+
*/
|
|
74
|
+
setOutput(output) {
|
|
75
|
+
this.data.output = output;
|
|
76
|
+
return this;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Record an error that occurred during span execution
|
|
80
|
+
*/
|
|
81
|
+
setError(error) {
|
|
82
|
+
this.data.error = {
|
|
83
|
+
message: error.message,
|
|
84
|
+
stack: error.stack,
|
|
85
|
+
code: error.code
|
|
86
|
+
};
|
|
87
|
+
return this;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Set metadata for this span
|
|
91
|
+
*/
|
|
92
|
+
setMetadata(key, value) {
|
|
93
|
+
this.data.metadata[key] = value;
|
|
94
|
+
return this;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Set multiple metadata fields at once
|
|
98
|
+
*/
|
|
99
|
+
setMetadataAll(metadata) {
|
|
100
|
+
this.data.metadata = { ...this.data.metadata, ...metadata };
|
|
101
|
+
return this;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Set token usage information
|
|
105
|
+
*/
|
|
106
|
+
setTokens(tokens) {
|
|
107
|
+
this.data.tokens = tokens;
|
|
108
|
+
return this;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* End the span and calculate duration
|
|
112
|
+
*/
|
|
113
|
+
end() {
|
|
114
|
+
const endTime = Date.now();
|
|
115
|
+
this.data.end_time = new Date(endTime);
|
|
116
|
+
this.data.duration_ms = endTime - this.startTime;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Get the span ID
|
|
120
|
+
*/
|
|
121
|
+
get id() {
|
|
122
|
+
return this.data.span_id;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Get the span data for serialization
|
|
126
|
+
*/
|
|
127
|
+
toJSON() {
|
|
128
|
+
return { ...this.data };
|
|
129
|
+
}
|
|
130
|
+
};
|
|
131
|
+
var Trace = class {
|
|
132
|
+
data;
|
|
133
|
+
startTime;
|
|
134
|
+
spans = [];
|
|
135
|
+
ended = false;
|
|
136
|
+
constructor(name, organizationId, projectId) {
|
|
137
|
+
this.startTime = Date.now();
|
|
138
|
+
this.data = {
|
|
139
|
+
trace_id: generateId(),
|
|
140
|
+
organization_id: organizationId,
|
|
141
|
+
project_id: projectId,
|
|
142
|
+
name,
|
|
143
|
+
status: TraceStatus.RUNNING,
|
|
144
|
+
start_time: new Date(this.startTime),
|
|
145
|
+
end_time: new Date(this.startTime),
|
|
146
|
+
// Will be updated on end()
|
|
147
|
+
duration_ms: 0,
|
|
148
|
+
metadata: {},
|
|
149
|
+
spans: [],
|
|
150
|
+
tags: []
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Start a new span within this trace
|
|
155
|
+
*/
|
|
156
|
+
startSpan(name, type, parentSpanId) {
|
|
157
|
+
if (this.ended) {
|
|
158
|
+
throw new Error("Cannot start span on an ended trace");
|
|
159
|
+
}
|
|
160
|
+
const span = new Span(name, type, parentSpanId);
|
|
161
|
+
this.spans.push(span);
|
|
162
|
+
return span;
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Add metadata to the trace
|
|
166
|
+
*/
|
|
167
|
+
setMetadata(key, value) {
|
|
168
|
+
this.data.metadata[key] = value;
|
|
169
|
+
return this;
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Set multiple metadata fields at once
|
|
173
|
+
*/
|
|
174
|
+
setMetadataAll(metadata) {
|
|
175
|
+
this.data.metadata = { ...this.data.metadata, ...metadata };
|
|
176
|
+
return this;
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Add tags to the trace
|
|
180
|
+
*/
|
|
181
|
+
addTag(tag) {
|
|
182
|
+
if (!this.data.tags.includes(tag)) {
|
|
183
|
+
this.data.tags.push(tag);
|
|
184
|
+
}
|
|
185
|
+
return this;
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Add multiple tags at once
|
|
189
|
+
*/
|
|
190
|
+
addTags(tags) {
|
|
191
|
+
tags.forEach((tag) => this.addTag(tag));
|
|
192
|
+
return this;
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Set the user ID associated with this trace
|
|
196
|
+
*/
|
|
197
|
+
setUserId(userId) {
|
|
198
|
+
this.data.user_id = userId;
|
|
199
|
+
return this;
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* Set the session ID associated with this trace
|
|
203
|
+
*/
|
|
204
|
+
setSessionId(sessionId) {
|
|
205
|
+
this.data.session_id = sessionId;
|
|
206
|
+
return this;
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* End the trace with a final status
|
|
210
|
+
*/
|
|
211
|
+
end(status = TraceStatus.SUCCESS) {
|
|
212
|
+
if (this.ended) {
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
const endTime = Date.now();
|
|
216
|
+
this.data.end_time = new Date(endTime);
|
|
217
|
+
this.data.duration_ms = endTime - this.startTime;
|
|
218
|
+
this.data.status = status;
|
|
219
|
+
this.data.spans = this.spans.map((span) => span.toJSON());
|
|
220
|
+
this.ended = true;
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Get the trace ID
|
|
224
|
+
*/
|
|
225
|
+
get id() {
|
|
226
|
+
return this.data.trace_id;
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* Check if the trace has ended
|
|
230
|
+
*/
|
|
231
|
+
get isEnded() {
|
|
232
|
+
return this.ended;
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* Get the trace data for serialization
|
|
236
|
+
*/
|
|
237
|
+
toJSON() {
|
|
238
|
+
return { ...this.data };
|
|
239
|
+
}
|
|
240
|
+
};
|
|
241
|
+
|
|
242
|
+
// src/opentelemetry/exporter.ts
|
|
243
|
+
var ObservyzeSpanExporter = class {
|
|
244
|
+
client;
|
|
245
|
+
config;
|
|
246
|
+
constructor(client, config) {
|
|
247
|
+
this.client = client;
|
|
248
|
+
this.config = {
|
|
249
|
+
serviceName: config?.serviceName || "unknown-service",
|
|
250
|
+
projectId: config?.projectId || "",
|
|
251
|
+
defaultSpanType: config?.defaultSpanType || "llm",
|
|
252
|
+
headers: config?.headers || {}
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
/**
|
|
256
|
+
* Export spans — called by OTel SDK when spans are ready.
|
|
257
|
+
* Converts OTel spans to Observyze traces and buffers them.
|
|
258
|
+
*/
|
|
259
|
+
async export(spans, resultCallback) {
|
|
260
|
+
if (!spans || spans.length === 0) {
|
|
261
|
+
resultCallback({ code: 0 });
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
264
|
+
try {
|
|
265
|
+
const organizationId = this.client.config?.organizationId || "";
|
|
266
|
+
const projectId = this.config.projectId || this.client.config?.projectId || "";
|
|
267
|
+
for (const span of spans) {
|
|
268
|
+
const spanContext = span.spanContext();
|
|
269
|
+
const traceId = spanContext?.traceId || span.spanId();
|
|
270
|
+
const trace = new Trace(
|
|
271
|
+
span.name || "otel-span",
|
|
272
|
+
organizationId,
|
|
273
|
+
projectId
|
|
274
|
+
);
|
|
275
|
+
trace.setMetadata("source", "opentelemetry");
|
|
276
|
+
trace.setMetadata("otel.trace_id", traceId);
|
|
277
|
+
trace.setMetadata("otel.span_id", spanContext?.spanId || "");
|
|
278
|
+
trace.setMetadata("service.name", this.config.serviceName);
|
|
279
|
+
trace.setMetadataAll(span.attributes || {});
|
|
280
|
+
if (span.resource?.attributes) {
|
|
281
|
+
trace.setMetadataAll(span.resource.attributes);
|
|
282
|
+
}
|
|
283
|
+
const input = span.attributes?.["gen_ai.prompt.0.content"] || span.attributes?.["gen_ai.completion.0.content"] || span.attributes?.["llm.input"] || void 0;
|
|
284
|
+
const output = span.attributes?.["gen_ai.completion.0.content"] || span.attributes?.["llm.output"] || void 0;
|
|
285
|
+
const model = span.attributes?.["gen_ai.request.model"] || span.attributes?.["llm.model"] || void 0;
|
|
286
|
+
const provider = span.attributes?.["gen_ai.request.provider"] || span.attributes?.["llm.provider"] || void 0;
|
|
287
|
+
const inputTokens = typeof span.attributes?.["gen_ai.usage.input_tokens"] === "number" ? span.attributes["gen_ai.usage.input_tokens"] : void 0;
|
|
288
|
+
const outputTokens = typeof span.attributes?.["gen_ai.usage.output_tokens"] === "number" ? span.attributes["gen_ai.usage.output_tokens"] : void 0;
|
|
289
|
+
trace.setMetadata("provider", provider || "unknown");
|
|
290
|
+
trace.setMetadata("model", model || "unknown");
|
|
291
|
+
const nsDuration = span.duration;
|
|
292
|
+
const durationMs = nsDuration ? Math.round(nsDuration / 1e6) : 0;
|
|
293
|
+
trace.setMetadata("latency_ms", durationMs);
|
|
294
|
+
trace.setMetadata("otel.duration_ns", nsDuration);
|
|
295
|
+
const oSpan = trace.startSpan(span.name || "otel-operation", this.config.defaultSpanType);
|
|
296
|
+
if (input) oSpan.setInput(input);
|
|
297
|
+
if (output) oSpan.setOutput(output);
|
|
298
|
+
if (inputTokens || outputTokens) {
|
|
299
|
+
oSpan.setTokens({
|
|
300
|
+
input: inputTokens || 0,
|
|
301
|
+
output: outputTokens || 0,
|
|
302
|
+
total: (inputTokens || 0) + (outputTokens || 0)
|
|
303
|
+
});
|
|
304
|
+
}
|
|
305
|
+
if (model) oSpan.setMetadata("model", model);
|
|
306
|
+
if (provider) oSpan.setMetadata("provider", provider);
|
|
307
|
+
if (span.attributes) oSpan.setMetadataAll(span.attributes);
|
|
308
|
+
const status = span.status;
|
|
309
|
+
const statusCode = status?.code;
|
|
310
|
+
if (statusCode === 2) {
|
|
311
|
+
oSpan.setError(new Error(status?.message || "OTel span error"));
|
|
312
|
+
trace.end(TraceStatus.ERROR);
|
|
313
|
+
} else {
|
|
314
|
+
trace.end(TraceStatus.SUCCESS);
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
resultCallback({ code: 0 });
|
|
318
|
+
} catch (error) {
|
|
319
|
+
resultCallback({
|
|
320
|
+
code: 1,
|
|
321
|
+
error: error instanceof Error ? error : new Error(String(error))
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
/**
|
|
326
|
+
* Called when the exporter is shut down.
|
|
327
|
+
* Flushes any remaining buffered traces via the SDK client.
|
|
328
|
+
*/
|
|
329
|
+
async shutdown() {
|
|
330
|
+
try {
|
|
331
|
+
await this.client.flush();
|
|
332
|
+
} catch {
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
/**
|
|
336
|
+
* Called by the OTel SDK to force-export buffered spans.
|
|
337
|
+
*/
|
|
338
|
+
async forceFlush() {
|
|
339
|
+
try {
|
|
340
|
+
await this.client.flush();
|
|
341
|
+
} catch {
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
};
|
|
345
|
+
|
|
346
|
+
export {
|
|
347
|
+
__require,
|
|
348
|
+
__esm,
|
|
349
|
+
__export,
|
|
350
|
+
__toCommonJS,
|
|
351
|
+
SpanType,
|
|
352
|
+
TraceStatus,
|
|
353
|
+
init_types,
|
|
354
|
+
Span,
|
|
355
|
+
Trace,
|
|
356
|
+
ObservyzeSpanExporter
|
|
357
|
+
};
|