@rivetkit/traces 2.1.0-rc.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/LICENSE +203 -0
- package/dist/schemas/v1.ts +653 -0
- package/dist/tsup/chunk-2D7JND4Z.js +63 -0
- package/dist/tsup/chunk-2D7JND4Z.js.map +1 -0
- package/dist/tsup/chunk-7RQXHEKZ.js +541 -0
- package/dist/tsup/chunk-7RQXHEKZ.js.map +1 -0
- package/dist/tsup/chunk-DXS2HLRN.cjs +63 -0
- package/dist/tsup/chunk-DXS2HLRN.cjs.map +1 -0
- package/dist/tsup/chunk-QOSSO6CN.cjs +541 -0
- package/dist/tsup/chunk-QOSSO6CN.cjs.map +1 -0
- package/dist/tsup/chunk-UNGPFJ4C.js +417 -0
- package/dist/tsup/chunk-UNGPFJ4C.js.map +1 -0
- package/dist/tsup/chunk-ZTVH74GC.cjs +417 -0
- package/dist/tsup/chunk-ZTVH74GC.cjs.map +1 -0
- package/dist/tsup/encoding.cjs +20 -0
- package/dist/tsup/encoding.cjs.map +1 -0
- package/dist/tsup/encoding.d.cts +6 -0
- package/dist/tsup/encoding.d.ts +6 -0
- package/dist/tsup/encoding.js +20 -0
- package/dist/tsup/encoding.js.map +1 -0
- package/dist/tsup/index.browser.cjs +15 -0
- package/dist/tsup/index.browser.cjs.map +1 -0
- package/dist/tsup/index.browser.d.cts +7 -0
- package/dist/tsup/index.browser.d.ts +7 -0
- package/dist/tsup/index.browser.js +15 -0
- package/dist/tsup/index.browser.js.map +1 -0
- package/dist/tsup/index.cjs +921 -0
- package/dist/tsup/index.cjs.map +1 -0
- package/dist/tsup/index.d.cts +9 -0
- package/dist/tsup/index.d.ts +9 -0
- package/dist/tsup/index.js +921 -0
- package/dist/tsup/index.js.map +1 -0
- package/dist/tsup/noop-CcgjEgCu.d.cts +99 -0
- package/dist/tsup/noop-D-YAZiGa.d.ts +99 -0
- package/dist/tsup/otlp-Da4Yz0xC.d.cts +81 -0
- package/dist/tsup/otlp-Da4Yz0xC.d.ts +81 -0
- package/dist/tsup/otlp-entry.cjs +16 -0
- package/dist/tsup/otlp-entry.cjs.map +1 -0
- package/dist/tsup/otlp-entry.d.cts +10 -0
- package/dist/tsup/otlp-entry.d.ts +10 -0
- package/dist/tsup/otlp-entry.js +16 -0
- package/dist/tsup/otlp-entry.js.map +1 -0
- package/dist/tsup/v1-DovAIc7f.d.cts +118 -0
- package/dist/tsup/v1-DovAIc7f.d.ts +118 -0
- package/package.json +74 -0
- package/schemas/v1.bare +177 -0
- package/schemas/versioned.ts +99 -0
- package/src/encoding.ts +18 -0
- package/src/index.browser.ts +13 -0
- package/src/index.ts +31 -0
- package/src/noop.ts +81 -0
- package/src/otlp-entry.ts +18 -0
- package/src/otlp.ts +158 -0
- package/src/read-range.ts +502 -0
- package/src/traces.ts +1186 -0
- package/src/types.ts +94 -0
package/src/otlp.ts
ADDED
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import { decode as decodeCbor } from "cbor-x";
|
|
2
|
+
|
|
3
|
+
export interface OtlpAnyValue {
|
|
4
|
+
stringValue?: string;
|
|
5
|
+
boolValue?: boolean;
|
|
6
|
+
intValue?: string;
|
|
7
|
+
doubleValue?: number;
|
|
8
|
+
bytesValue?: string;
|
|
9
|
+
arrayValue?: { values: OtlpAnyValue[] };
|
|
10
|
+
kvlistValue?: { values: OtlpKeyValue[] };
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface OtlpKeyValue {
|
|
14
|
+
key: string;
|
|
15
|
+
value?: OtlpAnyValue;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface OtlpSpanStatus {
|
|
19
|
+
code: number;
|
|
20
|
+
message?: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface OtlpSpanEvent {
|
|
24
|
+
timeUnixNano: string;
|
|
25
|
+
name: string;
|
|
26
|
+
attributes?: OtlpKeyValue[];
|
|
27
|
+
droppedAttributesCount?: number;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface OtlpSpanLink {
|
|
31
|
+
traceId: string;
|
|
32
|
+
spanId: string;
|
|
33
|
+
traceState?: string;
|
|
34
|
+
attributes?: OtlpKeyValue[];
|
|
35
|
+
droppedAttributesCount?: number;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface OtlpSpan {
|
|
39
|
+
traceId: string;
|
|
40
|
+
spanId: string;
|
|
41
|
+
parentSpanId?: string;
|
|
42
|
+
name: string;
|
|
43
|
+
kind?: number;
|
|
44
|
+
startTimeUnixNano: string;
|
|
45
|
+
endTimeUnixNano?: string;
|
|
46
|
+
attributes?: OtlpKeyValue[];
|
|
47
|
+
droppedAttributesCount?: number;
|
|
48
|
+
events?: OtlpSpanEvent[];
|
|
49
|
+
droppedEventsCount?: number;
|
|
50
|
+
links?: OtlpSpanLink[];
|
|
51
|
+
droppedLinksCount?: number;
|
|
52
|
+
status?: OtlpSpanStatus;
|
|
53
|
+
traceState?: string;
|
|
54
|
+
flags?: number;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export interface OtlpInstrumentationScope {
|
|
58
|
+
name: string;
|
|
59
|
+
version?: string;
|
|
60
|
+
attributes?: OtlpKeyValue[];
|
|
61
|
+
droppedAttributesCount?: number;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export interface OtlpScopeSpans {
|
|
65
|
+
scope?: OtlpInstrumentationScope;
|
|
66
|
+
spans: OtlpSpan[];
|
|
67
|
+
schemaUrl?: string;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export interface OtlpResource {
|
|
71
|
+
attributes?: OtlpKeyValue[];
|
|
72
|
+
droppedAttributesCount?: number;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export interface OtlpResourceSpans {
|
|
76
|
+
resource?: OtlpResource;
|
|
77
|
+
scopeSpans: OtlpScopeSpans[];
|
|
78
|
+
schemaUrl?: string;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export interface OtlpExportTraceServiceRequestJson {
|
|
82
|
+
resourceSpans: OtlpResourceSpans[];
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export function hexFromBytes(bytes: Uint8Array): string {
|
|
86
|
+
let out = "";
|
|
87
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
88
|
+
out += bytes[i].toString(16).padStart(2, "0");
|
|
89
|
+
}
|
|
90
|
+
return out;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export function base64FromBytes(bytes: Uint8Array): string {
|
|
94
|
+
const bufferCtor = (globalThis as { Buffer?: { from: (data: Uint8Array) => { toString: (encoding: string) => string } } }).Buffer;
|
|
95
|
+
if (bufferCtor) {
|
|
96
|
+
return bufferCtor.from(bytes).toString("base64");
|
|
97
|
+
}
|
|
98
|
+
let binary = "";
|
|
99
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
100
|
+
binary += String.fromCharCode(bytes[i]);
|
|
101
|
+
}
|
|
102
|
+
const btoaFn = (globalThis as { btoa?: (data: string) => string }).btoa;
|
|
103
|
+
if (!btoaFn) {
|
|
104
|
+
throw new Error("No base64 encoder available");
|
|
105
|
+
}
|
|
106
|
+
return btoaFn(binary);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export function anyValueFromCborBytes(bytes: Uint8Array): OtlpAnyValue {
|
|
110
|
+
const value = decodeCbor(bytes) as unknown;
|
|
111
|
+
return anyValueFromJs(value);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export function anyValueFromJs(value: unknown): OtlpAnyValue {
|
|
115
|
+
if (value === null || value === undefined) {
|
|
116
|
+
return { stringValue: "" };
|
|
117
|
+
}
|
|
118
|
+
if (typeof value === "string") {
|
|
119
|
+
return { stringValue: value };
|
|
120
|
+
}
|
|
121
|
+
if (typeof value === "boolean") {
|
|
122
|
+
return { boolValue: value };
|
|
123
|
+
}
|
|
124
|
+
if (typeof value === "number") {
|
|
125
|
+
if (Number.isFinite(value) && Number.isInteger(value)) {
|
|
126
|
+
return { intValue: value.toString() };
|
|
127
|
+
}
|
|
128
|
+
return { doubleValue: value };
|
|
129
|
+
}
|
|
130
|
+
if (typeof value === "bigint") {
|
|
131
|
+
return { intValue: value.toString() };
|
|
132
|
+
}
|
|
133
|
+
if (value instanceof Uint8Array) {
|
|
134
|
+
return { bytesValue: base64FromBytes(value) };
|
|
135
|
+
}
|
|
136
|
+
if (Array.isArray(value)) {
|
|
137
|
+
return { arrayValue: { values: value.map((v) => anyValueFromJs(v)) } };
|
|
138
|
+
}
|
|
139
|
+
if (value instanceof Map) {
|
|
140
|
+
const values: OtlpKeyValue[] = [];
|
|
141
|
+
for (const [key, mapValue] of value.entries()) {
|
|
142
|
+
if (typeof key !== "string") {
|
|
143
|
+
continue;
|
|
144
|
+
}
|
|
145
|
+
values.push({ key, value: anyValueFromJs(mapValue) });
|
|
146
|
+
}
|
|
147
|
+
return { kvlistValue: { values } };
|
|
148
|
+
}
|
|
149
|
+
if (typeof value === "object") {
|
|
150
|
+
const values: OtlpKeyValue[] = [];
|
|
151
|
+
for (const [key, objectValue] of Object.entries(value as object)) {
|
|
152
|
+
values.push({ key, value: anyValueFromJs(objectValue) });
|
|
153
|
+
}
|
|
154
|
+
return { kvlistValue: { values } };
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
return { stringValue: String(value) };
|
|
158
|
+
}
|
|
@@ -0,0 +1,502 @@
|
|
|
1
|
+
import { decode as decodeCbor } from "cbor-x";
|
|
2
|
+
import {
|
|
3
|
+
type Attributes,
|
|
4
|
+
type Chunk,
|
|
5
|
+
type ReadRangeWire,
|
|
6
|
+
type Record as TraceRecord,
|
|
7
|
+
type RecordBody,
|
|
8
|
+
type SpanId,
|
|
9
|
+
type SpanLink,
|
|
10
|
+
type SpanSnapshot,
|
|
11
|
+
type SpanStart,
|
|
12
|
+
type SpanStatus,
|
|
13
|
+
SpanStatusCode,
|
|
14
|
+
type TraceId,
|
|
15
|
+
} from "../schemas/versioned.js";
|
|
16
|
+
import {
|
|
17
|
+
anyValueFromJs,
|
|
18
|
+
hexFromBytes,
|
|
19
|
+
type OtlpExportTraceServiceRequestJson,
|
|
20
|
+
type OtlpKeyValue,
|
|
21
|
+
type OtlpResource,
|
|
22
|
+
type OtlpSpan,
|
|
23
|
+
type OtlpSpanEvent,
|
|
24
|
+
type OtlpSpanLink,
|
|
25
|
+
type OtlpSpanStatus,
|
|
26
|
+
} from "./otlp.js";
|
|
27
|
+
|
|
28
|
+
type AttributeMap = Map<string, unknown>;
|
|
29
|
+
|
|
30
|
+
type LinkState = {
|
|
31
|
+
traceId: TraceId;
|
|
32
|
+
spanId: SpanId;
|
|
33
|
+
traceState: string | null;
|
|
34
|
+
attributes: AttributeMap;
|
|
35
|
+
droppedAttributesCount: number;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
type SpanEventState = {
|
|
39
|
+
name: string;
|
|
40
|
+
timeUnixNs: bigint;
|
|
41
|
+
attributes: AttributeMap;
|
|
42
|
+
droppedAttributesCount: number;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
type SpanBuilder = {
|
|
46
|
+
traceId: TraceId;
|
|
47
|
+
spanId: SpanId;
|
|
48
|
+
parentSpanId: SpanId | null;
|
|
49
|
+
name: string;
|
|
50
|
+
kind: number;
|
|
51
|
+
traceState: string | null;
|
|
52
|
+
flags: number;
|
|
53
|
+
attributes: AttributeMap;
|
|
54
|
+
droppedAttributesCount: number;
|
|
55
|
+
links: LinkState[];
|
|
56
|
+
droppedLinksCount: number;
|
|
57
|
+
status: SpanStatus | null;
|
|
58
|
+
startTimeUnixNs: bigint;
|
|
59
|
+
endTimeUnixNs: bigint | null;
|
|
60
|
+
events: SpanEventState[];
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
type RecordEntry = {
|
|
64
|
+
record: TraceRecord;
|
|
65
|
+
strings: readonly string[];
|
|
66
|
+
absNs: bigint;
|
|
67
|
+
sequence: number;
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
type BaseRecordEntry = {
|
|
71
|
+
record: TraceRecord;
|
|
72
|
+
strings: readonly string[];
|
|
73
|
+
absNs: bigint;
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
function toUint8Array(buffer: ArrayBuffer): Uint8Array {
|
|
77
|
+
return new Uint8Array(buffer);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function normalizeBytes(input: Uint8Array | ArrayBuffer): Uint8Array {
|
|
81
|
+
return input instanceof Uint8Array ? input : new Uint8Array(input);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function spanKey(spanId: Uint8Array | SpanId): string {
|
|
85
|
+
return hexFromBytes(normalizeBytes(spanId));
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export function readRangeWireToOtlp(
|
|
89
|
+
wire: ReadRangeWire,
|
|
90
|
+
resource?: OtlpResource,
|
|
91
|
+
): { otlp: OtlpExportTraceServiceRequestJson; clamped: boolean } {
|
|
92
|
+
const startMs =
|
|
93
|
+
typeof wire.startTimeMs === "bigint"
|
|
94
|
+
? wire.startTimeMs
|
|
95
|
+
: BigInt(Math.floor(wire.startTimeMs));
|
|
96
|
+
const endMs =
|
|
97
|
+
typeof wire.endTimeMs === "bigint"
|
|
98
|
+
? wire.endTimeMs
|
|
99
|
+
: BigInt(Math.floor(wire.endTimeMs));
|
|
100
|
+
const limit =
|
|
101
|
+
typeof wire.limit === "bigint" ? Number(wire.limit) : wire.limit;
|
|
102
|
+
|
|
103
|
+
if (limit <= 0 || endMs <= startMs) {
|
|
104
|
+
return { otlp: emptyExport(resource), clamped: wire.clamped };
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const startNs = startMs * 1_000_000n;
|
|
108
|
+
const endNs = endMs * 1_000_000n;
|
|
109
|
+
const baseRecords = buildBaseRecordMap(wire.baseChunks);
|
|
110
|
+
const sequenceRef = { value: 0 };
|
|
111
|
+
const records: RecordEntry[] = [];
|
|
112
|
+
for (const chunk of wire.chunks) {
|
|
113
|
+
collectRecordEntries(records, chunk, startNs, endNs, sequenceRef);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
records.sort((a, b) => {
|
|
117
|
+
if (a.absNs < b.absNs) return -1;
|
|
118
|
+
if (a.absNs > b.absNs) return 1;
|
|
119
|
+
return a.sequence - b.sequence;
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
const { spans, reachedSpanLimit } = buildSpansFromRecords(
|
|
123
|
+
records,
|
|
124
|
+
baseRecords,
|
|
125
|
+
limit,
|
|
126
|
+
);
|
|
127
|
+
const exported = spans.map(toOtlpSpan);
|
|
128
|
+
return {
|
|
129
|
+
otlp: buildExport(exported, resource),
|
|
130
|
+
clamped: wire.clamped || reachedSpanLimit,
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function collectRecordEntries(
|
|
135
|
+
collector: RecordEntry[],
|
|
136
|
+
chunk: Chunk,
|
|
137
|
+
startNs: bigint,
|
|
138
|
+
endNs: bigint,
|
|
139
|
+
sequenceRef: { value: number },
|
|
140
|
+
): void {
|
|
141
|
+
for (const record of chunk.records) {
|
|
142
|
+
const absNs = chunk.baseUnixNs + record.timeOffsetNs;
|
|
143
|
+
if (absNs < startNs || absNs >= endNs) {
|
|
144
|
+
continue;
|
|
145
|
+
}
|
|
146
|
+
collector.push({
|
|
147
|
+
record,
|
|
148
|
+
strings: chunk.strings,
|
|
149
|
+
absNs,
|
|
150
|
+
sequence: sequenceRef.value++,
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function buildBaseRecordMap(
|
|
156
|
+
chunks: readonly Chunk[],
|
|
157
|
+
): Map<string, BaseRecordEntry> {
|
|
158
|
+
const map = new Map<string, BaseRecordEntry>();
|
|
159
|
+
for (const chunk of chunks) {
|
|
160
|
+
for (const record of chunk.records) {
|
|
161
|
+
const absNs = chunk.baseUnixNs + record.timeOffsetNs;
|
|
162
|
+
map.set(spanKey(recordSpanId(record.body)), {
|
|
163
|
+
record,
|
|
164
|
+
strings: chunk.strings,
|
|
165
|
+
absNs,
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
return map;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function buildSpansFromRecords(
|
|
173
|
+
records: RecordEntry[],
|
|
174
|
+
baseRecords: Map<string, BaseRecordEntry>,
|
|
175
|
+
limit: number,
|
|
176
|
+
): { spans: SpanBuilder[]; reachedSpanLimit: boolean } {
|
|
177
|
+
const spans = new Map<string, SpanBuilder>();
|
|
178
|
+
let reachedSpanLimit = false;
|
|
179
|
+
|
|
180
|
+
for (const entry of records) {
|
|
181
|
+
const body = entry.record.body;
|
|
182
|
+
const id = recordSpanId(body);
|
|
183
|
+
const key = spanKey(id);
|
|
184
|
+
let span = spans.get(key);
|
|
185
|
+
if (!span) {
|
|
186
|
+
if (spans.size >= limit) {
|
|
187
|
+
reachedSpanLimit = true;
|
|
188
|
+
continue;
|
|
189
|
+
}
|
|
190
|
+
const baseRecord = baseRecords.get(key);
|
|
191
|
+
if (baseRecord) {
|
|
192
|
+
span = initSpanFromBaseRecord(
|
|
193
|
+
baseRecord.record.body,
|
|
194
|
+
baseRecord.strings,
|
|
195
|
+
baseRecord.absNs,
|
|
196
|
+
);
|
|
197
|
+
}
|
|
198
|
+
if (!span) {
|
|
199
|
+
span = initSpanFromRecord(
|
|
200
|
+
body,
|
|
201
|
+
entry.absNs,
|
|
202
|
+
entry.strings,
|
|
203
|
+
);
|
|
204
|
+
}
|
|
205
|
+
if (!span) {
|
|
206
|
+
continue;
|
|
207
|
+
}
|
|
208
|
+
spans.set(key, span);
|
|
209
|
+
}
|
|
210
|
+
applyRecord(span, body, entry.absNs, entry.strings);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
return { spans: Array.from(spans.values()), reachedSpanLimit };
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
function recordSpanId(body: RecordBody): SpanId {
|
|
217
|
+
switch (body.tag) {
|
|
218
|
+
case "SpanStart":
|
|
219
|
+
return body.val.spanId;
|
|
220
|
+
case "SpanEvent":
|
|
221
|
+
return body.val.spanId;
|
|
222
|
+
case "SpanUpdate":
|
|
223
|
+
return body.val.spanId;
|
|
224
|
+
case "SpanEnd":
|
|
225
|
+
return body.val.spanId;
|
|
226
|
+
case "SpanSnapshot":
|
|
227
|
+
return body.val.spanId;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
function initSpanFromBaseRecord(
|
|
232
|
+
base: RecordBody,
|
|
233
|
+
strings: readonly string[],
|
|
234
|
+
absNs: bigint,
|
|
235
|
+
): SpanBuilder | undefined {
|
|
236
|
+
switch (base.tag) {
|
|
237
|
+
case "SpanStart":
|
|
238
|
+
return initSpanFromStart(base.val, absNs, strings);
|
|
239
|
+
case "SpanSnapshot":
|
|
240
|
+
return initSpanFromSnapshot(base.val, strings);
|
|
241
|
+
default:
|
|
242
|
+
return undefined;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
function initSpanFromRecord(
|
|
247
|
+
body: RecordBody,
|
|
248
|
+
absNs: bigint,
|
|
249
|
+
strings: readonly string[],
|
|
250
|
+
): SpanBuilder | undefined {
|
|
251
|
+
switch (body.tag) {
|
|
252
|
+
case "SpanStart":
|
|
253
|
+
return initSpanFromStart(body.val, absNs, strings);
|
|
254
|
+
case "SpanSnapshot":
|
|
255
|
+
return initSpanFromSnapshot(body.val, strings);
|
|
256
|
+
default:
|
|
257
|
+
return undefined;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
function initSpanFromStart(
|
|
262
|
+
start: SpanStart,
|
|
263
|
+
absNs: bigint | null,
|
|
264
|
+
strings: readonly string[],
|
|
265
|
+
): SpanBuilder {
|
|
266
|
+
return {
|
|
267
|
+
traceId: start.traceId,
|
|
268
|
+
spanId: start.spanId,
|
|
269
|
+
parentSpanId: start.parentSpanId,
|
|
270
|
+
name: strings[start.name] ?? "",
|
|
271
|
+
kind: start.kind,
|
|
272
|
+
traceState: start.traceState,
|
|
273
|
+
flags: start.flags,
|
|
274
|
+
attributes: decodeAttributeList(start.attributes, strings),
|
|
275
|
+
droppedAttributesCount: start.droppedAttributesCount,
|
|
276
|
+
links: decodeLinks(start.links, strings),
|
|
277
|
+
droppedLinksCount: start.droppedLinksCount,
|
|
278
|
+
status: null,
|
|
279
|
+
startTimeUnixNs: absNs ?? 0n,
|
|
280
|
+
endTimeUnixNs: null,
|
|
281
|
+
events: [],
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
function initSpanFromSnapshot(
|
|
286
|
+
snapshot: SpanSnapshot,
|
|
287
|
+
strings: readonly string[],
|
|
288
|
+
): SpanBuilder {
|
|
289
|
+
return {
|
|
290
|
+
traceId: snapshot.traceId,
|
|
291
|
+
spanId: snapshot.spanId,
|
|
292
|
+
parentSpanId: snapshot.parentSpanId,
|
|
293
|
+
name: strings[snapshot.name] ?? "",
|
|
294
|
+
kind: snapshot.kind,
|
|
295
|
+
traceState: snapshot.traceState,
|
|
296
|
+
flags: snapshot.flags,
|
|
297
|
+
attributes: decodeAttributeList(snapshot.attributes, strings),
|
|
298
|
+
droppedAttributesCount: snapshot.droppedAttributesCount,
|
|
299
|
+
links: decodeLinks(snapshot.links, strings),
|
|
300
|
+
droppedLinksCount: snapshot.droppedLinksCount,
|
|
301
|
+
status: snapshot.status,
|
|
302
|
+
startTimeUnixNs: snapshot.startTimeUnixNs,
|
|
303
|
+
endTimeUnixNs: null,
|
|
304
|
+
events: [],
|
|
305
|
+
};
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
function applyRecord(
|
|
309
|
+
span: SpanBuilder,
|
|
310
|
+
body: RecordBody,
|
|
311
|
+
absNs: bigint,
|
|
312
|
+
strings: readonly string[],
|
|
313
|
+
): void {
|
|
314
|
+
switch (body.tag) {
|
|
315
|
+
case "SpanStart":
|
|
316
|
+
if (span.startTimeUnixNs === 0n) {
|
|
317
|
+
span.startTimeUnixNs = absNs;
|
|
318
|
+
}
|
|
319
|
+
return;
|
|
320
|
+
case "SpanSnapshot":
|
|
321
|
+
span.traceId = body.val.traceId;
|
|
322
|
+
span.parentSpanId = body.val.parentSpanId;
|
|
323
|
+
span.name = strings[body.val.name] ?? "";
|
|
324
|
+
span.kind = body.val.kind;
|
|
325
|
+
span.traceState = body.val.traceState;
|
|
326
|
+
span.flags = body.val.flags;
|
|
327
|
+
span.attributes = decodeAttributeList(
|
|
328
|
+
body.val.attributes,
|
|
329
|
+
strings,
|
|
330
|
+
);
|
|
331
|
+
span.droppedAttributesCount = body.val.droppedAttributesCount;
|
|
332
|
+
span.links = decodeLinks(body.val.links, strings);
|
|
333
|
+
span.droppedLinksCount = body.val.droppedLinksCount;
|
|
334
|
+
span.status = body.val.status;
|
|
335
|
+
span.startTimeUnixNs = body.val.startTimeUnixNs;
|
|
336
|
+
return;
|
|
337
|
+
case "SpanUpdate":
|
|
338
|
+
applyAttributes(span.attributes, body.val.attributes, strings);
|
|
339
|
+
span.droppedAttributesCount += body.val.droppedAttributesCount;
|
|
340
|
+
if (body.val.status) {
|
|
341
|
+
span.status = body.val.status;
|
|
342
|
+
}
|
|
343
|
+
return;
|
|
344
|
+
case "SpanEvent":
|
|
345
|
+
span.events.push({
|
|
346
|
+
name: strings[body.val.name] ?? "",
|
|
347
|
+
timeUnixNs: absNs,
|
|
348
|
+
attributes: decodeAttributeList(
|
|
349
|
+
body.val.attributes,
|
|
350
|
+
strings,
|
|
351
|
+
),
|
|
352
|
+
droppedAttributesCount: body.val.droppedAttributesCount,
|
|
353
|
+
});
|
|
354
|
+
return;
|
|
355
|
+
case "SpanEnd":
|
|
356
|
+
span.endTimeUnixNs = absNs;
|
|
357
|
+
if (body.val.status) {
|
|
358
|
+
span.status = body.val.status;
|
|
359
|
+
}
|
|
360
|
+
return;
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
function decodeAttributeList(
|
|
365
|
+
attributes: Attributes,
|
|
366
|
+
strings: readonly string[],
|
|
367
|
+
): AttributeMap {
|
|
368
|
+
const map = new Map<string, unknown>();
|
|
369
|
+
for (const kv of attributes) {
|
|
370
|
+
const key = strings[kv.key] ?? "";
|
|
371
|
+
try {
|
|
372
|
+
map.set(key, decodeCbor(toUint8Array(kv.value)) as unknown);
|
|
373
|
+
} catch {
|
|
374
|
+
continue;
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
return map;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
function applyAttributes(
|
|
381
|
+
map: AttributeMap,
|
|
382
|
+
attributes: Attributes,
|
|
383
|
+
strings: readonly string[],
|
|
384
|
+
): void {
|
|
385
|
+
for (const kv of attributes) {
|
|
386
|
+
const key = strings[kv.key] ?? "";
|
|
387
|
+
try {
|
|
388
|
+
map.set(key, decodeCbor(toUint8Array(kv.value)) as unknown);
|
|
389
|
+
} catch {
|
|
390
|
+
continue;
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
function decodeLinks(
|
|
396
|
+
links: readonly SpanLink[],
|
|
397
|
+
strings: readonly string[],
|
|
398
|
+
): LinkState[] {
|
|
399
|
+
return links.map((link) => ({
|
|
400
|
+
traceId: link.traceId,
|
|
401
|
+
spanId: link.spanId,
|
|
402
|
+
traceState: link.traceState,
|
|
403
|
+
attributes: decodeAttributeList(link.attributes, strings),
|
|
404
|
+
droppedAttributesCount: link.droppedAttributesCount,
|
|
405
|
+
}));
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
function toOtlpSpan(span: SpanBuilder): OtlpSpan {
|
|
409
|
+
const attributes = mapToOtlpAttributes(span.attributes);
|
|
410
|
+
const events = span.events.map((event) => toOtlpEvent(event));
|
|
411
|
+
const links = span.links.map((link) => toOtlpLink(link));
|
|
412
|
+
const status = span.status ? toOtlpStatus(span.status) : undefined;
|
|
413
|
+
return {
|
|
414
|
+
traceId: hexFromBytes(normalizeBytes(span.traceId)),
|
|
415
|
+
spanId: hexFromBytes(normalizeBytes(span.spanId)),
|
|
416
|
+
parentSpanId: span.parentSpanId
|
|
417
|
+
? hexFromBytes(normalizeBytes(span.parentSpanId))
|
|
418
|
+
: undefined,
|
|
419
|
+
name: span.name,
|
|
420
|
+
kind: span.kind,
|
|
421
|
+
traceState: span.traceState ?? undefined,
|
|
422
|
+
flags: span.flags || undefined,
|
|
423
|
+
startTimeUnixNano: span.startTimeUnixNs.toString(),
|
|
424
|
+
endTimeUnixNano: span.endTimeUnixNs
|
|
425
|
+
? span.endTimeUnixNs.toString()
|
|
426
|
+
: undefined,
|
|
427
|
+
attributes: attributes.length > 0 ? attributes : undefined,
|
|
428
|
+
droppedAttributesCount: span.droppedAttributesCount || undefined,
|
|
429
|
+
events: events.length > 0 ? events : undefined,
|
|
430
|
+
links: links.length > 0 ? links : undefined,
|
|
431
|
+
droppedLinksCount: span.droppedLinksCount || undefined,
|
|
432
|
+
status,
|
|
433
|
+
};
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
function toOtlpEvent(event: SpanEventState): OtlpSpanEvent {
|
|
437
|
+
const attributes = mapToOtlpAttributes(event.attributes);
|
|
438
|
+
return {
|
|
439
|
+
timeUnixNano: event.timeUnixNs.toString(),
|
|
440
|
+
name: event.name,
|
|
441
|
+
attributes: attributes.length > 0 ? attributes : undefined,
|
|
442
|
+
droppedAttributesCount: event.droppedAttributesCount || undefined,
|
|
443
|
+
};
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
function toOtlpLink(link: LinkState): OtlpSpanLink {
|
|
447
|
+
const attributes = mapToOtlpAttributes(link.attributes);
|
|
448
|
+
return {
|
|
449
|
+
traceId: hexFromBytes(normalizeBytes(link.traceId)),
|
|
450
|
+
spanId: hexFromBytes(normalizeBytes(link.spanId)),
|
|
451
|
+
traceState: link.traceState ?? undefined,
|
|
452
|
+
attributes: attributes.length > 0 ? attributes : undefined,
|
|
453
|
+
droppedAttributesCount: link.droppedAttributesCount || undefined,
|
|
454
|
+
};
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
function toOtlpStatus(status: SpanStatus): OtlpSpanStatus {
|
|
458
|
+
const code =
|
|
459
|
+
status.code === SpanStatusCode.OK
|
|
460
|
+
? 1
|
|
461
|
+
: status.code === SpanStatusCode.ERROR
|
|
462
|
+
? 2
|
|
463
|
+
: 0;
|
|
464
|
+
return {
|
|
465
|
+
code,
|
|
466
|
+
message: status.message ?? undefined,
|
|
467
|
+
};
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
function mapToOtlpAttributes(map: AttributeMap): OtlpKeyValue[] {
|
|
471
|
+
const list: OtlpKeyValue[] = [];
|
|
472
|
+
for (const [key, value] of map.entries()) {
|
|
473
|
+
if (value === undefined || typeof value === "function") {
|
|
474
|
+
continue;
|
|
475
|
+
}
|
|
476
|
+
if (typeof value === "symbol") {
|
|
477
|
+
continue;
|
|
478
|
+
}
|
|
479
|
+
list.push({ key, value: anyValueFromJs(value) });
|
|
480
|
+
}
|
|
481
|
+
return list;
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
function emptyExport(
|
|
485
|
+
resourceValue?: OtlpResource,
|
|
486
|
+
): OtlpExportTraceServiceRequestJson {
|
|
487
|
+
return buildExport([], resourceValue);
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
function buildExport(
|
|
491
|
+
spans: OtlpSpan[],
|
|
492
|
+
resourceValue?: OtlpResource,
|
|
493
|
+
): OtlpExportTraceServiceRequestJson {
|
|
494
|
+
return {
|
|
495
|
+
resourceSpans: [
|
|
496
|
+
{
|
|
497
|
+
resource: resourceValue,
|
|
498
|
+
scopeSpans: [{ spans }],
|
|
499
|
+
},
|
|
500
|
+
],
|
|
501
|
+
};
|
|
502
|
+
}
|