@autter/runtime-node 1.8.0 → 1.9.0
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 +69 -1
- package/dist/{chunk-7H3FCV4P.cjs → chunk-AN45KLWD.cjs} +11 -11
- package/dist/{chunk-MOZOUEMA.cjs → chunk-BHYHORUD.cjs} +44 -3
- package/dist/{chunk-QKY2OQSO.js → chunk-HHKQ3UCH.js} +533 -31
- package/dist/{chunk-UC3QH4PC.js → chunk-JSGGUWRD.js} +1 -1
- package/dist/{chunk-37W7YYXO.cjs → chunk-YWTIKZSI.cjs} +596 -94
- package/dist/{chunk-B5ZMOCEM.js → chunk-ZKM76SDP.js} +44 -3
- package/dist/cli.cjs +8 -8
- package/dist/cli.js +2 -2
- package/dist/index.cjs +424 -209
- package/dist/index.d.cts +75 -3
- package/dist/index.d.ts +75 -3
- package/dist/index.js +231 -16
- package/dist/{sinks-CIdlmYwy.d.cts → sinks-CLtQFPnn.d.cts} +39 -3
- package/dist/{sinks-CIdlmYwy.d.ts → sinks-CLtQFPnn.d.ts} +39 -3
- 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 +7 -3
|
@@ -2,7 +2,7 @@ import {
|
|
|
2
2
|
noteCompatFeature,
|
|
3
3
|
observeIngesterResponse,
|
|
4
4
|
sharedSlot
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-ZKM76SDP.js";
|
|
6
6
|
|
|
7
7
|
// ../runtime-core/dist/index.js
|
|
8
8
|
var SENSITIVE_KEY_PATTERNS = [
|
|
@@ -132,8 +132,8 @@ function redactArray(value, r, state, depth) {
|
|
|
132
132
|
state.ancestors.delete(value);
|
|
133
133
|
return r.mask;
|
|
134
134
|
}
|
|
135
|
-
const
|
|
136
|
-
for (let i = 0; i <
|
|
135
|
+
const limit2 = Math.min(length, MAX_COLLECTION_ENTRIES);
|
|
136
|
+
for (let i = 0; i < limit2; i += 1) {
|
|
137
137
|
let item;
|
|
138
138
|
try {
|
|
139
139
|
item = value[i];
|
|
@@ -143,7 +143,7 @@ function redactArray(value, r, state, depth) {
|
|
|
143
143
|
}
|
|
144
144
|
out.push(redactValue(item, r, state, depth + 1));
|
|
145
145
|
}
|
|
146
|
-
if (length >
|
|
146
|
+
if (length > limit2) {
|
|
147
147
|
out.push(r.mask);
|
|
148
148
|
}
|
|
149
149
|
state.ancestors.delete(value);
|
|
@@ -392,6 +392,53 @@ function toSpanAttributes(context) {
|
|
|
392
392
|
])
|
|
393
393
|
);
|
|
394
394
|
}
|
|
395
|
+
var DEBUG_IDS_ATTRIBUTE = "autter.debug_ids";
|
|
396
|
+
var REGISTRY = "_autterDebugIds";
|
|
397
|
+
var pathOfKey = /* @__PURE__ */ new Map();
|
|
398
|
+
function withoutPosition(location) {
|
|
399
|
+
const match = /^(.*?):\d+:\d+$/.exec(location.trim());
|
|
400
|
+
return match?.[1] ? match[1] : null;
|
|
401
|
+
}
|
|
402
|
+
function firstFramePath(stack) {
|
|
403
|
+
for (const raw of stack.split("\n")) {
|
|
404
|
+
const line = raw.trim();
|
|
405
|
+
if (line.startsWith("at ")) {
|
|
406
|
+
const body = line.slice(3).trim();
|
|
407
|
+
const withFn = /^(.*?)\s+\((.*)\)$/.exec(body);
|
|
408
|
+
const path = withoutPosition(withFn ? withFn[2] : body.replace(/^async /, ""));
|
|
409
|
+
if (path) return path;
|
|
410
|
+
continue;
|
|
411
|
+
}
|
|
412
|
+
const at = line.lastIndexOf("@");
|
|
413
|
+
if (at >= 0) {
|
|
414
|
+
const path = withoutPosition(line.slice(at + 1));
|
|
415
|
+
if (path) return path;
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
return null;
|
|
419
|
+
}
|
|
420
|
+
function debugIdsForStacks(stacks) {
|
|
421
|
+
try {
|
|
422
|
+
const registry = globalThis[REGISTRY];
|
|
423
|
+
if (!registry || typeof registry !== "object") return void 0;
|
|
424
|
+
const texts = stacks.filter((stack) => typeof stack === "string" && stack !== "");
|
|
425
|
+
if (texts.length === 0) return void 0;
|
|
426
|
+
let found;
|
|
427
|
+
for (const key of Object.keys(registry)) {
|
|
428
|
+
const id = registry[key];
|
|
429
|
+
if (typeof id !== "string" || !id) continue;
|
|
430
|
+
let path = pathOfKey.get(key);
|
|
431
|
+
if (path === void 0) {
|
|
432
|
+
path = firstFramePath(key);
|
|
433
|
+
pathOfKey.set(key, path);
|
|
434
|
+
}
|
|
435
|
+
if (path && texts.some((stack) => stack.includes(path))) (found ??= {})[path] = id;
|
|
436
|
+
}
|
|
437
|
+
return found;
|
|
438
|
+
} catch {
|
|
439
|
+
return void 0;
|
|
440
|
+
}
|
|
441
|
+
}
|
|
395
442
|
var CODE_PATTERN = /^[a-z][a-z0-9_]*(\.[a-z0-9_]+){0,3}$/;
|
|
396
443
|
var CODE_MAX_LENGTH = 80;
|
|
397
444
|
var WHY_MAX = 1e3;
|
|
@@ -514,6 +561,13 @@ function errorStatusOf(err) {
|
|
|
514
561
|
function isExpectedError(err) {
|
|
515
562
|
return field(err, "expected") === true;
|
|
516
563
|
}
|
|
564
|
+
var STACK_ATTRIBUTE_RE = /^exception\.cause\.\d+\.stacktrace$|^autter\.debug_ids$/;
|
|
565
|
+
function stackAttributesLast(attributes) {
|
|
566
|
+
const head = {};
|
|
567
|
+
const tail = {};
|
|
568
|
+
for (const [key, value] of Object.entries(attributes)) (STACK_ATTRIBUTE_RE.test(key) ? tail : head)[key] = value;
|
|
569
|
+
return { ...head, ...tail };
|
|
570
|
+
}
|
|
517
571
|
function errorAttributes(err, options = {}) {
|
|
518
572
|
const out = {};
|
|
519
573
|
if (!err || typeof err !== "object") return out;
|
|
@@ -530,6 +584,9 @@ function errorAttributes(err, options = {}) {
|
|
|
530
584
|
const expected = field(err, "expected");
|
|
531
585
|
if (typeof expected === "boolean") out["autter.error.expected"] = expected;
|
|
532
586
|
if (options.causes !== false) {
|
|
587
|
+
const stacks = options.stacks !== false;
|
|
588
|
+
const ownStack = field(err, "stack");
|
|
589
|
+
const allStacks = typeof ownStack === "string" && ownStack ? [ownStack] : [];
|
|
533
590
|
let cause = field(err, "cause");
|
|
534
591
|
const seen = /* @__PURE__ */ new Set([err]);
|
|
535
592
|
for (let n = 1; n <= CAUSE_DEPTH && cause !== void 0 && cause !== null; n++) {
|
|
@@ -543,8 +600,17 @@ function errorAttributes(err, options = {}) {
|
|
|
543
600
|
const causeCode = field(cause, "code");
|
|
544
601
|
if (typeof causeCode === "string" && causeCode)
|
|
545
602
|
out[`exception.cause.${n}.code`] = causeCode.slice(0, CODE_MAX_LENGTH);
|
|
603
|
+
const causeStack = isError2 ? field(cause, "stack") : void 0;
|
|
604
|
+
if (stacks && typeof causeStack === "string" && causeStack) {
|
|
605
|
+
out[`exception.cause.${n}.stacktrace`] = causeStack;
|
|
606
|
+
allStacks.push(causeStack);
|
|
607
|
+
}
|
|
546
608
|
cause = isError2 ? field(cause, "cause") : void 0;
|
|
547
609
|
}
|
|
610
|
+
if (stacks) {
|
|
611
|
+
const ids = debugIdsForStacks(allStacks);
|
|
612
|
+
if (ids) out[DEBUG_IDS_ATTRIBUTE] = JSON.stringify(ids);
|
|
613
|
+
}
|
|
548
614
|
}
|
|
549
615
|
return out;
|
|
550
616
|
}
|
|
@@ -758,6 +824,7 @@ function requestOutcome(input) {
|
|
|
758
824
|
if (input.error !== void 0 && isExpectedError(input.error)) return "degraded";
|
|
759
825
|
if (input.thrown || input.status !== void 0 && input.status >= 500)
|
|
760
826
|
return "failed";
|
|
827
|
+
if (input.degraded) return "degraded";
|
|
761
828
|
if (input.aborted) return "cancelled";
|
|
762
829
|
return "succeeded";
|
|
763
830
|
}
|
|
@@ -974,6 +1041,132 @@ function splitFingerprint(attributes) {
|
|
|
974
1041
|
const { fingerprint, ...rest } = attributes;
|
|
975
1042
|
return { attributes: rest, fingerprint };
|
|
976
1043
|
}
|
|
1044
|
+
var HANDLED_ATTRIBUTE = "autter.handled";
|
|
1045
|
+
var HANDLING_ATTRIBUTE = "autter.handling";
|
|
1046
|
+
var LOG_SOURCE_ATTRIBUTE = "autter.log.source";
|
|
1047
|
+
var LOG_MESSAGE_ATTRIBUTE = "autter.log.message";
|
|
1048
|
+
var DEGRADED_REASON_ATTRIBUTE = "autter.degraded.reason";
|
|
1049
|
+
var CAUGHT_DROPPED_ATTRIBUTE = "autter.caught.dropped";
|
|
1050
|
+
var LOG_MESSAGE_MAX = 1e3;
|
|
1051
|
+
var DEGRADED_REASON_MAX = 500;
|
|
1052
|
+
var CAUGHT_BURST = 10;
|
|
1053
|
+
var CAUGHT_REFILL_MS = 1e4;
|
|
1054
|
+
var SOURCES = ["console", "pino", "winston", "bunyan", "runtimeLogger"];
|
|
1055
|
+
function loggedErrorSources(option) {
|
|
1056
|
+
const out = {};
|
|
1057
|
+
for (const source of SOURCES)
|
|
1058
|
+
out[source] = option === false ? false : option === void 0 || option === true || typeof option !== "object" ? true : option[source] !== false;
|
|
1059
|
+
return out;
|
|
1060
|
+
}
|
|
1061
|
+
function handledErrorKind(error) {
|
|
1062
|
+
let type = "Error";
|
|
1063
|
+
let site = "";
|
|
1064
|
+
try {
|
|
1065
|
+
if (isError(error)) {
|
|
1066
|
+
type = String(error.name || "Error").slice(0, 200);
|
|
1067
|
+
const lines = String(error.stack ?? "").split("\n");
|
|
1068
|
+
site = (lines.find((line) => /^\s*at\s/.test(line)) ?? lines[1] ?? "").trim().slice(0, 300);
|
|
1069
|
+
}
|
|
1070
|
+
} catch {
|
|
1071
|
+
}
|
|
1072
|
+
return { key: `${type}
|
|
1073
|
+
${site}`, type };
|
|
1074
|
+
}
|
|
1075
|
+
function caughtErrorAttributes(source, message, dropped = 0) {
|
|
1076
|
+
return {
|
|
1077
|
+
[HANDLED_ATTRIBUTE]: true,
|
|
1078
|
+
[HANDLING_ATTRIBUTE]: "caught",
|
|
1079
|
+
[LOG_SOURCE_ATTRIBUTE]: source,
|
|
1080
|
+
...message ? { [LOG_MESSAGE_ATTRIBUTE]: message.slice(0, LOG_MESSAGE_MAX) } : {},
|
|
1081
|
+
...dropped > 0 ? { [CAUGHT_DROPPED_ATTRIBUTE]: dropped } : {}
|
|
1082
|
+
};
|
|
1083
|
+
}
|
|
1084
|
+
function degradedAttributes(reason, dropped = 0) {
|
|
1085
|
+
let text = "";
|
|
1086
|
+
try {
|
|
1087
|
+
text = String(reason ?? "");
|
|
1088
|
+
} catch {
|
|
1089
|
+
}
|
|
1090
|
+
return {
|
|
1091
|
+
[HANDLED_ATTRIBUTE]: true,
|
|
1092
|
+
[HANDLING_ATTRIBUTE]: "deliberate",
|
|
1093
|
+
[DEGRADED_REASON_ATTRIBUTE]: text.slice(0, DEGRADED_REASON_MAX) || "degraded",
|
|
1094
|
+
...dropped > 0 ? { [CAUGHT_DROPPED_ATTRIBUTE]: dropped } : {}
|
|
1095
|
+
};
|
|
1096
|
+
}
|
|
1097
|
+
var BurstLimiter = class {
|
|
1098
|
+
constructor(burst = CAUGHT_BURST, refillMs = CAUGHT_REFILL_MS, maxKeys = 1e3) {
|
|
1099
|
+
this.burst = burst;
|
|
1100
|
+
this.refillMs = refillMs;
|
|
1101
|
+
this.maxKeys = maxKeys;
|
|
1102
|
+
}
|
|
1103
|
+
burst;
|
|
1104
|
+
refillMs;
|
|
1105
|
+
maxKeys;
|
|
1106
|
+
buckets = /* @__PURE__ */ new Map();
|
|
1107
|
+
evicted = [];
|
|
1108
|
+
/**
|
|
1109
|
+
* -1 when the record is left out (and counted). Otherwise it is sent,
|
|
1110
|
+
* and the number of its kind left out since the last one sent is
|
|
1111
|
+
* returned, to report with it.
|
|
1112
|
+
*/
|
|
1113
|
+
take(key, type, now = Date.now()) {
|
|
1114
|
+
const bucket = this.buckets.get(key) ?? { tokens: this.burst, at: now, dropped: 0, type };
|
|
1115
|
+
this.buckets.delete(key);
|
|
1116
|
+
this.buckets.set(key, bucket);
|
|
1117
|
+
this.refill(bucket, now);
|
|
1118
|
+
let result;
|
|
1119
|
+
if (bucket.tokens < 1) {
|
|
1120
|
+
bucket.dropped += 1;
|
|
1121
|
+
result = -1;
|
|
1122
|
+
} else {
|
|
1123
|
+
bucket.tokens -= 1;
|
|
1124
|
+
result = bucket.dropped;
|
|
1125
|
+
bucket.dropped = 0;
|
|
1126
|
+
}
|
|
1127
|
+
while (this.buckets.size > this.maxKeys) {
|
|
1128
|
+
const oldest = this.buckets.keys().next().value;
|
|
1129
|
+
const gone = this.buckets.get(oldest);
|
|
1130
|
+
this.buckets.delete(oldest);
|
|
1131
|
+
if (gone.dropped > 0) this.evicted.push({ key: oldest, type: gone.type, dropped: gone.dropped });
|
|
1132
|
+
}
|
|
1133
|
+
return result;
|
|
1134
|
+
}
|
|
1135
|
+
/**
|
|
1136
|
+
* Counts no sent record carried: kinds that have a token again (nothing
|
|
1137
|
+
* of theirs came along to carry the count) or, with `all`, every kind
|
|
1138
|
+
* with a count. The counts returned are reset.
|
|
1139
|
+
*/
|
|
1140
|
+
drain(now = Date.now(), all = false) {
|
|
1141
|
+
const out = this.evicted;
|
|
1142
|
+
this.evicted = [];
|
|
1143
|
+
for (const [key, bucket] of this.buckets) {
|
|
1144
|
+
if (bucket.dropped === 0) continue;
|
|
1145
|
+
this.refill(bucket, now);
|
|
1146
|
+
if (!all && bucket.tokens < 1) continue;
|
|
1147
|
+
out.push({ key, type: bucket.type, dropped: bucket.dropped });
|
|
1148
|
+
bucket.dropped = 0;
|
|
1149
|
+
}
|
|
1150
|
+
return out;
|
|
1151
|
+
}
|
|
1152
|
+
/** When the next uncarried count can be drained, or null when none is pending. */
|
|
1153
|
+
nextDrainAt() {
|
|
1154
|
+
if (this.evicted.length) return 0;
|
|
1155
|
+
let next = null;
|
|
1156
|
+
for (const bucket of this.buckets.values()) {
|
|
1157
|
+
if (bucket.dropped === 0) continue;
|
|
1158
|
+
const at = bucket.at + Math.max(0, 1 - bucket.tokens) * this.refillMs;
|
|
1159
|
+
if (next === null || at < next) next = at;
|
|
1160
|
+
}
|
|
1161
|
+
return next;
|
|
1162
|
+
}
|
|
1163
|
+
refill(bucket, now) {
|
|
1164
|
+
if (now > bucket.at) {
|
|
1165
|
+
bucket.tokens = Math.min(this.burst, bucket.tokens + (now - bucket.at) / this.refillMs);
|
|
1166
|
+
bucket.at = now;
|
|
1167
|
+
}
|
|
1168
|
+
}
|
|
1169
|
+
};
|
|
977
1170
|
function testPattern(pattern, type, message) {
|
|
978
1171
|
if (typeof pattern === "string") {
|
|
979
1172
|
return pattern !== "" && (message.includes(pattern) || type.includes(pattern));
|
|
@@ -1022,6 +1215,267 @@ import {
|
|
|
1022
1215
|
trace
|
|
1023
1216
|
} from "@opentelemetry/api";
|
|
1024
1217
|
|
|
1218
|
+
// src/caught-errors.ts
|
|
1219
|
+
import { createRequire } from "module";
|
|
1220
|
+
import { join } from "path";
|
|
1221
|
+
import { format } from "util";
|
|
1222
|
+
import { Hook } from "require-in-the-middle";
|
|
1223
|
+
|
|
1224
|
+
// src/captured.ts
|
|
1225
|
+
var CAPTURED = sharedSlot("requests", () => ({ captured: /* @__PURE__ */ new WeakSet() })).captured;
|
|
1226
|
+
function markCaptured(error) {
|
|
1227
|
+
if (error && typeof error === "object") CAPTURED.add(error);
|
|
1228
|
+
}
|
|
1229
|
+
function isCaptured(error) {
|
|
1230
|
+
return !!error && typeof error === "object" && CAPTURED.has(error);
|
|
1231
|
+
}
|
|
1232
|
+
|
|
1233
|
+
// src/caught-errors.ts
|
|
1234
|
+
var S = sharedSlot("handled", () => ({
|
|
1235
|
+
sources: null,
|
|
1236
|
+
capture: null,
|
|
1237
|
+
tail: null,
|
|
1238
|
+
limiter: new BurstLimiter(),
|
|
1239
|
+
decided: /* @__PURE__ */ new WeakSet(),
|
|
1240
|
+
timer: null,
|
|
1241
|
+
hooked: false
|
|
1242
|
+
}));
|
|
1243
|
+
var PATCHED = /* @__PURE__ */ Symbol.for("autter.runtime.logged-errors");
|
|
1244
|
+
function setHandledCapture(capture) {
|
|
1245
|
+
S.capture = capture;
|
|
1246
|
+
}
|
|
1247
|
+
function setHandledTail(tail) {
|
|
1248
|
+
S.tail = tail;
|
|
1249
|
+
}
|
|
1250
|
+
function scheduleTail() {
|
|
1251
|
+
if (S.timer) return;
|
|
1252
|
+
const at = S.limiter.nextDrainAt();
|
|
1253
|
+
if (at === null) return;
|
|
1254
|
+
S.timer = setTimeout(() => sendTails(false), Math.max(0, at - Date.now()) + 5);
|
|
1255
|
+
S.timer.unref?.();
|
|
1256
|
+
}
|
|
1257
|
+
function sendTails(all) {
|
|
1258
|
+
if (S.timer) clearTimeout(S.timer);
|
|
1259
|
+
S.timer = null;
|
|
1260
|
+
for (const tail of S.limiter.drain(Date.now(), all)) {
|
|
1261
|
+
try {
|
|
1262
|
+
S.tail?.(tail.type, tail.dropped);
|
|
1263
|
+
} catch {
|
|
1264
|
+
}
|
|
1265
|
+
}
|
|
1266
|
+
if (!all) scheduleTail();
|
|
1267
|
+
}
|
|
1268
|
+
function limit(prefix, error) {
|
|
1269
|
+
const kind = handledErrorKind(error);
|
|
1270
|
+
const dropped = S.limiter.take(`${prefix}
|
|
1271
|
+
${kind.key}`, kind.type);
|
|
1272
|
+
if (dropped < 0) scheduleTail();
|
|
1273
|
+
return dropped;
|
|
1274
|
+
}
|
|
1275
|
+
function reportLoggedError(error, source, message, attributes) {
|
|
1276
|
+
if (!S.sources?.[source] || !isError(error)) return;
|
|
1277
|
+
if (S.decided.has(error) || isCaptured(error)) return;
|
|
1278
|
+
S.decided.add(error);
|
|
1279
|
+
const run = () => {
|
|
1280
|
+
try {
|
|
1281
|
+
if (isCaptured(error) || !S.capture || !S.sources) return;
|
|
1282
|
+
const dropped = limit("caught", error);
|
|
1283
|
+
if (dropped < 0) return;
|
|
1284
|
+
markCaptured(error);
|
|
1285
|
+
S.capture(error, { ...attributes, ...caughtErrorAttributes(source, message, dropped) }, "error");
|
|
1286
|
+
} catch {
|
|
1287
|
+
}
|
|
1288
|
+
};
|
|
1289
|
+
try {
|
|
1290
|
+
setImmediate(run);
|
|
1291
|
+
} catch {
|
|
1292
|
+
}
|
|
1293
|
+
}
|
|
1294
|
+
function reportDegraded(error, reason, attributes) {
|
|
1295
|
+
if (!S.capture) return;
|
|
1296
|
+
if (error && typeof error === "object") S.decided.add(error);
|
|
1297
|
+
const dropped = limit("deliberate", error);
|
|
1298
|
+
if (dropped < 0) return;
|
|
1299
|
+
markCaptured(error);
|
|
1300
|
+
S.capture(error, { ...attributes, ...degradedAttributes(reason, dropped) }, "warning");
|
|
1301
|
+
}
|
|
1302
|
+
function onConsoleError(args) {
|
|
1303
|
+
if (!S.sources?.console) return;
|
|
1304
|
+
if (typeof args[0] === "string" && args[0].startsWith("[autter")) return;
|
|
1305
|
+
for (const arg of args) {
|
|
1306
|
+
if (!isError(arg)) continue;
|
|
1307
|
+
let message = "";
|
|
1308
|
+
try {
|
|
1309
|
+
message = format(...args.filter((other) => other !== arg));
|
|
1310
|
+
} catch {
|
|
1311
|
+
}
|
|
1312
|
+
reportLoggedError(arg, "console", message);
|
|
1313
|
+
}
|
|
1314
|
+
}
|
|
1315
|
+
function patchConsole() {
|
|
1316
|
+
const target = console;
|
|
1317
|
+
try {
|
|
1318
|
+
if (target[PATCHED]) return;
|
|
1319
|
+
const original = target.error;
|
|
1320
|
+
if (typeof original !== "function") return;
|
|
1321
|
+
target.error = function(...args) {
|
|
1322
|
+
try {
|
|
1323
|
+
onConsoleError(args);
|
|
1324
|
+
} catch {
|
|
1325
|
+
}
|
|
1326
|
+
return original.apply(this, args);
|
|
1327
|
+
};
|
|
1328
|
+
Object.defineProperty(target, PATCHED, { value: true });
|
|
1329
|
+
} catch {
|
|
1330
|
+
}
|
|
1331
|
+
}
|
|
1332
|
+
function errorField(value) {
|
|
1333
|
+
if (!value || typeof value !== "object") return void 0;
|
|
1334
|
+
const record = value;
|
|
1335
|
+
for (const key of ["err", "error"]) if (isError(record[key])) return record[key];
|
|
1336
|
+
let seen = 0;
|
|
1337
|
+
for (const key of Object.keys(record)) {
|
|
1338
|
+
if (++seen > 20) break;
|
|
1339
|
+
if (isError(record[key])) return record[key];
|
|
1340
|
+
}
|
|
1341
|
+
return void 0;
|
|
1342
|
+
}
|
|
1343
|
+
function wrapMethod(target, key, inspect) {
|
|
1344
|
+
const original = target[key];
|
|
1345
|
+
if (typeof original !== "function") return;
|
|
1346
|
+
target[key] = function(...args) {
|
|
1347
|
+
try {
|
|
1348
|
+
inspect(this, args);
|
|
1349
|
+
} catch {
|
|
1350
|
+
}
|
|
1351
|
+
return original.apply(this, args);
|
|
1352
|
+
};
|
|
1353
|
+
}
|
|
1354
|
+
function markPatched(target) {
|
|
1355
|
+
const record = target;
|
|
1356
|
+
if (record[PATCHED]) return false;
|
|
1357
|
+
try {
|
|
1358
|
+
Object.defineProperty(target, PATCHED, { value: true });
|
|
1359
|
+
return true;
|
|
1360
|
+
} catch {
|
|
1361
|
+
return false;
|
|
1362
|
+
}
|
|
1363
|
+
}
|
|
1364
|
+
function patchPino(protoFactory) {
|
|
1365
|
+
if (typeof protoFactory !== "function") return;
|
|
1366
|
+
const proto = Object.getPrototypeOf(protoFactory());
|
|
1367
|
+
if (!proto) return;
|
|
1368
|
+
const write = Object.getOwnPropertySymbols(proto).find((symbol) => symbol.description === "pino.write");
|
|
1369
|
+
if (!write || typeof proto[write] !== "function" || !markPatched(proto)) return;
|
|
1370
|
+
wrapMethod(proto, write, (_self, [obj, msg, level]) => {
|
|
1371
|
+
if (!S.sources?.pino || typeof level !== "number" || level < 50) return;
|
|
1372
|
+
const error = isError(obj) ? obj : errorField(obj);
|
|
1373
|
+
if (error) reportLoggedError(error, "pino", typeof msg === "string" ? msg : void 0);
|
|
1374
|
+
});
|
|
1375
|
+
}
|
|
1376
|
+
var WINSTON_ERROR_LEVELS = /^(?:error|fatal|crit|alert|emerg)$/;
|
|
1377
|
+
function winstonErrorLevel(logger, level) {
|
|
1378
|
+
if (typeof level !== "string") return false;
|
|
1379
|
+
const levels = logger?.levels;
|
|
1380
|
+
if (levels && typeof levels[level] === "number" && typeof levels.error === "number")
|
|
1381
|
+
return levels[level] <= levels.error;
|
|
1382
|
+
return WINSTON_ERROR_LEVELS.test(level);
|
|
1383
|
+
}
|
|
1384
|
+
function patchWinston(Logger) {
|
|
1385
|
+
const proto = Logger?.prototype;
|
|
1386
|
+
if (!proto || typeof proto.log !== "function" || !markPatched(proto)) return;
|
|
1387
|
+
wrapMethod(proto, "log", (self, [level, msg, ...meta]) => {
|
|
1388
|
+
if (!S.sources?.winston || !winstonErrorLevel(self, level)) return;
|
|
1389
|
+
for (const arg of [msg, ...meta]) {
|
|
1390
|
+
const error = isError(arg) ? arg : errorField(arg);
|
|
1391
|
+
if (error) reportLoggedError(error, "winston", typeof msg === "string" ? msg : void 0);
|
|
1392
|
+
}
|
|
1393
|
+
});
|
|
1394
|
+
wrapMethod(proto, "_addDefaultMeta", (self, [info]) => {
|
|
1395
|
+
if (!S.sources?.winston || !info || typeof info !== "object") return;
|
|
1396
|
+
if (!winstonErrorLevel(self, info.level)) return;
|
|
1397
|
+
if (isError(info)) reportLoggedError(info, "winston");
|
|
1398
|
+
else {
|
|
1399
|
+
const error = errorField(info);
|
|
1400
|
+
const message = info.message;
|
|
1401
|
+
if (error) reportLoggedError(error, "winston", typeof message === "string" ? message : void 0);
|
|
1402
|
+
}
|
|
1403
|
+
});
|
|
1404
|
+
}
|
|
1405
|
+
function patchBunyan(Logger) {
|
|
1406
|
+
const proto = Logger?.prototype;
|
|
1407
|
+
if (!proto || typeof proto.error !== "function" || !markPatched(proto)) return;
|
|
1408
|
+
for (const [method, level] of [["error", 50], ["fatal", 60]]) {
|
|
1409
|
+
wrapMethod(proto, method, (self, args) => {
|
|
1410
|
+
if (!S.sources?.bunyan || args.length === 0) return;
|
|
1411
|
+
const min = self?._level;
|
|
1412
|
+
if (typeof min === "number" && min > level) return;
|
|
1413
|
+
const error = isError(args[0]) ? args[0] : errorField(args[0]);
|
|
1414
|
+
if (!error) return;
|
|
1415
|
+
let message = "";
|
|
1416
|
+
try {
|
|
1417
|
+
message = format(...args.slice(1));
|
|
1418
|
+
} catch {
|
|
1419
|
+
}
|
|
1420
|
+
reportLoggedError(error, "bunyan", message);
|
|
1421
|
+
});
|
|
1422
|
+
}
|
|
1423
|
+
}
|
|
1424
|
+
var TARGETS = [
|
|
1425
|
+
{ file: "pino/lib/proto.js", patch: patchPino },
|
|
1426
|
+
{ file: "winston/lib/winston/logger.js", patch: patchWinston },
|
|
1427
|
+
{ file: "bunyan/lib/bunyan.js", patch: patchBunyan }
|
|
1428
|
+
];
|
|
1429
|
+
function patchModule(file, exports) {
|
|
1430
|
+
const target = TARGETS.find((entry) => entry.file === file);
|
|
1431
|
+
if (!target) return;
|
|
1432
|
+
try {
|
|
1433
|
+
target.patch(exports);
|
|
1434
|
+
} catch {
|
|
1435
|
+
}
|
|
1436
|
+
}
|
|
1437
|
+
function scanModuleCache() {
|
|
1438
|
+
try {
|
|
1439
|
+
const cache = createRequire(join(process.cwd(), "autter-runtime-loggers.cjs")).cache;
|
|
1440
|
+
for (const [key, entry] of Object.entries(cache)) {
|
|
1441
|
+
const path = key.replace(/\\/g, "/");
|
|
1442
|
+
for (const target of TARGETS)
|
|
1443
|
+
if (path.endsWith(`/node_modules/${target.file}`)) patchModule(target.file, entry?.exports);
|
|
1444
|
+
}
|
|
1445
|
+
} catch {
|
|
1446
|
+
}
|
|
1447
|
+
}
|
|
1448
|
+
function hookLaterLoads() {
|
|
1449
|
+
if (S.hooked) return;
|
|
1450
|
+
S.hooked = true;
|
|
1451
|
+
try {
|
|
1452
|
+
new Hook(["pino", "winston", "bunyan"], { internals: true }, (exports, name) => {
|
|
1453
|
+
const file = name.replace(/\\/g, "/");
|
|
1454
|
+
patchModule(file === "bunyan" ? "bunyan/lib/bunyan.js" : file, exports);
|
|
1455
|
+
return exports;
|
|
1456
|
+
});
|
|
1457
|
+
} catch {
|
|
1458
|
+
}
|
|
1459
|
+
for (const delay of [0, 1e3, 1e4]) {
|
|
1460
|
+
const timer = setTimeout(scanModuleCache, delay);
|
|
1461
|
+
timer.unref?.();
|
|
1462
|
+
}
|
|
1463
|
+
}
|
|
1464
|
+
function startLoggedErrorCapture(option) {
|
|
1465
|
+
const sources = loggedErrorSources(option);
|
|
1466
|
+
S.sources = sources;
|
|
1467
|
+
if (!Object.values(sources).some(Boolean)) return;
|
|
1468
|
+
if (sources.console) patchConsole();
|
|
1469
|
+
if (sources.pino || sources.winston || sources.bunyan) {
|
|
1470
|
+
scanModuleCache();
|
|
1471
|
+
hookLaterLoads();
|
|
1472
|
+
}
|
|
1473
|
+
}
|
|
1474
|
+
function stopLoggedErrorCapture() {
|
|
1475
|
+
S.sources = null;
|
|
1476
|
+
sendTails(true);
|
|
1477
|
+
}
|
|
1478
|
+
|
|
1025
1479
|
// src/sinks.ts
|
|
1026
1480
|
import {
|
|
1027
1481
|
appendFileSync,
|
|
@@ -1030,7 +1484,7 @@ import {
|
|
|
1030
1484
|
statSync,
|
|
1031
1485
|
unlinkSync
|
|
1032
1486
|
} from "fs";
|
|
1033
|
-
import { join, resolve } from "path";
|
|
1487
|
+
import { join as join2, resolve } from "path";
|
|
1034
1488
|
var sinkStats = sharedSlot("sinks", () => ({ dropped: 0 }));
|
|
1035
1489
|
function otlpSink(options = {}) {
|
|
1036
1490
|
let context = null;
|
|
@@ -1284,14 +1738,14 @@ function formatPretty(event, color = false) {
|
|
|
1284
1738
|
return lines.join("\n");
|
|
1285
1739
|
}
|
|
1286
1740
|
function consoleSink(options = {}) {
|
|
1287
|
-
const
|
|
1741
|
+
const format2 = options.format ?? (process.env.NODE_ENV === "production" ? "json" : "pretty");
|
|
1288
1742
|
const color = options.color ?? (process.stdout?.isTTY === true && !process.env.NO_COLOR);
|
|
1289
1743
|
const write = options.write ?? ((line) => console.log(line));
|
|
1290
1744
|
return {
|
|
1291
1745
|
name: "console",
|
|
1292
1746
|
write(event) {
|
|
1293
1747
|
write(
|
|
1294
|
-
|
|
1748
|
+
format2 === "json" ? JSON.stringify({
|
|
1295
1749
|
...event.attributes,
|
|
1296
1750
|
level: event.level,
|
|
1297
1751
|
message: event.message
|
|
@@ -1321,7 +1775,7 @@ function fileSink(options = {}) {
|
|
|
1321
1775
|
const existing = files().filter((file) => file.name !== current);
|
|
1322
1776
|
const excess = existing.length + 1 - maxFiles;
|
|
1323
1777
|
for (const file of existing.slice(0, Math.max(0, excess)))
|
|
1324
|
-
unlinkSync(
|
|
1778
|
+
unlinkSync(join2(dir, file.name));
|
|
1325
1779
|
};
|
|
1326
1780
|
const open = (nextDay) => {
|
|
1327
1781
|
mkdirSync(dir, { recursive: true });
|
|
@@ -1329,7 +1783,7 @@ function fileSink(options = {}) {
|
|
|
1329
1783
|
const sameDay = files().filter((file) => file.day === day);
|
|
1330
1784
|
index = sameDay.length ? sameDay[sameDay.length - 1].index : 0;
|
|
1331
1785
|
try {
|
|
1332
|
-
currentBytes = statSync(
|
|
1786
|
+
currentBytes = statSync(join2(dir, fileName(day, index))).size;
|
|
1333
1787
|
} catch {
|
|
1334
1788
|
currentBytes = 0;
|
|
1335
1789
|
}
|
|
@@ -1371,7 +1825,7 @@ function fileSink(options = {}) {
|
|
|
1371
1825
|
currentBytes = 0;
|
|
1372
1826
|
prune();
|
|
1373
1827
|
}
|
|
1374
|
-
appendFileSync(
|
|
1828
|
+
appendFileSync(join2(dir, fileName(day, index)), line);
|
|
1375
1829
|
currentBytes += bytes;
|
|
1376
1830
|
} catch (error) {
|
|
1377
1831
|
disable(error);
|
|
@@ -1467,13 +1921,13 @@ function emit(level, message, attributes, extra = {}, options = {}) {
|
|
|
1467
1921
|
const exceptions = Object.fromEntries(
|
|
1468
1922
|
Object.entries(user).filter(([key]) => key.startsWith("exception."))
|
|
1469
1923
|
);
|
|
1470
|
-
const raw = {
|
|
1924
|
+
const raw = stackAttributesLast({
|
|
1471
1925
|
"autter.event.id": randomUUID(),
|
|
1472
1926
|
...extra,
|
|
1473
1927
|
...exceptions,
|
|
1474
1928
|
...operationAttributes(state),
|
|
1475
1929
|
...user
|
|
1476
|
-
};
|
|
1930
|
+
});
|
|
1477
1931
|
const enrichers = shared.config?.options?.enrich;
|
|
1478
1932
|
if (summary && state && enrichers?.length) {
|
|
1479
1933
|
const event2 = { level, message, attributes: raw };
|
|
@@ -1583,29 +2037,32 @@ function createRuntimeLogger(attributes = {}) {
|
|
|
1583
2037
|
debug: (message, extra) => emit("debug", message, merge(attrs, extra)),
|
|
1584
2038
|
info: (message, extra) => emit("info", message, merge(attrs, extra)),
|
|
1585
2039
|
warn: (message, extra) => emit("warning", message, merge(attrs, extra)),
|
|
1586
|
-
error: (error, extra) =>
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
2040
|
+
error: (error, extra) => {
|
|
2041
|
+
emit(
|
|
2042
|
+
"error",
|
|
2043
|
+
isError(error) ? error.message : String(error),
|
|
2044
|
+
{
|
|
2045
|
+
...attrs,
|
|
2046
|
+
...extra,
|
|
2047
|
+
...isError(error) ? {
|
|
2048
|
+
"exception.type": error.name,
|
|
2049
|
+
"exception.stacktrace": error.stack ?? ""
|
|
2050
|
+
} : {}
|
|
2051
|
+
},
|
|
2052
|
+
errorAttributes(error)
|
|
2053
|
+
);
|
|
2054
|
+
if (isError(error)) reportLoggedError(error, "runtimeLogger", void 0, toSpanAttributes(merge(attrs, extra)));
|
|
2055
|
+
}
|
|
1599
2056
|
};
|
|
1600
2057
|
}
|
|
1601
2058
|
var runtimeLogger = createRuntimeLogger();
|
|
1602
|
-
function captureExceptionAsLog(error, attributes = {}) {
|
|
2059
|
+
function captureExceptionAsLog(error, attributes = {}, level = "error") {
|
|
1603
2060
|
const isErr = isError(error);
|
|
1604
2061
|
const message = isErr ? error.message : String(error);
|
|
1605
2062
|
const stack = isErr && error.stack ? error.stack : new Error().stack?.split("\n").filter((line, i) => i === 0 || !/captureException/.test(line)).join("\n") ?? "";
|
|
1606
|
-
noteOperationError(error);
|
|
2063
|
+
if (level === "error") noteOperationError(error);
|
|
1607
2064
|
emit(
|
|
1608
|
-
|
|
2065
|
+
level,
|
|
1609
2066
|
message,
|
|
1610
2067
|
{
|
|
1611
2068
|
...attributes,
|
|
@@ -1690,13 +2147,15 @@ function summaryExtra(state, level, http = {}) {
|
|
|
1690
2147
|
"autter.operation.level": maxLevel(state.inline.level, level),
|
|
1691
2148
|
...state.ai ? { "autter.operation.ai": state.ai } : {},
|
|
1692
2149
|
...http,
|
|
1693
|
-
|
|
2150
|
+
// A summary is no occurrence: no cause stacks or debug ids.
|
|
2151
|
+
...err !== void 0 ? errorAttributes(err, { stacks: false }) : {},
|
|
1694
2152
|
...state.inline.logsTruncated ? { "autter.operation.logs_truncated": true } : {},
|
|
1695
2153
|
...state.inline.logs.length ? { "autter.operation.logs": state.inline.logs } : {}
|
|
1696
2154
|
};
|
|
1697
2155
|
}
|
|
1698
2156
|
function emitSummary(state, options = {}) {
|
|
1699
2157
|
if (state.sealed) return;
|
|
2158
|
+
settleDegraded(state);
|
|
1700
2159
|
const err = state.error;
|
|
1701
2160
|
const level = state.outcome === "failed" ? "error" : "info";
|
|
1702
2161
|
emit(
|
|
@@ -1714,6 +2173,10 @@ function emitSummary(state, options = {}) {
|
|
|
1714
2173
|
);
|
|
1715
2174
|
state.sealed = true;
|
|
1716
2175
|
}
|
|
2176
|
+
function settleDegraded(state) {
|
|
2177
|
+
if (state.degraded && state.outcome === "succeeded" && !state.explicitOutcome)
|
|
2178
|
+
state.outcome = "degraded";
|
|
2179
|
+
}
|
|
1717
2180
|
function reportExplicitFailure(state) {
|
|
1718
2181
|
if (state.outcome === "failed" && state.explicitOutcome && !state.errorThrown)
|
|
1719
2182
|
shared.config?.reportOutcome(state.name, state.message ?? "Operation failed", {
|
|
@@ -1753,6 +2216,7 @@ function startOperation(name, fn, attributes, options, internal = {}) {
|
|
|
1753
2216
|
shared.config?.captureThrown?.(error);
|
|
1754
2217
|
throw error;
|
|
1755
2218
|
} finally {
|
|
2219
|
+
settleDegraded(state);
|
|
1756
2220
|
trace.getActiveSpan()?.setAttributes({
|
|
1757
2221
|
...traceAttributes(state),
|
|
1758
2222
|
"autter.operation.outcome": state.outcome,
|
|
@@ -1824,6 +2288,30 @@ var runtimeContext = {
|
|
|
1824
2288
|
noteOperationError(error);
|
|
1825
2289
|
runtimeLogger.error(error, attributes);
|
|
1826
2290
|
},
|
|
2291
|
+
/**
|
|
2292
|
+
* The app caught `error` and chose to continue on a fallback (RF-02):
|
|
2293
|
+
*
|
|
2294
|
+
* } catch (err) {
|
|
2295
|
+
* runtimeContext.degraded(err, "pricing service down, using cached prices");
|
|
2296
|
+
* }
|
|
2297
|
+
*
|
|
2298
|
+
* Recorded as an occurrence at `warning` severity, marked
|
|
2299
|
+
* `autter.handling = "deliberate"` with the reason (grouped by its code
|
|
2300
|
+
* when the error has one), never an incident. The current request or
|
|
2301
|
+
* operation ends "degraded" unless it failed or set its outcome.
|
|
2302
|
+
*/
|
|
2303
|
+
degraded(error, reason, attributes) {
|
|
2304
|
+
try {
|
|
2305
|
+
const state = currentOperation();
|
|
2306
|
+
if (state) {
|
|
2307
|
+
state.degraded = true;
|
|
2308
|
+
noteOperationError(error, false, state);
|
|
2309
|
+
appendInlineLog(state.inline, "warning", `degraded: ${String(reason)}`, void 0, redact);
|
|
2310
|
+
}
|
|
2311
|
+
reportDegraded(error, reason, toSpanAttributes(userContext(attributes, redact)));
|
|
2312
|
+
} catch {
|
|
2313
|
+
}
|
|
2314
|
+
},
|
|
1827
2315
|
/** Current operation id. */
|
|
1828
2316
|
get id() {
|
|
1829
2317
|
return local.getStore()?.id;
|
|
@@ -1853,6 +2341,15 @@ var runtimeContext = {
|
|
|
1853
2341
|
});
|
|
1854
2342
|
}
|
|
1855
2343
|
};
|
|
2344
|
+
setHandledTail(
|
|
2345
|
+
(type, dropped) => emit(
|
|
2346
|
+
"warning",
|
|
2347
|
+
`${dropped} caught ${type} error(s) left out by the burst limit`,
|
|
2348
|
+
{ "autter.caught.dropped": dropped, "autter.caught.type": type },
|
|
2349
|
+
{},
|
|
2350
|
+
{ state: null }
|
|
2351
|
+
)
|
|
2352
|
+
);
|
|
1856
2353
|
|
|
1857
2354
|
// src/breadcrumbs.ts
|
|
1858
2355
|
var INVALID_TRACE_ID = "00000000000000000000000000000000";
|
|
@@ -1915,10 +2412,10 @@ function recordLogBreadcrumb(level, message, attributes, state) {
|
|
|
1915
2412
|
}
|
|
1916
2413
|
}
|
|
1917
2414
|
function attributeBudget() {
|
|
1918
|
-
const
|
|
2415
|
+
const limit2 = Number(
|
|
1919
2416
|
process.env.OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT ?? process.env.OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT
|
|
1920
2417
|
);
|
|
1921
|
-
return Number.isFinite(
|
|
2418
|
+
return Number.isFinite(limit2) && limit2 > 0 ? Math.min(BREADCRUMBS_ATTRIBUTE_MAX_CHARS, limit2) : BREADCRUMBS_ATTRIBUTE_MAX_CHARS;
|
|
1922
2419
|
}
|
|
1923
2420
|
function rememberThrownBreadcrumbs(error, state) {
|
|
1924
2421
|
if (state.breadcrumbs && error !== null && (typeof error === "object" || typeof error === "function"))
|
|
@@ -1964,6 +2461,11 @@ export {
|
|
|
1964
2461
|
createErrorFilter,
|
|
1965
2462
|
addBreadcrumb,
|
|
1966
2463
|
breadcrumbEventAttributes,
|
|
2464
|
+
markCaptured,
|
|
2465
|
+
isCaptured,
|
|
2466
|
+
setHandledCapture,
|
|
2467
|
+
startLoggedErrorCapture,
|
|
2468
|
+
stopLoggedErrorCapture,
|
|
1967
2469
|
otlpSink,
|
|
1968
2470
|
consoleSink,
|
|
1969
2471
|
fileSink,
|