@hue-run/sdk 0.1.4 → 0.1.5
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/README.md +11 -4
- package/dist/ai-sdk.js +10 -1
- package/dist/client.d.ts +11 -1
- package/dist/client.js +232 -97
- package/dist/config.d.ts +1 -1
- package/dist/config.js +18 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/privacy.js +38 -20
- package/dist/safety.d.ts +8 -0
- package/dist/safety.js +179 -0
- package/dist/snapshot.d.ts +12 -0
- package/dist/snapshot.js +196 -0
- package/dist/transport.d.ts +6 -0
- package/dist/transport.js +192 -32
- package/dist/types.d.ts +27 -4
- package/package.json +2 -2
package/dist/privacy.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { resourceFromAttributes } from "@opentelemetry/resources";
|
|
2
|
-
import { MAX_CONTENT_BYTES } from "./config.js";
|
|
2
|
+
import { MAX_BODY_BYTES, MAX_CONTENT_BYTES } from "./config.js";
|
|
3
3
|
const contentPrefixes = [
|
|
4
4
|
"gen_ai.input.messages",
|
|
5
5
|
"gen_ai.output.messages",
|
|
@@ -38,45 +38,62 @@ const contentPrefixes = [
|
|
|
38
38
|
function isContentKey(key) {
|
|
39
39
|
return contentPrefixes.some((prefix) => key === prefix || key.startsWith(`${prefix}.`));
|
|
40
40
|
}
|
|
41
|
-
function redactValue(value, path, options, depth = 0) {
|
|
42
|
-
if (depth > 32)
|
|
41
|
+
function redactValue(value, path, options, budget, depth = 0) {
|
|
42
|
+
if (++budget.nodes > 16384 || depth > 32)
|
|
43
43
|
throw new Error("Telemetry value exceeds the supported nesting limit");
|
|
44
44
|
if (typeof value === "string") {
|
|
45
45
|
const result = options.redact ? options.redact(value, path) : value;
|
|
46
|
-
|
|
46
|
+
// JavaScript can supply an async redactor despite the synchronous contract.
|
|
47
|
+
// Observe its rejection before dropping the invalid record.
|
|
48
|
+
if (result && typeof result === "object")
|
|
49
|
+
void Promise.resolve(result).catch(() => { });
|
|
50
|
+
if (typeof result !== "string" ||
|
|
51
|
+
result.length > MAX_CONTENT_BYTES ||
|
|
52
|
+
!result.isWellFormed() ||
|
|
53
|
+
result.includes("\u0000"))
|
|
47
54
|
throw new Error("Redaction produced unsupported text");
|
|
48
55
|
if (Buffer.byteLength(result) > MAX_CONTENT_BYTES)
|
|
49
56
|
throw new Error("Telemetry text exceeds 256 KiB");
|
|
57
|
+
budget.bytes += Buffer.byteLength(result);
|
|
58
|
+
if (budget.bytes > MAX_BODY_BYTES)
|
|
59
|
+
throw new Error("Redacted record exceeds the content budget");
|
|
50
60
|
return result;
|
|
51
61
|
}
|
|
52
|
-
if (Array.isArray(value))
|
|
53
|
-
|
|
62
|
+
if (Array.isArray(value)) {
|
|
63
|
+
if (value.length > 16384)
|
|
64
|
+
throw new Error("Telemetry array exceeds the complexity limit");
|
|
65
|
+
return value.map((item, index) => redactValue(item, `${path}.${index}`, options, budget, depth + 1));
|
|
66
|
+
}
|
|
54
67
|
if (value instanceof Uint8Array) {
|
|
55
68
|
if (value.byteLength > MAX_CONTENT_BYTES)
|
|
56
69
|
throw new Error("Telemetry bytes exceed 256 KiB");
|
|
70
|
+
budget.bytes += value.byteLength;
|
|
71
|
+
if (budget.bytes > MAX_BODY_BYTES)
|
|
72
|
+
throw new Error("Redacted record exceeds the content budget");
|
|
57
73
|
return value;
|
|
58
74
|
}
|
|
59
75
|
if (value !== null && typeof value === "object")
|
|
60
76
|
return Object.fromEntries(Object.entries(value).map(([key, item]) => [
|
|
61
77
|
key,
|
|
62
|
-
redactValue(item, `${path}.${key}`, options, depth + 1),
|
|
78
|
+
redactValue(item, `${path}.${key}`, options, budget, depth + 1),
|
|
63
79
|
]));
|
|
64
80
|
return value;
|
|
65
81
|
}
|
|
66
|
-
function attributes(source, options, path) {
|
|
82
|
+
function attributes(source, options, path, budget) {
|
|
67
83
|
return Object.fromEntries(Object.entries(source).flatMap(([key, value]) => !options.captureContent && isContentKey(key)
|
|
68
84
|
? []
|
|
69
|
-
: [[key, redactValue(value, `${path}.${key}`, options)]]));
|
|
85
|
+
: [[key, redactValue(value, `${path}.${key}`, options, budget)]]));
|
|
70
86
|
}
|
|
71
|
-
function redactResource(resource, options, cache) {
|
|
87
|
+
function redactResource(resource, options, cache, budget) {
|
|
72
88
|
let result = cache.get(resource);
|
|
73
89
|
if (!result) {
|
|
74
|
-
result = resourceFromAttributes(attributes(resource.attributes, options, "resource.attributes"), { schemaUrl: resource.schemaUrl });
|
|
90
|
+
result = resourceFromAttributes(attributes(resource.attributes, options, "resource.attributes", budget), { schemaUrl: resource.schemaUrl });
|
|
75
91
|
cache.set(resource, result);
|
|
76
92
|
}
|
|
77
93
|
return result;
|
|
78
94
|
}
|
|
79
95
|
export function redactSpan(span, options, cache) {
|
|
96
|
+
const budget = { bytes: 0, nodes: 0 };
|
|
80
97
|
return {
|
|
81
98
|
name: span.name,
|
|
82
99
|
kind: span.kind,
|
|
@@ -89,22 +106,22 @@ export function redactSpan(span, options, cache) {
|
|
|
89
106
|
status: {
|
|
90
107
|
code: span.status.code,
|
|
91
108
|
...(options.captureContent && span.status.message !== undefined
|
|
92
|
-
? { message: String(redactValue(span.status.message, "status.message", options)) }
|
|
109
|
+
? { message: String(redactValue(span.status.message, "status.message", options, budget)) }
|
|
93
110
|
: {}),
|
|
94
111
|
},
|
|
95
|
-
attributes: attributes(span.attributes, options, "attributes"),
|
|
112
|
+
attributes: attributes(span.attributes, options, "attributes", budget),
|
|
96
113
|
events: span.events
|
|
97
114
|
.filter((event) => options.captureContent ||
|
|
98
115
|
!/^gen_ai\.(?:system|user|assistant|tool|choice)/.test(event.name))
|
|
99
116
|
.map((event) => ({
|
|
100
117
|
...event,
|
|
101
|
-
attributes: attributes(event.attributes ?? {}, options, `events.${event.name}
|
|
118
|
+
attributes: attributes(event.attributes ?? {}, options, `events.${event.name}`, budget),
|
|
102
119
|
})),
|
|
103
120
|
links: span.links.map((link) => ({
|
|
104
121
|
...link,
|
|
105
|
-
attributes: attributes(link.attributes ?? {}, options, "links.attributes"),
|
|
122
|
+
attributes: attributes(link.attributes ?? {}, options, "links.attributes", budget),
|
|
106
123
|
})),
|
|
107
|
-
resource: redactResource(span.resource, options, cache),
|
|
124
|
+
resource: redactResource(span.resource, options, cache, budget),
|
|
108
125
|
instrumentationScope: span.instrumentationScope,
|
|
109
126
|
droppedAttributesCount: span.droppedAttributesCount,
|
|
110
127
|
droppedEventsCount: span.droppedEventsCount,
|
|
@@ -112,7 +129,8 @@ export function redactSpan(span, options, cache) {
|
|
|
112
129
|
};
|
|
113
130
|
}
|
|
114
131
|
export function redactLog(log, options, cache) {
|
|
115
|
-
const
|
|
132
|
+
const budget = { bytes: 0, nodes: 0 };
|
|
133
|
+
const body = options.captureContent ? redactValue(log.body, "body", options, budget) : undefined;
|
|
116
134
|
if (body !== undefined && Buffer.byteLength(JSON.stringify(body)) > MAX_CONTENT_BYTES)
|
|
117
135
|
throw new Error("Telemetry log body exceeds 256 KiB");
|
|
118
136
|
return {
|
|
@@ -123,13 +141,13 @@ export function redactLog(log, options, cache) {
|
|
|
123
141
|
severityNumber: log.severityNumber,
|
|
124
142
|
eventName: log.eventName,
|
|
125
143
|
body: body,
|
|
126
|
-
attributes: attributes(log.attributes, options, "attributes"),
|
|
127
|
-
resource: redactResource(log.resource, options, cache),
|
|
144
|
+
attributes: attributes(log.attributes, options, "attributes", budget),
|
|
145
|
+
resource: redactResource(log.resource, options, cache, budget),
|
|
128
146
|
instrumentationScope: {
|
|
129
147
|
...log.instrumentationScope,
|
|
130
148
|
...(log.instrumentationScope.attributes
|
|
131
149
|
? {
|
|
132
|
-
attributes: attributes(log.instrumentationScope.attributes, options, "scope.attributes"),
|
|
150
|
+
attributes: attributes(log.instrumentationScope.attributes, options, "scope.attributes", budget),
|
|
133
151
|
}
|
|
134
152
|
: {}),
|
|
135
153
|
},
|
package/dist/safety.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type Span } from "@opentelemetry/api";
|
|
2
|
+
import type { JsonValue } from "./types.js";
|
|
3
|
+
export declare function noopSpan(): Span;
|
|
4
|
+
export declare function safeSpan(source: Span, failed: () => void): Span;
|
|
5
|
+
/** Validate a bounded data tree without invoking toJSON or property getters. */
|
|
6
|
+
export declare function encodeContent(value: JsonValue): string;
|
|
7
|
+
/** Bounded conservative accounting for the record data retained by our queue, not total process RSS. */
|
|
8
|
+
export declare function estimateRecordBytes(value: unknown, limit: number): number;
|
package/dist/safety.js
ADDED
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
import { INVALID_SPAN_CONTEXT, trace } from "@opentelemetry/api";
|
|
2
|
+
import { types as utilTypes } from "node:util";
|
|
3
|
+
import { MAX_CONTENT_BYTES } from "./config.js";
|
|
4
|
+
export function noopSpan() {
|
|
5
|
+
return trace.wrapSpanContext(INVALID_SPAN_CONTEXT);
|
|
6
|
+
}
|
|
7
|
+
/** Isolate the public Span interface without changing its fluent method contract. */
|
|
8
|
+
class SafeSpan {
|
|
9
|
+
source;
|
|
10
|
+
failed;
|
|
11
|
+
constructor(source, failed) {
|
|
12
|
+
this.source = source;
|
|
13
|
+
this.failed = failed;
|
|
14
|
+
}
|
|
15
|
+
write(work) {
|
|
16
|
+
try {
|
|
17
|
+
const result = work();
|
|
18
|
+
// Broken/custom providers can return rejected promises from synchronous
|
|
19
|
+
// OTel methods. Observe them without awaiting on application code paths.
|
|
20
|
+
if (result && typeof result.then === "function")
|
|
21
|
+
void Promise.resolve(result).catch(this.failed);
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
this.failed();
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
spanContext() {
|
|
28
|
+
try {
|
|
29
|
+
const ids = this.source.spanContext();
|
|
30
|
+
if (!ids || typeof ids.traceId !== "string" || typeof ids.spanId !== "string")
|
|
31
|
+
throw new TypeError("Invalid span context");
|
|
32
|
+
return {
|
|
33
|
+
traceId: ids.traceId,
|
|
34
|
+
spanId: ids.spanId,
|
|
35
|
+
traceFlags: ids.traceFlags,
|
|
36
|
+
isRemote: ids.isRemote,
|
|
37
|
+
traceState: ids.traceState,
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
this.failed();
|
|
42
|
+
return INVALID_SPAN_CONTEXT;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
isRecording() {
|
|
46
|
+
try {
|
|
47
|
+
return this.source.isRecording() === true;
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
this.failed();
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
setAttribute(...args) {
|
|
55
|
+
this.write(() => this.source.setAttribute(...args));
|
|
56
|
+
return this;
|
|
57
|
+
}
|
|
58
|
+
setAttributes(...args) {
|
|
59
|
+
this.write(() => this.source.setAttributes(...args));
|
|
60
|
+
return this;
|
|
61
|
+
}
|
|
62
|
+
addEvent(...args) {
|
|
63
|
+
this.write(() => this.source.addEvent(...args));
|
|
64
|
+
return this;
|
|
65
|
+
}
|
|
66
|
+
addLink(...args) {
|
|
67
|
+
this.write(() => this.source.addLink(...args));
|
|
68
|
+
return this;
|
|
69
|
+
}
|
|
70
|
+
addLinks(...args) {
|
|
71
|
+
this.write(() => this.source.addLinks(...args));
|
|
72
|
+
return this;
|
|
73
|
+
}
|
|
74
|
+
setStatus(...args) {
|
|
75
|
+
this.write(() => this.source.setStatus(...args));
|
|
76
|
+
return this;
|
|
77
|
+
}
|
|
78
|
+
updateName(...args) {
|
|
79
|
+
this.write(() => this.source.updateName(...args));
|
|
80
|
+
return this;
|
|
81
|
+
}
|
|
82
|
+
end(...args) {
|
|
83
|
+
this.write(() => this.source.end(...args));
|
|
84
|
+
}
|
|
85
|
+
recordException(...args) {
|
|
86
|
+
this.write(() => this.source.recordException(...args));
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
export function safeSpan(source, failed) {
|
|
90
|
+
return new SafeSpan(source, failed);
|
|
91
|
+
}
|
|
92
|
+
/** Validate a bounded data tree without invoking toJSON or property getters. */
|
|
93
|
+
export function encodeContent(value) {
|
|
94
|
+
let nodes = 0;
|
|
95
|
+
let bytes = 0;
|
|
96
|
+
const ancestors = new Set();
|
|
97
|
+
const charge = (amount) => {
|
|
98
|
+
bytes += amount;
|
|
99
|
+
if (bytes > MAX_CONTENT_BYTES)
|
|
100
|
+
throw new RangeError("Content limit exceeded");
|
|
101
|
+
};
|
|
102
|
+
const visit = (item, depth) => {
|
|
103
|
+
if (++nodes > 16384 || depth > 32)
|
|
104
|
+
throw new RangeError("Content complexity limit exceeded");
|
|
105
|
+
if (typeof item === "string") {
|
|
106
|
+
if (item.length > MAX_CONTENT_BYTES)
|
|
107
|
+
throw new RangeError("Content limit exceeded");
|
|
108
|
+
charge(Buffer.byteLength(JSON.stringify(item)));
|
|
109
|
+
return item;
|
|
110
|
+
}
|
|
111
|
+
if (item === null || typeof item === "boolean" || typeof item === "number") {
|
|
112
|
+
if (typeof item === "number" && !Number.isFinite(item))
|
|
113
|
+
throw new TypeError("Invalid number");
|
|
114
|
+
charge(JSON.stringify(item).length);
|
|
115
|
+
return item;
|
|
116
|
+
}
|
|
117
|
+
if (!item || typeof item !== "object" || ancestors.has(item))
|
|
118
|
+
throw new TypeError("Invalid JSON");
|
|
119
|
+
// Even descriptor/prototype reads can execute application code on a Proxy.
|
|
120
|
+
// The native check also handles revoked proxies without invoking their traps.
|
|
121
|
+
if (utilTypes.isProxy(item))
|
|
122
|
+
throw new TypeError("JSON proxies are unsupported");
|
|
123
|
+
const array = Array.isArray(item);
|
|
124
|
+
if (!array && ![Object.prototype, null].includes(Object.getPrototypeOf(item)))
|
|
125
|
+
throw new TypeError("Expected JSON data");
|
|
126
|
+
ancestors.add(item);
|
|
127
|
+
charge(2);
|
|
128
|
+
const result = array ? [] : Object.create(null);
|
|
129
|
+
// Own descriptors avoid executing application accessors during capture.
|
|
130
|
+
const keys = array
|
|
131
|
+
? Array.from({ length: Math.min(item.length, 16385) }, (_, i) => String(i))
|
|
132
|
+
: Object.keys(item);
|
|
133
|
+
if (keys.length > 16384)
|
|
134
|
+
throw new RangeError("Content complexity limit exceeded");
|
|
135
|
+
for (const key of keys) {
|
|
136
|
+
const descriptor = Object.getOwnPropertyDescriptor(item, key);
|
|
137
|
+
if (!descriptor || !("value" in descriptor))
|
|
138
|
+
throw new TypeError("Expected JSON data property");
|
|
139
|
+
charge(1 + (array ? 0 : Buffer.byteLength(JSON.stringify(key)) + 1));
|
|
140
|
+
const child = visit(descriptor.value, depth + 1);
|
|
141
|
+
if (array)
|
|
142
|
+
result.push(child);
|
|
143
|
+
else
|
|
144
|
+
result[key] = child;
|
|
145
|
+
}
|
|
146
|
+
ancestors.delete(item);
|
|
147
|
+
return result;
|
|
148
|
+
};
|
|
149
|
+
const encoded = JSON.stringify(visit(value, 0));
|
|
150
|
+
if (Buffer.byteLength(encoded) > MAX_CONTENT_BYTES)
|
|
151
|
+
throw new RangeError("Content limit exceeded");
|
|
152
|
+
return encoded;
|
|
153
|
+
}
|
|
154
|
+
/** Bounded conservative accounting for the record data retained by our queue, not total process RSS. */
|
|
155
|
+
export function estimateRecordBytes(value, limit) {
|
|
156
|
+
let bytes = 0;
|
|
157
|
+
let nodes = 0;
|
|
158
|
+
const seen = new Set();
|
|
159
|
+
const visit = (item, depth) => {
|
|
160
|
+
if (++nodes > 16384 || depth > 32)
|
|
161
|
+
throw new RangeError("Telemetry complexity limit exceeded");
|
|
162
|
+
bytes += 16;
|
|
163
|
+
if (typeof item === "string")
|
|
164
|
+
bytes += item.length * 2;
|
|
165
|
+
else if (item instanceof Uint8Array)
|
|
166
|
+
bytes += item.byteLength;
|
|
167
|
+
else if (item && typeof item === "object" && !seen.has(item)) {
|
|
168
|
+
seen.add(item);
|
|
169
|
+
for (const [key, child] of Object.entries(item)) {
|
|
170
|
+
bytes += key.length * 2 + 16;
|
|
171
|
+
visit(child, depth + 1);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
if (bytes > limit)
|
|
175
|
+
throw new RangeError("Telemetry byte limit exceeded");
|
|
176
|
+
};
|
|
177
|
+
visit(value, 0);
|
|
178
|
+
return bytes;
|
|
179
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { ReadableSpan } from "@opentelemetry/sdk-trace";
|
|
2
|
+
import type { ReadableLogRecord, ReadWriteLogRecord } from "@opentelemetry/sdk-logs";
|
|
3
|
+
export declare function snapshotSpan(source: ReadableSpan, limit: number): {
|
|
4
|
+
record: ReadableSpan;
|
|
5
|
+
bytes: number;
|
|
6
|
+
unresolvedResource: boolean;
|
|
7
|
+
};
|
|
8
|
+
export declare function snapshotLog(source: ReadableLogRecord, limit: number): {
|
|
9
|
+
record: ReadWriteLogRecord;
|
|
10
|
+
bytes: number;
|
|
11
|
+
unresolvedResource: boolean;
|
|
12
|
+
};
|
package/dist/snapshot.js
ADDED
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
import { createTraceState } from "@opentelemetry/api";
|
|
2
|
+
import { types as utilTypes } from "node:util";
|
|
3
|
+
import { resourceFromAttributes } from "@opentelemetry/resources";
|
|
4
|
+
const typedArrayByteLength = Object.getOwnPropertyDescriptor(Object.getPrototypeOf(Uint8Array.prototype), "byteLength").get;
|
|
5
|
+
const typedArraySet = Uint8Array.prototype.set;
|
|
6
|
+
/** Copies only exported data, with the same finite budget used for admission. */
|
|
7
|
+
class Snapshot {
|
|
8
|
+
limit;
|
|
9
|
+
bytes = 512;
|
|
10
|
+
unresolvedResource = false;
|
|
11
|
+
nodes = 0;
|
|
12
|
+
ancestors = new Set();
|
|
13
|
+
copied = new Map();
|
|
14
|
+
constructor(limit) {
|
|
15
|
+
this.limit = limit;
|
|
16
|
+
}
|
|
17
|
+
charge(bytes) {
|
|
18
|
+
this.bytes += bytes;
|
|
19
|
+
if (this.bytes > this.limit)
|
|
20
|
+
throw new RangeError("Telemetry byte budget exceeded");
|
|
21
|
+
}
|
|
22
|
+
copy(value, depth = 0) {
|
|
23
|
+
if (++this.nodes > 16384 || depth > 32)
|
|
24
|
+
throw new RangeError("Telemetry complexity limit exceeded");
|
|
25
|
+
this.charge(16);
|
|
26
|
+
if (typeof value === "string") {
|
|
27
|
+
this.charge(value.length * 2);
|
|
28
|
+
return value;
|
|
29
|
+
}
|
|
30
|
+
if (value === null ||
|
|
31
|
+
value === undefined ||
|
|
32
|
+
typeof value === "boolean" ||
|
|
33
|
+
typeof value === "number")
|
|
34
|
+
return value;
|
|
35
|
+
if (typeof value !== "object")
|
|
36
|
+
throw new TypeError("Unsupported telemetry value");
|
|
37
|
+
if (utilTypes.isProxy(value))
|
|
38
|
+
throw new TypeError("Telemetry proxies are unsupported");
|
|
39
|
+
if (this.ancestors.has(value))
|
|
40
|
+
throw new TypeError("Cyclic telemetry value");
|
|
41
|
+
if (this.copied.has(value))
|
|
42
|
+
return this.copied.get(value);
|
|
43
|
+
if (utilTypes.isUint8Array(value)) {
|
|
44
|
+
// Own accessors/subclasses cannot disguise the retained byte count.
|
|
45
|
+
// A length-tracking SharedArrayBuffer view can grow on another thread
|
|
46
|
+
// after charging: keep the destination fixed and reject growth during
|
|
47
|
+
// the intrinsic copy instead of retaining an uncharged larger array.
|
|
48
|
+
const length = typedArrayByteLength.call(value);
|
|
49
|
+
this.charge(length);
|
|
50
|
+
const copy = new Uint8Array(length);
|
|
51
|
+
typedArraySet.call(copy, value);
|
|
52
|
+
this.copied.set(value, copy);
|
|
53
|
+
return copy;
|
|
54
|
+
}
|
|
55
|
+
const array = Array.isArray(value);
|
|
56
|
+
if (!array && ![Object.prototype, null].includes(Object.getPrototypeOf(value)))
|
|
57
|
+
throw new TypeError("Telemetry must contain data objects");
|
|
58
|
+
const copy = array ? [] : Object.create(null);
|
|
59
|
+
this.copied.set(value, copy);
|
|
60
|
+
this.ancestors.add(value);
|
|
61
|
+
if (array) {
|
|
62
|
+
if (value.length > 16384)
|
|
63
|
+
throw new RangeError("Telemetry complexity limit exceeded");
|
|
64
|
+
for (let index = 0; index < value.length; index++) {
|
|
65
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, index);
|
|
66
|
+
if (descriptor && !("value" in descriptor))
|
|
67
|
+
throw new TypeError("Telemetry accessors are unsupported");
|
|
68
|
+
copy.push(this.copy(descriptor?.value, depth + 1));
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
for (const key in value) {
|
|
73
|
+
if (!Object.hasOwn(value, key))
|
|
74
|
+
continue;
|
|
75
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, key);
|
|
76
|
+
if (!descriptor || !("value" in descriptor))
|
|
77
|
+
throw new TypeError("Telemetry accessors are unsupported");
|
|
78
|
+
this.charge(key.length * 2 + 16);
|
|
79
|
+
copy[key] = this.copy(descriptor.value, depth + 1);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
this.ancestors.delete(value);
|
|
83
|
+
return copy;
|
|
84
|
+
}
|
|
85
|
+
context(source) {
|
|
86
|
+
if (!source)
|
|
87
|
+
return undefined;
|
|
88
|
+
const { traceState, ...context } = this.copy({
|
|
89
|
+
traceId: source.traceId,
|
|
90
|
+
spanId: source.spanId,
|
|
91
|
+
traceFlags: source.traceFlags,
|
|
92
|
+
isRemote: source.isRemote,
|
|
93
|
+
traceState: source.traceState?.serialize(),
|
|
94
|
+
});
|
|
95
|
+
return {
|
|
96
|
+
...context,
|
|
97
|
+
...(traceState !== undefined ? { traceState: createTraceState(traceState) } : {}),
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
resource(source) {
|
|
101
|
+
// Do not retain a detector promise or its mutable resource graph. Later
|
|
102
|
+
// records can include metadata once detection completes. The caller is
|
|
103
|
+
// informed if this record omits unresolved resource attributes.
|
|
104
|
+
this.unresolvedResource ||= source.asyncAttributesPending === true;
|
|
105
|
+
const attributes = Object.create(null);
|
|
106
|
+
const raw = source.getRawAttributes();
|
|
107
|
+
if (raw.length > 16384)
|
|
108
|
+
throw new RangeError("Resource complexity limit exceeded");
|
|
109
|
+
for (const [key, value] of raw) {
|
|
110
|
+
if (value && typeof value.then === "function") {
|
|
111
|
+
this.unresolvedResource = true;
|
|
112
|
+
continue;
|
|
113
|
+
}
|
|
114
|
+
if (value == null || Object.hasOwn(attributes, key))
|
|
115
|
+
continue;
|
|
116
|
+
this.charge(key.length * 2 + 16);
|
|
117
|
+
attributes[key] = this.copy(value);
|
|
118
|
+
}
|
|
119
|
+
return resourceFromAttributes(attributes, { schemaUrl: this.copy(source.schemaUrl) });
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
function contextReader(context) {
|
|
123
|
+
return () => context;
|
|
124
|
+
}
|
|
125
|
+
export function snapshotSpan(source, limit) {
|
|
126
|
+
const snapshot = new Snapshot(limit);
|
|
127
|
+
const context = snapshot.context(source.spanContext());
|
|
128
|
+
if (source.links.length > 16384)
|
|
129
|
+
throw new RangeError("Link complexity limit exceeded");
|
|
130
|
+
const links = source.links.map((link) => ({
|
|
131
|
+
context: snapshot.context(link.context),
|
|
132
|
+
attributes: snapshot.copy(link.attributes),
|
|
133
|
+
droppedAttributesCount: snapshot.copy(link.droppedAttributesCount),
|
|
134
|
+
}));
|
|
135
|
+
const record = {
|
|
136
|
+
...snapshot.copy({
|
|
137
|
+
name: source.name,
|
|
138
|
+
kind: source.kind,
|
|
139
|
+
startTime: source.startTime,
|
|
140
|
+
endTime: source.endTime,
|
|
141
|
+
duration: source.duration,
|
|
142
|
+
ended: source.ended,
|
|
143
|
+
status: source.status,
|
|
144
|
+
attributes: source.attributes,
|
|
145
|
+
events: source.events,
|
|
146
|
+
instrumentationScope: source.instrumentationScope,
|
|
147
|
+
droppedAttributesCount: source.droppedAttributesCount,
|
|
148
|
+
droppedEventsCount: source.droppedEventsCount,
|
|
149
|
+
droppedLinksCount: source.droppedLinksCount,
|
|
150
|
+
}),
|
|
151
|
+
spanContext: contextReader(context),
|
|
152
|
+
parentSpanContext: snapshot.context(source.parentSpanContext),
|
|
153
|
+
links,
|
|
154
|
+
resource: snapshot.resource(source.resource),
|
|
155
|
+
};
|
|
156
|
+
return { record, bytes: snapshot.bytes, unresolvedResource: snapshot.unresolvedResource };
|
|
157
|
+
}
|
|
158
|
+
export function snapshotLog(source, limit) {
|
|
159
|
+
const snapshot = new Snapshot(limit);
|
|
160
|
+
// Only our batching processor sees this copy. Its writer methods deliberately
|
|
161
|
+
// cannot mutate the admitted snapshot or invalidate its charged byte count.
|
|
162
|
+
const record = {
|
|
163
|
+
...snapshot.copy({
|
|
164
|
+
hrTime: source.hrTime,
|
|
165
|
+
hrTimeObserved: source.hrTimeObserved,
|
|
166
|
+
severityText: source.severityText,
|
|
167
|
+
severityNumber: source.severityNumber,
|
|
168
|
+
eventName: source.eventName,
|
|
169
|
+
body: source.body,
|
|
170
|
+
attributes: source.attributes,
|
|
171
|
+
instrumentationScope: source.instrumentationScope,
|
|
172
|
+
droppedAttributesCount: source.droppedAttributesCount,
|
|
173
|
+
}),
|
|
174
|
+
spanContext: snapshot.context(source.spanContext),
|
|
175
|
+
resource: snapshot.resource(source.resource),
|
|
176
|
+
setAttribute() {
|
|
177
|
+
return this;
|
|
178
|
+
},
|
|
179
|
+
setAttributes() {
|
|
180
|
+
return this;
|
|
181
|
+
},
|
|
182
|
+
setBody() {
|
|
183
|
+
return this;
|
|
184
|
+
},
|
|
185
|
+
setEventName() {
|
|
186
|
+
return this;
|
|
187
|
+
},
|
|
188
|
+
setSeverityNumber() {
|
|
189
|
+
return this;
|
|
190
|
+
},
|
|
191
|
+
setSeverityText() {
|
|
192
|
+
return this;
|
|
193
|
+
},
|
|
194
|
+
};
|
|
195
|
+
return { record, bytes: snapshot.bytes, unresolvedResource: snapshot.unresolvedResource };
|
|
196
|
+
}
|
package/dist/transport.d.ts
CHANGED
|
@@ -22,6 +22,11 @@ export declare class HueTransport {
|
|
|
22
22
|
private failed;
|
|
23
23
|
private spans;
|
|
24
24
|
private logs;
|
|
25
|
+
private pendingBytes;
|
|
26
|
+
private dropped;
|
|
27
|
+
private instrumentationFailures;
|
|
28
|
+
private diagnosticPending;
|
|
29
|
+
private lastDiagnosticAt;
|
|
25
30
|
private traceExporter;
|
|
26
31
|
private logExporter;
|
|
27
32
|
private closed;
|
|
@@ -32,6 +37,7 @@ export declare class HueTransport {
|
|
|
32
37
|
finish(signal: Signal, records: RecordValue[]): void;
|
|
33
38
|
acceptedRecords(signal: Signal, count: number): void;
|
|
34
39
|
issue(signal: Signal, kind: ExportIssue["kind"], count: number, message: string, status?: number): void;
|
|
40
|
+
instrumentationFailure(signal?: Signal): void;
|
|
35
41
|
getReport(): ExportReport;
|
|
36
42
|
getIssues(): ExportIssue[];
|
|
37
43
|
/** Monotonic failure marker, retained even when the bounded issue history rolls over. */
|