@autter/runtime-node 1.5.0 → 1.6.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/README.md +139 -1
- package/dist/{chunk-GI47WGRR.cjs → chunk-4MVWIRBI.cjs} +120 -39
- package/dist/{chunk-3TJHUCZ6.js → chunk-5KISVAVB.js} +326 -54
- package/dist/{chunk-L6SRL65J.cjs → chunk-7OTD2YPT.cjs} +11 -11
- package/dist/{chunk-L7HWKMCZ.js → chunk-HRDELHZP.js} +112 -31
- package/dist/{chunk-ZS4WQJQR.js → chunk-L5ENELB2.js} +1 -1
- package/dist/{chunk-P4VRTFAV.cjs → chunk-UXTIMS3G.cjs} +353 -81
- package/dist/cli.cjs +29 -9
- package/dist/cli.js +22 -2
- package/dist/index.cjs +668 -272
- package/dist/index.d.cts +160 -7
- package/dist/index.d.ts +160 -7
- package/dist/index.js +529 -133
- package/dist/{sinks-RFV76lF1.d.cts → sinks-CPGDyfU8.d.cts} +29 -1
- package/dist/{sinks-RFV76lF1.d.ts → sinks-CPGDyfU8.d.ts} +29 -1
- package/dist/testing.cjs +8 -8
- package/dist/testing.d.cts +2 -2
- package/dist/testing.d.ts +2 -2
- package/dist/testing.js +2 -2
- package/package.json +3 -1
package/dist/index.js
CHANGED
|
@@ -2,6 +2,8 @@ import {
|
|
|
2
2
|
CODE_PATTERN,
|
|
3
3
|
DEFAULT_REQUEST_ID_HEADER,
|
|
4
4
|
RuntimeError,
|
|
5
|
+
addBreadcrumb,
|
|
6
|
+
breadcrumbEventAttributes,
|
|
5
7
|
captureExceptionAsLog,
|
|
6
8
|
compileIgnore,
|
|
7
9
|
configureRuntimeLogger,
|
|
@@ -15,7 +17,10 @@ import {
|
|
|
15
17
|
errorCodeOf,
|
|
16
18
|
errorInternal,
|
|
17
19
|
fileSink,
|
|
20
|
+
fingerprintEventAttributes,
|
|
21
|
+
fingerprintValues,
|
|
18
22
|
flushRuntimeLogs,
|
|
23
|
+
isError,
|
|
19
24
|
isRuntimeErrorLike,
|
|
20
25
|
local,
|
|
21
26
|
makeRedactor,
|
|
@@ -35,13 +40,15 @@ import {
|
|
|
35
40
|
runtimeContext,
|
|
36
41
|
runtimeLogStats,
|
|
37
42
|
runtimeLogger,
|
|
43
|
+
sanitizeBreadcrumbs,
|
|
38
44
|
shutdownRuntimeLogger,
|
|
45
|
+
splitFingerprint,
|
|
39
46
|
toClientError,
|
|
40
47
|
withRuntimeOperation
|
|
41
|
-
} from "./chunk-
|
|
48
|
+
} from "./chunk-5KISVAVB.js";
|
|
42
49
|
import {
|
|
43
50
|
runDoctor
|
|
44
|
-
} from "./chunk-
|
|
51
|
+
} from "./chunk-L5ENELB2.js";
|
|
45
52
|
import {
|
|
46
53
|
COMPAT_MANIFEST,
|
|
47
54
|
CountingExporter,
|
|
@@ -60,9 +67,10 @@ import {
|
|
|
60
67
|
observeIngesterResponse,
|
|
61
68
|
registerFlushTarget,
|
|
62
69
|
setDebugMode,
|
|
70
|
+
sharedSlot,
|
|
63
71
|
telemetryStats,
|
|
64
72
|
unregisterFlushTargets
|
|
65
|
-
} from "./chunk-
|
|
73
|
+
} from "./chunk-HRDELHZP.js";
|
|
66
74
|
|
|
67
75
|
// src/relay.ts
|
|
68
76
|
var IpWindow = class {
|
|
@@ -102,6 +110,7 @@ var EVENT_TYPES = /* @__PURE__ */ new Set([
|
|
|
102
110
|
"timing"
|
|
103
111
|
]);
|
|
104
112
|
var SEVERITIES = /* @__PURE__ */ new Set(["fatal", "error", "warning", "info"]);
|
|
113
|
+
var TRACE_ID = /^(?!0{32})[0-9a-f]{32}$/;
|
|
105
114
|
var CONTEXT_MAX_DEPTH = 6;
|
|
106
115
|
var CONTEXT_MAX_NODES = 256;
|
|
107
116
|
var CONTEXT_MAX_STRING = 4e3;
|
|
@@ -214,6 +223,27 @@ function relayContext(raw, redactor) {
|
|
|
214
223
|
const bounded = boundContext(raw);
|
|
215
224
|
return Array.isArray(bounded) ? {} : { context: redactor(bounded) };
|
|
216
225
|
}
|
|
226
|
+
function relayBreadcrumbs(raw, redactor) {
|
|
227
|
+
const breadcrumbs = sanitizeBreadcrumbs(raw, redactor);
|
|
228
|
+
return breadcrumbs ? { breadcrumbs } : {};
|
|
229
|
+
}
|
|
230
|
+
var DEBUG_ID_RE = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/;
|
|
231
|
+
function relayDebugIds(raw, redactor) {
|
|
232
|
+
if (typeof raw !== "object" || raw === null || Array.isArray(raw)) return {};
|
|
233
|
+
const debugIds = {};
|
|
234
|
+
let count = 0;
|
|
235
|
+
for (const [url, id] of Object.entries(raw)) {
|
|
236
|
+
if (count >= 50) break;
|
|
237
|
+
if (typeof id !== "string" || !DEBUG_ID_RE.test(id) || url.length > 1e3) continue;
|
|
238
|
+
debugIds[redactor.text(url.split(/[?#]/)[0])] = id.toLowerCase();
|
|
239
|
+
count++;
|
|
240
|
+
}
|
|
241
|
+
return count ? { debugIds } : {};
|
|
242
|
+
}
|
|
243
|
+
function relayFingerprint(raw, redactor) {
|
|
244
|
+
const values = fingerprintValues(raw, redactor.text);
|
|
245
|
+
return values ? { fingerprint: Array.isArray(raw) ? values : values[0] } : {};
|
|
246
|
+
}
|
|
217
247
|
var defaultRelayRedactor = makeRedactor(true);
|
|
218
248
|
function sanitizeBrowserPayload(raw, redactor = defaultRelayRedactor) {
|
|
219
249
|
if (typeof raw !== "object" || raw === null) return null;
|
|
@@ -241,7 +271,11 @@ function sanitizeBrowserPayload(raw, redactor = defaultRelayRedactor) {
|
|
|
241
271
|
...typeof e.line === "number" ? { line: e.line } : {},
|
|
242
272
|
...typeof e.column === "number" ? { column: e.column } : {},
|
|
243
273
|
...typeof e.route === "string" ? { route: e.route.split("?")[0].slice(0, 1e3) } : {},
|
|
244
|
-
...typeof e.context === "object" && e.context !== null ? relayContext(e.context, redactor) : {}
|
|
274
|
+
...typeof e.context === "object" && e.context !== null ? relayContext(e.context, redactor) : {},
|
|
275
|
+
...relayBreadcrumbs(e.breadcrumbs, redactor),
|
|
276
|
+
...relayDebugIds(e.debugIds, redactor),
|
|
277
|
+
...relayFingerprint(e.fingerprint, redactor),
|
|
278
|
+
...typeof e.traceId === "string" && TRACE_ID.test(e.traceId) ? { traceId: e.traceId } : {}
|
|
245
279
|
});
|
|
246
280
|
}
|
|
247
281
|
return {
|
|
@@ -275,6 +309,9 @@ function forward(payload, opts) {
|
|
|
275
309
|
);
|
|
276
310
|
observeIngesterResponse({ status: response.status, headers: response.headers });
|
|
277
311
|
for (const feature of featuresForBrowserEvents(types)) noteCompatFeature(feature);
|
|
312
|
+
if ((payload.events ?? []).some((e) => e.traceId)) {
|
|
313
|
+
noteCompatFeature("browser_trace_propagation");
|
|
314
|
+
}
|
|
278
315
|
noteRelayRejection(types, response.status, response.headers);
|
|
279
316
|
}).catch((err) => {
|
|
280
317
|
(opts.onError ?? ((e) => console.warn("autter relay forward failed:", e)))(
|
|
@@ -413,7 +450,7 @@ import {
|
|
|
413
450
|
SpanStatusCode,
|
|
414
451
|
TraceFlags
|
|
415
452
|
} from "@opentelemetry/api";
|
|
416
|
-
import { getRPCMetadata, RPCType } from "@opentelemetry/core";
|
|
453
|
+
import { getRPCMetadata as getRPCMetadata2, RPCType as RPCType2 } from "@opentelemetry/core";
|
|
417
454
|
import {
|
|
418
455
|
AggregationTemporalityPreference,
|
|
419
456
|
OTLPMetricExporter
|
|
@@ -442,6 +479,89 @@ import { Resource } from "@opentelemetry/resources";
|
|
|
442
479
|
import {
|
|
443
480
|
AggregationTemporality
|
|
444
481
|
} from "@opentelemetry/sdk-metrics";
|
|
482
|
+
|
|
483
|
+
// src/url-query.ts
|
|
484
|
+
var URL_KEYS = ["http.target", "http.url", "url.full", "url.original", "url.path"];
|
|
485
|
+
var QUERY_KEY = "url.query";
|
|
486
|
+
var NAME_KEYS = ["next.span_name"];
|
|
487
|
+
var MASK = "[redacted]";
|
|
488
|
+
var SCHEME = /^[a-z][a-z0-9+.-]*:\/\//i;
|
|
489
|
+
function splitUrl(value) {
|
|
490
|
+
const from = SCHEME.test(value) ? value.indexOf("://") + 3 : 0;
|
|
491
|
+
const question = value.indexOf("?", from);
|
|
492
|
+
const hash = value.indexOf("#", from);
|
|
493
|
+
const at = question < 0 ? hash : hash < 0 ? question : Math.min(question, hash);
|
|
494
|
+
if (at < 0) return null;
|
|
495
|
+
const query = at === question ? value.slice(at + 1, hash > at ? hash : void 0) : null;
|
|
496
|
+
return { base: value.slice(0, at), query };
|
|
497
|
+
}
|
|
498
|
+
function decodeName(raw) {
|
|
499
|
+
try {
|
|
500
|
+
return decodeURIComponent(raw.replace(/\+/g, " "));
|
|
501
|
+
} catch {
|
|
502
|
+
return raw;
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
function makeUrlQueryPolicy(keepQueryParams = []) {
|
|
506
|
+
const keep = new Set(keepQueryParams.filter((name) => typeof name === "string"));
|
|
507
|
+
const maskQuery = (query) => {
|
|
508
|
+
const out = [];
|
|
509
|
+
for (const part of query.split("&")) {
|
|
510
|
+
if (!part) continue;
|
|
511
|
+
const eq = part.indexOf("=");
|
|
512
|
+
const name = eq < 0 ? part : part.slice(0, eq);
|
|
513
|
+
if (keep.size && keep.has(decodeName(name))) out.push(part);
|
|
514
|
+
else if (eq < 0) out.push(MASK);
|
|
515
|
+
else out.push(eq === part.length - 1 ? part : `${name}=${MASK}`);
|
|
516
|
+
}
|
|
517
|
+
return out.join("&");
|
|
518
|
+
};
|
|
519
|
+
const stripName = (name) => {
|
|
520
|
+
if (!name.includes("?") && !name.includes("#")) return name;
|
|
521
|
+
return name.split(" ").map((token) => {
|
|
522
|
+
if (!token.startsWith("/") && !SCHEME.test(token)) return token;
|
|
523
|
+
return splitUrl(token)?.base ?? token;
|
|
524
|
+
}).join(" ");
|
|
525
|
+
};
|
|
526
|
+
return {
|
|
527
|
+
attributes(attributes) {
|
|
528
|
+
let out = null;
|
|
529
|
+
const set = (key, value) => {
|
|
530
|
+
out ??= { ...attributes };
|
|
531
|
+
out[key] = value;
|
|
532
|
+
};
|
|
533
|
+
let dropped = null;
|
|
534
|
+
for (const key of URL_KEYS) {
|
|
535
|
+
const value = attributes[key];
|
|
536
|
+
if (typeof value !== "string") continue;
|
|
537
|
+
const split = splitUrl(value);
|
|
538
|
+
if (!split) continue;
|
|
539
|
+
set(key, split.base);
|
|
540
|
+
if (split.query) dropped ??= split.query;
|
|
541
|
+
}
|
|
542
|
+
for (const key of NAME_KEYS) {
|
|
543
|
+
const value = attributes[key];
|
|
544
|
+
if (typeof value !== "string") continue;
|
|
545
|
+
const stripped = stripName(value);
|
|
546
|
+
if (stripped !== value) set(key, stripped);
|
|
547
|
+
}
|
|
548
|
+
const current = attributes[QUERY_KEY];
|
|
549
|
+
if (typeof current === "string") {
|
|
550
|
+
const masked = maskQuery(current.startsWith("?") ? current.slice(1) : current);
|
|
551
|
+
if (masked !== current) set(QUERY_KEY, masked);
|
|
552
|
+
} else if (current !== void 0) {
|
|
553
|
+
set(QUERY_KEY, MASK);
|
|
554
|
+
} else if (dropped !== null) {
|
|
555
|
+
const masked = maskQuery(dropped);
|
|
556
|
+
if (masked) set(QUERY_KEY, masked);
|
|
557
|
+
}
|
|
558
|
+
return out ?? attributes;
|
|
559
|
+
},
|
|
560
|
+
name: stripName
|
|
561
|
+
};
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
// src/redact-exporter.ts
|
|
445
565
|
var scrubbedResources = /* @__PURE__ */ new WeakMap();
|
|
446
566
|
function redactResource(resource, redactor) {
|
|
447
567
|
const cached = scrubbedResources.get(resource);
|
|
@@ -450,17 +570,41 @@ function redactResource(resource, redactor) {
|
|
|
450
570
|
scrubbedResources.set(resource, scrubbed);
|
|
451
571
|
return scrubbed;
|
|
452
572
|
}
|
|
453
|
-
|
|
454
|
-
|
|
573
|
+
var DEFAULT_QUERY_POLICY = makeUrlQueryPolicy();
|
|
574
|
+
function stripQueries(span, query) {
|
|
575
|
+
const name = query.name(span.name);
|
|
576
|
+
const attributes = query.attributes(span.attributes);
|
|
577
|
+
if (name === span.name && attributes === span.attributes) return span;
|
|
578
|
+
return wrap(span, { name, attributes });
|
|
579
|
+
}
|
|
580
|
+
function wrap(span, fields) {
|
|
455
581
|
return {
|
|
456
|
-
name:
|
|
582
|
+
name: fields.name,
|
|
457
583
|
kind: span.kind,
|
|
458
584
|
spanContext: () => span.spanContext(),
|
|
459
585
|
parentSpanId: span.parentSpanId,
|
|
460
586
|
startTime: span.startTime,
|
|
461
587
|
endTime: span.endTime,
|
|
588
|
+
status: fields.status ?? span.status,
|
|
589
|
+
attributes: fields.attributes,
|
|
590
|
+
links: fields.links ?? span.links,
|
|
591
|
+
events: fields.events ?? span.events,
|
|
592
|
+
duration: span.duration,
|
|
593
|
+
ended: span.ended,
|
|
594
|
+
resource: fields.resource ?? span.resource,
|
|
595
|
+
instrumentationLibrary: span.instrumentationLibrary,
|
|
596
|
+
droppedAttributesCount: span.droppedAttributesCount,
|
|
597
|
+
droppedEventsCount: span.droppedEventsCount,
|
|
598
|
+
droppedLinksCount: span.droppedLinksCount
|
|
599
|
+
};
|
|
600
|
+
}
|
|
601
|
+
function redactSpan(span, redactor, query = DEFAULT_QUERY_POLICY) {
|
|
602
|
+
if (!redactor.enabled) return stripQueries(span, query);
|
|
603
|
+
const status = span.status.message ? { ...span.status, message: redactor.text(span.status.message) } : span.status;
|
|
604
|
+
return wrap(span, {
|
|
605
|
+
name: query.name(redactor.text(span.name)),
|
|
462
606
|
status,
|
|
463
|
-
attributes: redactor.exported(span.attributes),
|
|
607
|
+
attributes: query.attributes(redactor.exported(span.attributes)),
|
|
464
608
|
links: span.links.map(
|
|
465
609
|
(link) => link.attributes ? { ...link, attributes: redactor.exported(link.attributes) } : link
|
|
466
610
|
),
|
|
@@ -469,34 +613,27 @@ function redactSpan(span, redactor) {
|
|
|
469
613
|
name: redactor.text(event.name),
|
|
470
614
|
...event.attributes ? { attributes: redactor.exported(event.attributes) } : {}
|
|
471
615
|
})),
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
resource: redactResource(span.resource, redactor),
|
|
475
|
-
instrumentationLibrary: span.instrumentationLibrary,
|
|
476
|
-
droppedAttributesCount: span.droppedAttributesCount,
|
|
477
|
-
droppedEventsCount: span.droppedEventsCount,
|
|
478
|
-
droppedLinksCount: span.droppedLinksCount
|
|
479
|
-
};
|
|
616
|
+
resource: redactResource(span.resource, redactor)
|
|
617
|
+
});
|
|
480
618
|
}
|
|
481
619
|
var RedactingSpanExporter = class {
|
|
482
|
-
constructor(inner, redactor) {
|
|
620
|
+
constructor(inner, redactor, query = DEFAULT_QUERY_POLICY) {
|
|
483
621
|
this.inner = inner;
|
|
484
622
|
this.redactor = redactor;
|
|
623
|
+
this.query = query;
|
|
485
624
|
}
|
|
486
625
|
inner;
|
|
487
626
|
redactor;
|
|
627
|
+
query;
|
|
488
628
|
export(spans, resultCallback) {
|
|
489
629
|
const redactor = this.redactor();
|
|
490
|
-
|
|
630
|
+
const scrubbed = [];
|
|
491
631
|
let failed = 0;
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
} catch {
|
|
498
|
-
failed++;
|
|
499
|
-
}
|
|
632
|
+
for (const span of spans) {
|
|
633
|
+
try {
|
|
634
|
+
scrubbed.push(redactSpan(span, redactor, this.query));
|
|
635
|
+
} catch {
|
|
636
|
+
failed++;
|
|
500
637
|
}
|
|
501
638
|
}
|
|
502
639
|
if (!failed) {
|
|
@@ -602,14 +739,20 @@ function startMemoryMetrics() {
|
|
|
602
739
|
// src/requests.ts
|
|
603
740
|
import { AsyncResource } from "async_hooks";
|
|
604
741
|
import { context, trace } from "@opentelemetry/api";
|
|
742
|
+
import { getRPCMetadata, RPCType } from "@opentelemetry/core";
|
|
605
743
|
var REQUEST_STATE = /* @__PURE__ */ Symbol.for("autter.runtime.request");
|
|
606
744
|
var BOUND = /* @__PURE__ */ Symbol.for("autter.runtime.bound");
|
|
607
|
-
var CAPTURED = /* @__PURE__ */ new WeakSet();
|
|
745
|
+
var CAPTURED = sharedSlot("requests", () => ({ captured: /* @__PURE__ */ new WeakSet() })).captured;
|
|
608
746
|
var capturer = () => {
|
|
609
747
|
};
|
|
610
748
|
function setRequestExceptionCapturer(fn) {
|
|
611
749
|
capturer = fn;
|
|
612
750
|
}
|
|
751
|
+
var annotator = () => {
|
|
752
|
+
};
|
|
753
|
+
function setRequestSpanAnnotator(fn) {
|
|
754
|
+
annotator = fn;
|
|
755
|
+
}
|
|
613
756
|
function markCaptured(error) {
|
|
614
757
|
if (error && typeof error === "object") CAPTURED.add(error);
|
|
615
758
|
}
|
|
@@ -624,23 +767,54 @@ function expressRouteOf(req) {
|
|
|
624
767
|
function defaultShouldCapture(error) {
|
|
625
768
|
return error instanceof RuntimeError || errorCodeOf(error) !== void 0 || responseStatusOf(error) >= 500;
|
|
626
769
|
}
|
|
627
|
-
function
|
|
628
|
-
const
|
|
770
|
+
function onRequestSpan(fn) {
|
|
771
|
+
const active = context.active();
|
|
772
|
+
const metadata = getRPCMetadata(active);
|
|
773
|
+
const span = metadata?.type === RPCType.HTTP ? metadata.span : void 0;
|
|
774
|
+
if (!span || !span.isRecording() || trace.getSpan(active) === span) {
|
|
775
|
+
fn();
|
|
776
|
+
return;
|
|
777
|
+
}
|
|
778
|
+
context.with(trace.setSpan(active, span), fn);
|
|
779
|
+
}
|
|
780
|
+
function captureAtBoundary(error, state, rule = void 0, attributes) {
|
|
781
|
+
let wanted;
|
|
782
|
+
try {
|
|
783
|
+
wanted = typeof rule === "function" ? rule(error) : rule === void 0 ? defaultShouldCapture(error) : rule;
|
|
784
|
+
} catch {
|
|
785
|
+
wanted = false;
|
|
786
|
+
}
|
|
629
787
|
if (!wanted) return;
|
|
630
788
|
if (error && typeof error === "object" && CAPTURED.has(error)) return;
|
|
631
789
|
markCaptured(error);
|
|
632
790
|
try {
|
|
633
|
-
|
|
634
|
-
|
|
791
|
+
const run = () => onRequestSpan(() => capturer(error, attributes));
|
|
792
|
+
if (state && !state.sealed) local.run(state, run);
|
|
793
|
+
else run();
|
|
635
794
|
} catch {
|
|
636
795
|
}
|
|
637
796
|
}
|
|
797
|
+
function requestStateOf(req) {
|
|
798
|
+
if (!req || typeof req !== "object") return void 0;
|
|
799
|
+
return req[REQUEST_STATE];
|
|
800
|
+
}
|
|
801
|
+
function captureRequestError(error, attributes) {
|
|
802
|
+
if (error && typeof error === "object" && CAPTURED.has(error)) {
|
|
803
|
+
if (!attributes) return;
|
|
804
|
+
try {
|
|
805
|
+
onRequestSpan(() => annotator(attributes));
|
|
806
|
+
} catch {
|
|
807
|
+
}
|
|
808
|
+
return;
|
|
809
|
+
}
|
|
810
|
+
captureAtBoundary(error, currentOperation(), true, attributes);
|
|
811
|
+
}
|
|
638
812
|
function bindEmitter(emitter, state) {
|
|
639
813
|
const target = emitter;
|
|
640
814
|
if (!target || typeof target !== "object" || target[BOUND]) return;
|
|
641
815
|
target[BOUND] = true;
|
|
642
816
|
const wrappers = /* @__PURE__ */ new WeakMap();
|
|
643
|
-
const
|
|
817
|
+
const wrap2 = (listener) => {
|
|
644
818
|
if (typeof listener !== "function") return listener;
|
|
645
819
|
let wrapped = wrappers.get(listener);
|
|
646
820
|
if (!wrapped) {
|
|
@@ -664,7 +838,7 @@ function bindEmitter(emitter, state) {
|
|
|
664
838
|
const original = target[method];
|
|
665
839
|
if (typeof original !== "function") continue;
|
|
666
840
|
target[method] = function(event, listener) {
|
|
667
|
-
return original.call(this, event,
|
|
841
|
+
return original.call(this, event, wrap2(listener));
|
|
668
842
|
};
|
|
669
843
|
}
|
|
670
844
|
for (const method of ["removeListener", "off"]) {
|
|
@@ -976,7 +1150,140 @@ function startHookRequest(res) {
|
|
|
976
1150
|
local.enterWith(state);
|
|
977
1151
|
}
|
|
978
1152
|
|
|
1153
|
+
// src/unhandled-rejections.ts
|
|
1154
|
+
var MODES = /* @__PURE__ */ new Set([
|
|
1155
|
+
"throw",
|
|
1156
|
+
"strict",
|
|
1157
|
+
"warn",
|
|
1158
|
+
"warn-with-error-code",
|
|
1159
|
+
"none"
|
|
1160
|
+
]);
|
|
1161
|
+
var LISTENER_MARK = /* @__PURE__ */ Symbol.for("autter.runtime.unhandledRejection.listener");
|
|
1162
|
+
var HANDLED_KEY = /* @__PURE__ */ Symbol.for("autter.runtime.unhandledRejection.handled");
|
|
1163
|
+
function handledPromises() {
|
|
1164
|
+
const registry = globalThis;
|
|
1165
|
+
let set = registry[HANDLED_KEY];
|
|
1166
|
+
if (!set) {
|
|
1167
|
+
set = /* @__PURE__ */ new WeakSet();
|
|
1168
|
+
registry[HANDLED_KEY] = set;
|
|
1169
|
+
}
|
|
1170
|
+
return set;
|
|
1171
|
+
}
|
|
1172
|
+
var recorded = /* @__PURE__ */ new WeakSet();
|
|
1173
|
+
function markRecorded(reason) {
|
|
1174
|
+
if (!reason || typeof reason !== "object") return;
|
|
1175
|
+
recorded.add(reason);
|
|
1176
|
+
setImmediate(() => recorded.delete(reason)).unref?.();
|
|
1177
|
+
}
|
|
1178
|
+
function recordedAsRejection(error) {
|
|
1179
|
+
return !!error && typeof error === "object" && recorded.has(error);
|
|
1180
|
+
}
|
|
1181
|
+
function splitOptions(value) {
|
|
1182
|
+
return value ? value.split(/\s+/).filter(Boolean) : [];
|
|
1183
|
+
}
|
|
1184
|
+
function unhandledRejectionsMode(execArgv = process.execArgv, nodeOptions = process.env.NODE_OPTIONS) {
|
|
1185
|
+
const args = [...splitOptions(nodeOptions), ...execArgv];
|
|
1186
|
+
let mode = "throw";
|
|
1187
|
+
for (let i = 0; i < args.length; i++) {
|
|
1188
|
+
const arg = args[i] ?? "";
|
|
1189
|
+
if (!arg.startsWith("--")) continue;
|
|
1190
|
+
const eq = arg.indexOf("=");
|
|
1191
|
+
const name = (eq < 0 ? arg : arg.slice(0, eq)).replace(/_/g, "-");
|
|
1192
|
+
if (name !== "--unhandled-rejections") continue;
|
|
1193
|
+
const value = eq < 0 ? args[++i] : arg.slice(eq + 1);
|
|
1194
|
+
if (value && MODES.has(value))
|
|
1195
|
+
mode = value;
|
|
1196
|
+
}
|
|
1197
|
+
return mode;
|
|
1198
|
+
}
|
|
1199
|
+
function isErrorLike(value) {
|
|
1200
|
+
try {
|
|
1201
|
+
return typeof value === "object" && value !== null && Object.prototype.hasOwnProperty.call(value, "stack");
|
|
1202
|
+
} catch {
|
|
1203
|
+
return false;
|
|
1204
|
+
}
|
|
1205
|
+
}
|
|
1206
|
+
function describeReason(reason) {
|
|
1207
|
+
if (typeof reason !== "object" && typeof reason !== "function" || reason === null) {
|
|
1208
|
+
try {
|
|
1209
|
+
return String(reason);
|
|
1210
|
+
} catch {
|
|
1211
|
+
return typeof reason;
|
|
1212
|
+
}
|
|
1213
|
+
}
|
|
1214
|
+
try {
|
|
1215
|
+
const proto = Object.getPrototypeOf(reason);
|
|
1216
|
+
const ctor = proto?.constructor;
|
|
1217
|
+
const name = typeof ctor === "function" ? ctor.name : "";
|
|
1218
|
+
return name ? `#<${name}>` : "[object Object]";
|
|
1219
|
+
} catch {
|
|
1220
|
+
return "[object Object]";
|
|
1221
|
+
}
|
|
1222
|
+
}
|
|
1223
|
+
function toUncaught(reason, from) {
|
|
1224
|
+
if (isErrorLike(reason)) return reason;
|
|
1225
|
+
const error = new Error(
|
|
1226
|
+
`This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). The promise rejected with the reason "${describeReason(reason)}".`
|
|
1227
|
+
);
|
|
1228
|
+
error.name = "UnhandledPromiseRejection";
|
|
1229
|
+
error.code = "ERR_UNHANDLED_REJECTION";
|
|
1230
|
+
Error.captureStackTrace?.(error, from);
|
|
1231
|
+
return error;
|
|
1232
|
+
}
|
|
1233
|
+
var warningId = 0;
|
|
1234
|
+
function emitNodeWarning(reason) {
|
|
1235
|
+
try {
|
|
1236
|
+
const text = isErrorLike(reason) ? String(reason.stack) : describeReason(reason);
|
|
1237
|
+
process.emitWarning(text, "UnhandledPromiseRejectionWarning");
|
|
1238
|
+
process.emitWarning(
|
|
1239
|
+
`Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection, use the CLI flag \`--unhandled-rejections=strict\` (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: ${++warningId})`,
|
|
1240
|
+
"UnhandledPromiseRejectionWarning"
|
|
1241
|
+
);
|
|
1242
|
+
} catch {
|
|
1243
|
+
}
|
|
1244
|
+
}
|
|
1245
|
+
function installUnhandledRejectionCapture(capture, mode = unhandledRejectionsMode()) {
|
|
1246
|
+
if (mode === "strict") return () => {
|
|
1247
|
+
};
|
|
1248
|
+
const listener = (reason, promise) => {
|
|
1249
|
+
try {
|
|
1250
|
+
if (promise && typeof promise === "object") {
|
|
1251
|
+
const handled = handledPromises();
|
|
1252
|
+
if (handled.has(promise)) return;
|
|
1253
|
+
handled.add(promise);
|
|
1254
|
+
}
|
|
1255
|
+
const alone = process.listeners("unhandledRejection").every((fn) => fn[LISTENER_MARK] === true);
|
|
1256
|
+
if (alone && mode === "throw") {
|
|
1257
|
+
const error = toUncaught(reason, listener);
|
|
1258
|
+
process.nextTick(() => {
|
|
1259
|
+
throw error;
|
|
1260
|
+
});
|
|
1261
|
+
return;
|
|
1262
|
+
}
|
|
1263
|
+
if (alone && mode === "warn-with-error-code") {
|
|
1264
|
+
emitNodeWarning(reason);
|
|
1265
|
+
process.exitCode = 1;
|
|
1266
|
+
}
|
|
1267
|
+
markRecorded(reason);
|
|
1268
|
+
capture(reason);
|
|
1269
|
+
} catch {
|
|
1270
|
+
}
|
|
1271
|
+
};
|
|
1272
|
+
listener[LISTENER_MARK] = true;
|
|
1273
|
+
process.on("unhandledRejection", listener);
|
|
1274
|
+
return () => {
|
|
1275
|
+
process.off("unhandledRejection", listener);
|
|
1276
|
+
};
|
|
1277
|
+
}
|
|
1278
|
+
|
|
979
1279
|
// src/server.ts
|
|
1280
|
+
var S = sharedSlot("server", () => ({
|
|
1281
|
+
active: null,
|
|
1282
|
+
alwaysOnProvider: null,
|
|
1283
|
+
redactor: makeRedactor(true),
|
|
1284
|
+
loggerOnlyCapture: null,
|
|
1285
|
+
hookRequests: false
|
|
1286
|
+
}));
|
|
980
1287
|
var LLM_MARKER_ATTRIBUTES = [
|
|
981
1288
|
"gen_ai.operation.name",
|
|
982
1289
|
"gen_ai.system",
|
|
@@ -1104,9 +1411,9 @@ var ErrorTraceRetentionProcessor = class {
|
|
|
1104
1411
|
}
|
|
1105
1412
|
/** Promote the active trace, if any — called on captured errors. */
|
|
1106
1413
|
retainActiveTrace() {
|
|
1107
|
-
const
|
|
1108
|
-
if (!
|
|
1109
|
-
const entry = this.entryFor(
|
|
1414
|
+
const active = trace2.getActiveSpan()?.spanContext();
|
|
1415
|
+
if (!active || (active.traceFlags & TraceFlags.SAMPLED) !== 0) return;
|
|
1416
|
+
const entry = this.entryFor(active.traceId);
|
|
1110
1417
|
if (entry.retained) return;
|
|
1111
1418
|
entry.retained = true;
|
|
1112
1419
|
this.flush(entry);
|
|
@@ -1173,7 +1480,7 @@ function llmBaseAttributes(info) {
|
|
|
1173
1480
|
...info.sessionId ? { "autter.session_id": info.sessionId } : {},
|
|
1174
1481
|
// Caller-supplied extras go through redaction like every other
|
|
1175
1482
|
// custom attribute family (prompts can contain emails/keys).
|
|
1176
|
-
...info.attributes ?
|
|
1483
|
+
...info.attributes ? S.redactor(info.attributes) : {}
|
|
1177
1484
|
};
|
|
1178
1485
|
}
|
|
1179
1486
|
function usageAttributes(usage) {
|
|
@@ -1214,7 +1521,7 @@ async function runLlmSpan(tracer, info, fn) {
|
|
|
1214
1521
|
},
|
|
1215
1522
|
setResponseModel,
|
|
1216
1523
|
setModel: setResponseModel,
|
|
1217
|
-
setAttributes: (attributes) => span.setAttributes(
|
|
1524
|
+
setAttributes: (attributes) => span.setAttributes(S.redactor(attributes))
|
|
1218
1525
|
};
|
|
1219
1526
|
try {
|
|
1220
1527
|
const result = await context2.with(
|
|
@@ -1225,7 +1532,7 @@ async function runLlmSpan(tracer, info, fn) {
|
|
|
1225
1532
|
return result;
|
|
1226
1533
|
} catch (err) {
|
|
1227
1534
|
recordError(span, err);
|
|
1228
|
-
if (err
|
|
1535
|
+
if (isError(err)) span.setAttribute("error.type", err.name);
|
|
1229
1536
|
throw err;
|
|
1230
1537
|
} finally {
|
|
1231
1538
|
span.end();
|
|
@@ -1247,7 +1554,7 @@ function recordLlmCall(tracer, call) {
|
|
|
1247
1554
|
);
|
|
1248
1555
|
if (call.error !== void 0 && call.error !== null) {
|
|
1249
1556
|
recordError(span, call.error);
|
|
1250
|
-
if (call.error
|
|
1557
|
+
if (isError(call.error)) {
|
|
1251
1558
|
span.setAttribute("error.type", call.error.name);
|
|
1252
1559
|
}
|
|
1253
1560
|
} else {
|
|
@@ -1275,24 +1582,25 @@ async function runWithSpan(tracer, name, fn, attributes, links) {
|
|
|
1275
1582
|
}
|
|
1276
1583
|
}
|
|
1277
1584
|
function recordError(span, err) {
|
|
1278
|
-
const
|
|
1279
|
-
const message =
|
|
1280
|
-
if (
|
|
1585
|
+
const isErr = isError(err);
|
|
1586
|
+
const message = S.redactor.text(isErr ? err.message : String(err));
|
|
1587
|
+
if (isErr) {
|
|
1281
1588
|
span.addEvent("exception", {
|
|
1282
1589
|
"exception.type": err.name,
|
|
1283
1590
|
"exception.message": message,
|
|
1284
|
-
...err.stack ? { "exception.stacktrace":
|
|
1591
|
+
...err.stack ? { "exception.stacktrace": S.redactor.text(err.stack) } : {},
|
|
1592
|
+
...breadcrumbEventAttributes(S.redactor.text, err)
|
|
1285
1593
|
});
|
|
1286
1594
|
}
|
|
1287
1595
|
span.setStatus({ code: SpanStatusCode.ERROR, message });
|
|
1288
1596
|
}
|
|
1289
1597
|
function redactWithActive(attributes) {
|
|
1290
|
-
return
|
|
1598
|
+
return S.redactor(attributes);
|
|
1291
1599
|
}
|
|
1292
1600
|
function captureExpressRoute(_span, response) {
|
|
1293
1601
|
if (!(response instanceof ServerResponse)) return;
|
|
1294
|
-
const rpcMetadata =
|
|
1295
|
-
if (rpcMetadata?.type !==
|
|
1602
|
+
const rpcMetadata = getRPCMetadata2(context2.active());
|
|
1603
|
+
if (rpcMetadata?.type !== RPCType2.HTTP) return;
|
|
1296
1604
|
const setRoute = () => {
|
|
1297
1605
|
if (rpcMetadata.route) return;
|
|
1298
1606
|
const route = expressRouteOf(response.req);
|
|
@@ -1300,42 +1608,41 @@ function captureExpressRoute(_span, response) {
|
|
|
1300
1608
|
};
|
|
1301
1609
|
response.prependListener("finish", setRoute);
|
|
1302
1610
|
response.prependListener("close", setRoute);
|
|
1303
|
-
if (hookRequests) startHookRequest(response);
|
|
1611
|
+
if (S.hookRequests) startHookRequest(response);
|
|
1304
1612
|
}
|
|
1305
|
-
var hookRequests = false;
|
|
1306
1613
|
function structuredErrorAttributes(error) {
|
|
1307
1614
|
const attrs = { ...errorAttributes(error, { causes: false }) };
|
|
1308
1615
|
const internal = errorInternal(error);
|
|
1309
1616
|
if (internal) {
|
|
1310
1617
|
try {
|
|
1311
1618
|
attrs["autter.error.internal"] = JSON.stringify(
|
|
1312
|
-
|
|
1619
|
+
S.redactor(internal)
|
|
1313
1620
|
).slice(0, 4e3);
|
|
1314
1621
|
} catch {
|
|
1315
1622
|
}
|
|
1316
1623
|
}
|
|
1317
1624
|
return attrs;
|
|
1318
1625
|
}
|
|
1319
|
-
var active = null;
|
|
1320
|
-
var activeAlwaysOnProvider = null;
|
|
1321
|
-
var activeRedactor = makeRedactor(true);
|
|
1322
1626
|
function initAutterServer(options) {
|
|
1323
|
-
if (active) return active;
|
|
1627
|
+
if (S.active) return S.active;
|
|
1324
1628
|
const endpoint = (options.endpoint ?? "https://otlp.autter.dev").replace(
|
|
1325
1629
|
/\/$/,
|
|
1326
1630
|
""
|
|
1327
1631
|
);
|
|
1328
1632
|
const headers = { authorization: `Bearer ${options.apiKey}` };
|
|
1633
|
+
const redactor = makeRedactor(options.redactAttributes ?? true);
|
|
1634
|
+
const queryPolicy = makeUrlQueryPolicy(options.keepQueryParams);
|
|
1329
1635
|
const traceExporter = () => new CountingExporter(
|
|
1330
1636
|
new RedactingSpanExporter(
|
|
1331
1637
|
new OTLPTraceExporter({ url: `${endpoint}/v1/traces`, headers }),
|
|
1332
|
-
() =>
|
|
1638
|
+
() => redactor,
|
|
1639
|
+
queryPolicy
|
|
1333
1640
|
)
|
|
1334
1641
|
);
|
|
1335
1642
|
const environment = options.environment ?? process.env.NODE_ENV ?? "production";
|
|
1336
1643
|
if (isDebugEnabled() || options.debug === true) setDebugMode(true);
|
|
1337
1644
|
debugLog(`initialising service=${options.service} endpoint=${endpoint}`);
|
|
1338
|
-
|
|
1645
|
+
S.redactor = redactor;
|
|
1339
1646
|
const distro = options.distro ?? SDK_IDENTITY;
|
|
1340
1647
|
configureCompatCheck({ endpoint, enabled: options.compatCheck !== false, sdk: distro });
|
|
1341
1648
|
const resource = new Resource2({
|
|
@@ -1352,8 +1659,15 @@ function initAutterServer(options) {
|
|
|
1352
1659
|
// would double-count every sampled request).
|
|
1353
1660
|
"autter.metrics_wired": true
|
|
1354
1661
|
});
|
|
1662
|
+
const rootSampler = new TraceIdRatioBasedSampler(options.traceSampleRate ?? 0.01);
|
|
1355
1663
|
const headSampler = new ParentBasedSampler({
|
|
1356
|
-
root:
|
|
1664
|
+
root: rootSampler,
|
|
1665
|
+
// Browsers (runtime-browser's traceparent) send unsampled parents: they
|
|
1666
|
+
// make no sampling decision. Apply this service's own rate to such
|
|
1667
|
+
// requests instead of never head-sampling them. The ratio sampler is
|
|
1668
|
+
// deterministic by trace id, so services sharing a rate still agree
|
|
1669
|
+
// with an upstream service that chose not to sample.
|
|
1670
|
+
remoteParentNotSampled: rootSampler
|
|
1357
1671
|
});
|
|
1358
1672
|
const retainOnError = options.retainTracesOnError !== false;
|
|
1359
1673
|
const slowThresholdMs = Number.isFinite(options.retainTracesAboveMs) ? Math.max(0, options.retainTracesAboveMs) : 0;
|
|
@@ -1377,7 +1691,7 @@ function initAutterServer(options) {
|
|
|
1377
1691
|
// 60 s export.
|
|
1378
1692
|
temporalityPreference: AggregationTemporalityPreference.DELTA
|
|
1379
1693
|
}),
|
|
1380
|
-
() =>
|
|
1694
|
+
() => redactor
|
|
1381
1695
|
),
|
|
1382
1696
|
exportIntervalMillis: options.metricIntervalMs ?? 6e4
|
|
1383
1697
|
});
|
|
@@ -1420,18 +1734,22 @@ function initAutterServer(options) {
|
|
|
1420
1734
|
function captureException2(error, attributes) {
|
|
1421
1735
|
errorTraceBuffer?.retainActiveTrace();
|
|
1422
1736
|
telemetryStats.markCaptured();
|
|
1423
|
-
const
|
|
1424
|
-
const message =
|
|
1425
|
-
const stackText =
|
|
1426
|
-
const
|
|
1737
|
+
const isErr = isError(error);
|
|
1738
|
+
const message = redactor.text(isErr ? error.message : String(error));
|
|
1739
|
+
const stackText = isErr && error.stack ? redactor.text(error.stack) : "";
|
|
1740
|
+
const split = splitFingerprint(attributes);
|
|
1741
|
+
const fingerprint = fingerprintEventAttributes(split.fingerprint, redactor.text);
|
|
1742
|
+
if (fingerprint["autter.fingerprint"]) noteCompatFeature("fingerprint_override");
|
|
1743
|
+
const redacted = redactor(split.attributes);
|
|
1427
1744
|
const activeSpan = trace2.getActiveSpan();
|
|
1428
1745
|
const reuseActive = activeSpan?.isRecording() === true;
|
|
1429
|
-
const span = reuseActive ? activeSpan : errorTracer.startSpan(
|
|
1746
|
+
const span = reuseActive ? activeSpan : errorTracer.startSpan(isErr ? error.name : "Error");
|
|
1430
1747
|
const structured = structuredErrorAttributes(error);
|
|
1431
1748
|
const causes = errorAttributes(error);
|
|
1432
|
-
const declared =
|
|
1433
|
-
Object.entries(causes).filter(([key]) => key !== "autter.error.internal")
|
|
1434
|
-
|
|
1749
|
+
const declared = {
|
|
1750
|
+
...Object.fromEntries(Object.entries(causes).filter(([key]) => key !== "autter.error.internal")),
|
|
1751
|
+
...fingerprint
|
|
1752
|
+
};
|
|
1435
1753
|
const requestId = currentOperation()?.requestId;
|
|
1436
1754
|
span.setAttributes({
|
|
1437
1755
|
"autter.severity": "error",
|
|
@@ -1441,23 +1759,25 @@ function initAutterServer(options) {
|
|
|
1441
1759
|
});
|
|
1442
1760
|
markCaptured(error);
|
|
1443
1761
|
noteOperationError(error);
|
|
1444
|
-
notifyException(error, redacted);
|
|
1445
|
-
if (
|
|
1762
|
+
notifyException(error, { ...redacted, ...fingerprint });
|
|
1763
|
+
if (isErr && stackText) {
|
|
1446
1764
|
span.addEvent("exception", {
|
|
1447
1765
|
"exception.type": error.name,
|
|
1448
1766
|
"exception.message": message,
|
|
1449
1767
|
"exception.stacktrace": stackText,
|
|
1450
1768
|
...redacted["autter.handled"] === true ? { "autter.handled": true, "autter.sampled": redacted["autter.sampled"] === true } : {},
|
|
1451
|
-
...declared
|
|
1769
|
+
...declared,
|
|
1770
|
+
...breadcrumbEventAttributes(redactor.text, error)
|
|
1452
1771
|
});
|
|
1453
1772
|
} else {
|
|
1454
1773
|
const stack = new Error().stack?.split("\n").filter((line, i) => i === 0 || !line.includes("captureException")).join("\n");
|
|
1455
1774
|
span.addEvent("exception", {
|
|
1456
|
-
"exception.type":
|
|
1775
|
+
"exception.type": isErr ? error.name : "Error",
|
|
1457
1776
|
"exception.message": message,
|
|
1458
|
-
...stack ? { "exception.stacktrace":
|
|
1777
|
+
...stack ? { "exception.stacktrace": redactor.text(stack) } : {},
|
|
1459
1778
|
...redacted["autter.handled"] === true ? { "autter.handled": true, "autter.sampled": redacted["autter.sampled"] === true } : {},
|
|
1460
|
-
...declared
|
|
1779
|
+
...declared,
|
|
1780
|
+
...breadcrumbEventAttributes(redactor.text, error)
|
|
1461
1781
|
});
|
|
1462
1782
|
}
|
|
1463
1783
|
span.setStatus({ code: SpanStatusCode.ERROR, message });
|
|
@@ -1469,15 +1789,16 @@ function initAutterServer(options) {
|
|
|
1469
1789
|
}
|
|
1470
1790
|
telemetryStats.markCaptured();
|
|
1471
1791
|
const stack = new Error().stack?.split("\n").filter((line, i) => i === 0 || !line.includes("captureMessage")).join("\n");
|
|
1472
|
-
const safeMessage =
|
|
1792
|
+
const safeMessage = redactor.text(message);
|
|
1473
1793
|
const span = errorTracer.startSpan("Message", {
|
|
1474
|
-
attributes: { "autter.severity": severity, ...
|
|
1794
|
+
attributes: { "autter.severity": severity, ...redactor(attributes) }
|
|
1475
1795
|
});
|
|
1476
1796
|
span.addEvent("exception", {
|
|
1477
1797
|
"exception.type": "Message",
|
|
1478
1798
|
"exception.message": safeMessage,
|
|
1479
|
-
...stack ? { "exception.stacktrace":
|
|
1480
|
-
"autter.severity": severity
|
|
1799
|
+
...stack ? { "exception.stacktrace": redactor.text(stack) } : {},
|
|
1800
|
+
"autter.severity": severity,
|
|
1801
|
+
...breadcrumbEventAttributes(redactor.text)
|
|
1481
1802
|
});
|
|
1482
1803
|
span.setStatus({ code: SpanStatusCode.ERROR, message: safeMessage });
|
|
1483
1804
|
span.end();
|
|
@@ -1488,11 +1809,11 @@ function initAutterServer(options) {
|
|
|
1488
1809
|
const activeSpan = trace2.getActiveSpan();
|
|
1489
1810
|
const reuseActive = activeSpan?.isRecording() === true;
|
|
1490
1811
|
const span = reuseActive ? activeSpan : errorTracer.startSpan("OutcomeFailure");
|
|
1491
|
-
const safeOutcome =
|
|
1812
|
+
const safeOutcome = redactor({ "autter.outcome.name": name.slice(0, 200), "autter.outcome.message": message.slice(0, 1e3) });
|
|
1492
1813
|
const safeName = String(safeOutcome["autter.outcome.name"] ?? "operation");
|
|
1493
1814
|
const safeMessage = String(safeOutcome["autter.outcome.message"] ?? "failed");
|
|
1494
1815
|
span.addEvent("autter.outcome", {
|
|
1495
|
-
...
|
|
1816
|
+
...redactor(attributes),
|
|
1496
1817
|
"autter.outcome.status": "error",
|
|
1497
1818
|
"autter.outcome.name": safeName,
|
|
1498
1819
|
"autter.outcome.message": safeMessage
|
|
@@ -1508,17 +1829,26 @@ function initAutterServer(options) {
|
|
|
1508
1829
|
distro,
|
|
1509
1830
|
release: options.release,
|
|
1510
1831
|
options: options.logging,
|
|
1511
|
-
redact:
|
|
1512
|
-
run: (name, fn, attributes, links) => runWithSpan(processTracer, name, fn,
|
|
1832
|
+
redact: redactor,
|
|
1833
|
+
run: (name, fn, attributes, links) => runWithSpan(processTracer, name, fn, redactor(attributes), links),
|
|
1513
1834
|
reportOutcome: reportOutcome2
|
|
1514
1835
|
});
|
|
1515
|
-
hookRequests = options.logging?.requests === true;
|
|
1836
|
+
S.hookRequests = options.logging?.requests === true;
|
|
1837
|
+
let onCrash = null;
|
|
1838
|
+
let uninstallRejections = null;
|
|
1516
1839
|
if (options.captureGlobalErrors !== false) {
|
|
1517
|
-
|
|
1840
|
+
onCrash = (err) => {
|
|
1841
|
+
if (recordedAsRejection(err)) return;
|
|
1518
1842
|
captureException2(err, { "autter.unhandled": true });
|
|
1519
1843
|
if (options.autoFlush === false) void Promise.resolve(flushTarget.forceFlush()).catch(() => {
|
|
1520
1844
|
});
|
|
1521
|
-
}
|
|
1845
|
+
};
|
|
1846
|
+
process.on("uncaughtExceptionMonitor", onCrash);
|
|
1847
|
+
if (options.captureUnhandledRejections !== false) {
|
|
1848
|
+
uninstallRejections = installUnhandledRejectionCapture(
|
|
1849
|
+
(reason) => captureException2(reason, { "autter.unhandled_rejection": true })
|
|
1850
|
+
);
|
|
1851
|
+
}
|
|
1522
1852
|
}
|
|
1523
1853
|
const flushTarget = {
|
|
1524
1854
|
forceFlush: async () => {
|
|
@@ -1546,15 +1876,20 @@ function initAutterServer(options) {
|
|
|
1546
1876
|
captureException: captureException2,
|
|
1547
1877
|
captureMessage: captureMessage2,
|
|
1548
1878
|
reportOutcome: reportOutcome2,
|
|
1549
|
-
withProcessSpan: (name, fn, attributes) => runWithSpan(processTracer, name, fn,
|
|
1879
|
+
withProcessSpan: (name, fn, attributes) => runWithSpan(processTracer, name, fn, redactor(attributes)),
|
|
1550
1880
|
withLlmCall: (info, fn) => runLlmSpan(llmTracer, info, fn),
|
|
1551
1881
|
trackLlmCall: (call) => recordLlmCall(llmTracer, call),
|
|
1552
1882
|
shutdown: async () => {
|
|
1553
1883
|
const logShutdown = await Promise.allSettled([shutdownRuntimeLogger()]);
|
|
1554
|
-
|
|
1884
|
+
if (onCrash) process.off("uncaughtExceptionMonitor", onCrash);
|
|
1885
|
+
uninstallRejections?.();
|
|
1555
1886
|
stopMemoryMetrics?.();
|
|
1556
|
-
active
|
|
1557
|
-
|
|
1887
|
+
if (S.active === server) {
|
|
1888
|
+
S.active = null;
|
|
1889
|
+
S.alwaysOnProvider = null;
|
|
1890
|
+
S.hookRequests = false;
|
|
1891
|
+
S.redactor = makeRedactor(true);
|
|
1892
|
+
}
|
|
1558
1893
|
autoFlushHandle?.dispose();
|
|
1559
1894
|
autoFlushHandle = null;
|
|
1560
1895
|
unregisterFlushTargets();
|
|
@@ -1563,75 +1898,81 @@ function initAutterServer(options) {
|
|
|
1563
1898
|
if (logShutdown[0]?.status === "rejected") throw logShutdown[0].reason;
|
|
1564
1899
|
}
|
|
1565
1900
|
};
|
|
1566
|
-
active = server;
|
|
1567
|
-
|
|
1901
|
+
S.active = server;
|
|
1902
|
+
S.alwaysOnProvider = alwaysOnProvider;
|
|
1568
1903
|
return server;
|
|
1569
1904
|
}
|
|
1570
1905
|
function captureException(error, attributes) {
|
|
1571
|
-
if (active) {
|
|
1572
|
-
active.captureException(error, attributes);
|
|
1906
|
+
if (S.active) {
|
|
1907
|
+
S.active.captureException(error, attributes);
|
|
1573
1908
|
return;
|
|
1574
1909
|
}
|
|
1575
|
-
if (loggerOnlyCapture) {
|
|
1576
|
-
loggerOnlyCapture(error, attributes);
|
|
1910
|
+
if (S.loggerOnlyCapture) {
|
|
1911
|
+
S.loggerOnlyCapture(error, attributes);
|
|
1577
1912
|
return;
|
|
1578
1913
|
}
|
|
1579
1914
|
markCaptured(error);
|
|
1580
1915
|
noteOperationError(error);
|
|
1581
|
-
|
|
1582
|
-
const
|
|
1583
|
-
|
|
1916
|
+
const split = splitFingerprint(attributes);
|
|
1917
|
+
const fingerprint = fingerprintEventAttributes(split.fingerprint, S.redactor.text);
|
|
1918
|
+
notifyException(error, { ...split.attributes, ...fingerprint });
|
|
1919
|
+
const span = trace2.getTracer("autter-errors").startSpan(isError(error) ? error.name : "Error", {
|
|
1920
|
+
attributes: { ...S.redactor(split.attributes), ...fingerprint }
|
|
1584
1921
|
});
|
|
1585
1922
|
recordError(span, error);
|
|
1586
1923
|
span.end();
|
|
1587
1924
|
}
|
|
1588
|
-
var loggerOnlyCapture = null;
|
|
1589
1925
|
function setLoggerOnlyCapture(fn) {
|
|
1590
|
-
loggerOnlyCapture = fn;
|
|
1926
|
+
S.loggerOnlyCapture = fn;
|
|
1591
1927
|
}
|
|
1592
1928
|
function serverActive() {
|
|
1593
|
-
return active !== null;
|
|
1929
|
+
return S.active !== null;
|
|
1594
1930
|
}
|
|
1595
|
-
setRequestExceptionCapturer((error) => captureException(error));
|
|
1931
|
+
setRequestExceptionCapturer((error, attributes) => captureException(error, attributes));
|
|
1932
|
+
setRequestSpanAnnotator((attributes) => {
|
|
1933
|
+
const span = trace2.getActiveSpan();
|
|
1934
|
+
if (span?.isRecording()) span.setAttributes(S.redactor(splitFingerprint(attributes).attributes));
|
|
1935
|
+
});
|
|
1596
1936
|
function withProcessSpan(name, fn, attributes) {
|
|
1597
|
-
if (active) return active.withProcessSpan(name, fn, attributes);
|
|
1937
|
+
if (S.active) return S.active.withProcessSpan(name, fn, attributes);
|
|
1598
1938
|
return runWithSpan(
|
|
1599
1939
|
trace2.getTracer("autter-processes"),
|
|
1600
1940
|
name,
|
|
1601
1941
|
fn,
|
|
1602
|
-
|
|
1942
|
+
S.redactor(attributes)
|
|
1603
1943
|
);
|
|
1604
1944
|
}
|
|
1605
1945
|
function withLlmCall(info, fn) {
|
|
1606
|
-
if (active) return active.withLlmCall(info, fn);
|
|
1946
|
+
if (S.active) return S.active.withLlmCall(info, fn);
|
|
1607
1947
|
return runLlmSpan(trace2.getTracer("autter-llm"), info, fn);
|
|
1608
1948
|
}
|
|
1609
1949
|
function trackLlmCall(call) {
|
|
1610
|
-
if (active) {
|
|
1611
|
-
active.trackLlmCall(call);
|
|
1950
|
+
if (S.active) {
|
|
1951
|
+
S.active.trackLlmCall(call);
|
|
1612
1952
|
return;
|
|
1613
1953
|
}
|
|
1614
1954
|
recordLlmCall(trace2.getTracer("autter-llm"), call);
|
|
1615
1955
|
}
|
|
1616
1956
|
function captureMessage(message, severity = "warning", attributes) {
|
|
1617
|
-
if (active) {
|
|
1618
|
-
active.captureMessage(message, severity, attributes);
|
|
1957
|
+
if (S.active) {
|
|
1958
|
+
S.active.captureMessage(message, severity, attributes);
|
|
1619
1959
|
return;
|
|
1620
1960
|
}
|
|
1621
|
-
const safeMessage =
|
|
1961
|
+
const safeMessage = S.redactor.text(message);
|
|
1622
1962
|
const span = trace2.getTracer("autter-errors").startSpan("Message", {
|
|
1623
|
-
attributes: { "autter.severity": severity, ...
|
|
1963
|
+
attributes: { "autter.severity": severity, ...S.redactor(attributes) }
|
|
1624
1964
|
});
|
|
1625
1965
|
span.addEvent("exception", {
|
|
1626
1966
|
"exception.type": "Message",
|
|
1627
1967
|
"exception.message": safeMessage,
|
|
1628
|
-
"autter.severity": severity
|
|
1968
|
+
"autter.severity": severity,
|
|
1969
|
+
...breadcrumbEventAttributes(S.redactor.text)
|
|
1629
1970
|
});
|
|
1630
1971
|
span.setStatus({ code: SpanStatusCode.ERROR, message: safeMessage });
|
|
1631
1972
|
span.end();
|
|
1632
1973
|
}
|
|
1633
1974
|
function reportOutcome(name, message, attributes) {
|
|
1634
|
-
if (active) return active.reportOutcome(name, message, attributes);
|
|
1975
|
+
if (S.active) return S.active.reportOutcome(name, message, attributes);
|
|
1635
1976
|
const span = trace2.getTracer("autter-outcomes").startSpan("OutcomeFailure");
|
|
1636
1977
|
const safeOutcome = redactAttributes({ "autter.outcome.name": name.slice(0, 200), "autter.outcome.message": message.slice(0, 1e3) });
|
|
1637
1978
|
const safeName = String(safeOutcome["autter.outcome.name"] ?? "operation");
|
|
@@ -1653,13 +1994,13 @@ function makeSafeCapture(options) {
|
|
|
1653
1994
|
};
|
|
1654
1995
|
}
|
|
1655
1996
|
function alwaysOnTracer(name) {
|
|
1656
|
-
return
|
|
1997
|
+
return S.alwaysOnProvider ? S.alwaysOnProvider.getTracer(name) : trace2.getTracer(name);
|
|
1657
1998
|
}
|
|
1658
1999
|
function autterLlmTracer() {
|
|
1659
2000
|
return alwaysOnTracer("autter-llm");
|
|
1660
2001
|
}
|
|
1661
2002
|
async function emitLlmSelftestTrace() {
|
|
1662
|
-
const provider =
|
|
2003
|
+
const provider = S.alwaysOnProvider;
|
|
1663
2004
|
if (!provider) {
|
|
1664
2005
|
throw new Error(
|
|
1665
2006
|
"emitLlmSelftestTrace() requires initAutterServer() to have been called"
|
|
@@ -1917,14 +2258,14 @@ function startCaughtExceptionSampler(options = {}) {
|
|
|
1917
2258
|
const rate = Math.max(0, Math.min(1, options.sampleRate ?? 0.01));
|
|
1918
2259
|
const cap = Math.max(1, Math.min(100, options.maxPerMinute ?? 10));
|
|
1919
2260
|
const session = new inspector.Session();
|
|
1920
|
-
let
|
|
2261
|
+
let active = true;
|
|
1921
2262
|
let minute = 0;
|
|
1922
2263
|
let sent = 0;
|
|
1923
2264
|
session.connect();
|
|
1924
2265
|
session.on("Debugger.paused", (message) => {
|
|
1925
2266
|
session.post("Debugger.resume", () => {
|
|
1926
2267
|
});
|
|
1927
|
-
if (!
|
|
2268
|
+
if (!active || message.params.reason !== "exception") return;
|
|
1928
2269
|
const nowMinute = Math.floor(Date.now() / 6e4);
|
|
1929
2270
|
if (nowMinute !== minute) {
|
|
1930
2271
|
minute = nowMinute;
|
|
@@ -1939,30 +2280,66 @@ function startCaughtExceptionSampler(options = {}) {
|
|
|
1939
2280
|
error.stack = `${error.name}: ${error.message}
|
|
1940
2281
|
${frames.join("\n")}`;
|
|
1941
2282
|
queueMicrotask(() => {
|
|
1942
|
-
if (
|
|
2283
|
+
if (active) captureException(error, { "autter.handled": true, "autter.sampled": true });
|
|
1943
2284
|
});
|
|
1944
2285
|
});
|
|
1945
2286
|
session.post("Debugger.enable", (error) => {
|
|
1946
|
-
if (!error &&
|
|
2287
|
+
if (!error && active) session.post("Debugger.setPauseOnExceptions", { state: "all" }, () => {
|
|
1947
2288
|
});
|
|
1948
2289
|
});
|
|
1949
2290
|
return { stop() {
|
|
1950
|
-
if (!
|
|
1951
|
-
|
|
2291
|
+
if (!active) return;
|
|
2292
|
+
active = false;
|
|
1952
2293
|
session.post("Debugger.setPauseOnExceptions", { state: "none" }, () => {
|
|
1953
2294
|
session.post("Debugger.disable", () => session.disconnect());
|
|
1954
2295
|
});
|
|
1955
2296
|
} };
|
|
1956
2297
|
}
|
|
1957
2298
|
|
|
2299
|
+
// src/framework-errors.ts
|
|
2300
|
+
function recordBoundaryError(error, req, rule) {
|
|
2301
|
+
try {
|
|
2302
|
+
const state = requestStateOf(req);
|
|
2303
|
+
if (state) noteOperationError(error, false, state);
|
|
2304
|
+
captureAtBoundary(error, state, rule);
|
|
2305
|
+
} catch {
|
|
2306
|
+
}
|
|
2307
|
+
}
|
|
2308
|
+
function autterErrorHandler(options = {}) {
|
|
2309
|
+
return function autterErrorHandlerMiddleware(error, req, _res, next) {
|
|
2310
|
+
recordBoundaryError(error, req, options.capture);
|
|
2311
|
+
next(error);
|
|
2312
|
+
};
|
|
2313
|
+
}
|
|
2314
|
+
function autterFastifyErrors(fastify, options, done) {
|
|
2315
|
+
const rule = options?.capture;
|
|
2316
|
+
fastify.addHook("onError", ((request, _reply, error, next) => {
|
|
2317
|
+
recordBoundaryError(error, request?.raw, rule);
|
|
2318
|
+
next();
|
|
2319
|
+
}));
|
|
2320
|
+
done?.();
|
|
2321
|
+
}
|
|
2322
|
+
autterFastifyErrors[/* @__PURE__ */ Symbol.for("skip-override")] = true;
|
|
2323
|
+
autterFastifyErrors[/* @__PURE__ */ Symbol.for("fastify.display-name")] = "autter-runtime-errors";
|
|
2324
|
+
function autterKoa(options = {}) {
|
|
2325
|
+
return async function autterKoaMiddleware(ctx, next) {
|
|
2326
|
+
try {
|
|
2327
|
+
await next();
|
|
2328
|
+
} catch (error) {
|
|
2329
|
+
recordBoundaryError(error, ctx?.req, options.capture);
|
|
2330
|
+
throw error;
|
|
2331
|
+
}
|
|
2332
|
+
};
|
|
2333
|
+
}
|
|
2334
|
+
|
|
1958
2335
|
// src/logging-only.ts
|
|
1959
2336
|
import {
|
|
1960
2337
|
SpanStatusCode as SpanStatusCode3,
|
|
1961
2338
|
trace as trace3
|
|
1962
2339
|
} from "@opentelemetry/api";
|
|
1963
|
-
var
|
|
2340
|
+
var L = sharedSlot("logging", () => ({ active: null }));
|
|
1964
2341
|
function initAutterLogging(options) {
|
|
1965
|
-
if (
|
|
2342
|
+
if (L.active) return L.active;
|
|
1966
2343
|
if (serverActive()) {
|
|
1967
2344
|
console.warn(
|
|
1968
2345
|
"[autter-runtime] initAutterLogging() ignored: initAutterServer() is already active and includes logging"
|
|
@@ -1982,14 +2359,16 @@ function initAutterLogging(options) {
|
|
|
1982
2359
|
debugLog(`initialising logger-only service=${options.service} endpoint=${endpoint}`);
|
|
1983
2360
|
function captureException2(error, attributes) {
|
|
1984
2361
|
markCaptured(error);
|
|
1985
|
-
const
|
|
2362
|
+
const split = splitFingerprint(attributes);
|
|
2363
|
+
const fingerprint = fingerprintEventAttributes(split.fingerprint, redact.text);
|
|
2364
|
+
const redacted = redact(split.attributes);
|
|
1986
2365
|
const span = trace3.getActiveSpan();
|
|
1987
2366
|
if (mode === "auto" && span?.isRecording()) {
|
|
1988
2367
|
noteOperationError(error);
|
|
1989
|
-
notifyException(error, redacted);
|
|
2368
|
+
notifyException(error, { ...redacted, ...fingerprint });
|
|
1990
2369
|
span.setAttributes({ "autter.severity": "error", ...redacted, ...structuredErrorAttributes(error) });
|
|
1991
|
-
const declared = errorAttributes(error);
|
|
1992
|
-
if (error
|
|
2370
|
+
const declared = { ...errorAttributes(error), ...fingerprint };
|
|
2371
|
+
if (isError(error)) {
|
|
1993
2372
|
span.addEvent("exception", {
|
|
1994
2373
|
"exception.type": error.name,
|
|
1995
2374
|
"exception.message": error.message,
|
|
@@ -2005,12 +2384,12 @@ function initAutterLogging(options) {
|
|
|
2005
2384
|
}
|
|
2006
2385
|
span.setStatus({
|
|
2007
2386
|
code: SpanStatusCode3.ERROR,
|
|
2008
|
-
message: error
|
|
2387
|
+
message: isError(error) ? error.message : String(error)
|
|
2009
2388
|
});
|
|
2010
2389
|
return;
|
|
2011
2390
|
}
|
|
2012
|
-
notifyException(error, redacted);
|
|
2013
|
-
captureExceptionAsLog(error, redacted);
|
|
2391
|
+
notifyException(error, { ...redacted, ...fingerprint });
|
|
2392
|
+
captureExceptionAsLog(error, { ...redacted, ...fingerprint });
|
|
2014
2393
|
}
|
|
2015
2394
|
const captured = /* @__PURE__ */ new WeakSet();
|
|
2016
2395
|
configureRuntimeLogger({
|
|
@@ -2037,9 +2416,18 @@ function initAutterLogging(options) {
|
|
|
2037
2416
|
}
|
|
2038
2417
|
});
|
|
2039
2418
|
setLoggerOnlyCapture(captureException2);
|
|
2040
|
-
const onCrash = (error) =>
|
|
2041
|
-
|
|
2419
|
+
const onCrash = (error) => {
|
|
2420
|
+
if (recordedAsRejection(error)) return;
|
|
2421
|
+
captureException2(error, { "autter.unhandled": true });
|
|
2422
|
+
};
|
|
2423
|
+
let uninstallRejections = null;
|
|
2424
|
+
if (options.captureGlobalErrors !== false) {
|
|
2042
2425
|
process.on("uncaughtExceptionMonitor", onCrash);
|
|
2426
|
+
if (options.captureUnhandledRejections !== false)
|
|
2427
|
+
uninstallRejections = installUnhandledRejectionCapture(
|
|
2428
|
+
(reason) => captureException2(reason, { "autter.unhandled_rejection": true })
|
|
2429
|
+
);
|
|
2430
|
+
}
|
|
2043
2431
|
registerFlushTarget("logging", { forceFlush: () => flushRuntimeLogs() });
|
|
2044
2432
|
let autoFlushHandle = options.autoFlush === false ? null : installAutterAutoFlush();
|
|
2045
2433
|
const handle = {
|
|
@@ -2047,15 +2435,17 @@ function initAutterLogging(options) {
|
|
|
2047
2435
|
flush: flushRuntimeLogs,
|
|
2048
2436
|
shutdown: async () => {
|
|
2049
2437
|
process.off("uncaughtExceptionMonitor", onCrash);
|
|
2438
|
+
uninstallRejections?.();
|
|
2439
|
+
uninstallRejections = null;
|
|
2050
2440
|
setLoggerOnlyCapture(null);
|
|
2051
2441
|
autoFlushHandle?.dispose();
|
|
2052
2442
|
autoFlushHandle = null;
|
|
2053
2443
|
unregisterFlushTargets();
|
|
2054
|
-
|
|
2444
|
+
if (L.active === handle) L.active = null;
|
|
2055
2445
|
await shutdownRuntimeLogger();
|
|
2056
2446
|
}
|
|
2057
2447
|
};
|
|
2058
|
-
|
|
2448
|
+
L.active = handle;
|
|
2059
2449
|
return handle;
|
|
2060
2450
|
}
|
|
2061
2451
|
|
|
@@ -2152,11 +2542,16 @@ export {
|
|
|
2152
2542
|
INGESTER_VERSION_HEADER,
|
|
2153
2543
|
RuntimeError,
|
|
2154
2544
|
SDK_IDENTITY,
|
|
2545
|
+
addBreadcrumb,
|
|
2546
|
+
autterErrorHandler,
|
|
2155
2547
|
autterErrorResponse,
|
|
2156
2548
|
autterFastify,
|
|
2549
|
+
autterFastifyErrors,
|
|
2550
|
+
autterKoa,
|
|
2157
2551
|
autterRequests,
|
|
2158
2552
|
captureException,
|
|
2159
2553
|
captureMessage,
|
|
2554
|
+
captureRequestError,
|
|
2160
2555
|
consoleSink,
|
|
2161
2556
|
createBrowserRelayFetchHandler,
|
|
2162
2557
|
createBrowserRelayHandler,
|
|
@@ -2191,6 +2586,7 @@ export {
|
|
|
2191
2586
|
startCaughtExceptionSampler,
|
|
2192
2587
|
toClientError,
|
|
2193
2588
|
trackLlmCall,
|
|
2589
|
+
unhandledRejectionsMode,
|
|
2194
2590
|
withLlmCall,
|
|
2195
2591
|
withProcessSpan,
|
|
2196
2592
|
withRuntimeOperation,
|