@autter/runtime-node 1.7.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 +103 -1
- package/dist/{chunk-HKVFURYT.cjs → chunk-AN45KLWD.cjs} +11 -11
- package/dist/{chunk-GYTQLITW.cjs → chunk-BHYHORUD.cjs} +58 -3
- package/dist/{chunk-KA4AOZX2.js → chunk-HHKQ3UCH.js} +571 -31
- package/dist/{chunk-QUC6OUE7.js → chunk-JSGGUWRD.js} +1 -1
- package/dist/{chunk-RV4MJNM2.cjs → chunk-YWTIKZSI.cjs} +631 -91
- package/dist/{chunk-LG52CW3V.js → chunk-ZKM76SDP.js} +58 -3
- package/dist/cli.cjs +8 -8
- package/dist/cli.js +2 -2
- package/dist/index.cjs +447 -210
- package/dist/index.d.cts +98 -3
- package/dist/index.d.ts +98 -3
- package/dist/index.js +253 -16
- package/dist/{sinks-CPGDyfU8.d.ts → sinks-CLtQFPnn.d.cts} +59 -3
- package/dist/{sinks-CPGDyfU8.d.cts → sinks-CLtQFPnn.d.ts} +59 -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,169 @@ 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
|
+
};
|
|
1170
|
+
function testPattern(pattern, type, message) {
|
|
1171
|
+
if (typeof pattern === "string") {
|
|
1172
|
+
return pattern !== "" && (message.includes(pattern) || type.includes(pattern));
|
|
1173
|
+
}
|
|
1174
|
+
if (!(pattern instanceof RegExp)) return false;
|
|
1175
|
+
const re = pattern.global || pattern.sticky ? new RegExp(pattern.source, pattern.flags.replace(/[gy]/g, "")) : pattern;
|
|
1176
|
+
return re.test(message) || type !== "" && (re.test(type) || re.test(`${type}: ${message}`));
|
|
1177
|
+
}
|
|
1178
|
+
function isIgnoredError(patterns, type, message) {
|
|
1179
|
+
if (!patterns?.length) return false;
|
|
1180
|
+
for (const pattern of patterns) {
|
|
1181
|
+
try {
|
|
1182
|
+
if (testPattern(pattern, type, message)) return true;
|
|
1183
|
+
} catch {
|
|
1184
|
+
}
|
|
1185
|
+
}
|
|
1186
|
+
return false;
|
|
1187
|
+
}
|
|
1188
|
+
function effectiveSampleRate(rate) {
|
|
1189
|
+
return typeof rate === "number" && Number.isFinite(rate) && rate >= 0 && rate < 1 ? rate : null;
|
|
1190
|
+
}
|
|
1191
|
+
function createErrorFilter(options = {}, random = Math.random) {
|
|
1192
|
+
const patterns = Array.isArray(options.ignoreErrors) ? options.ignoreErrors.slice() : [];
|
|
1193
|
+
const rate = effectiveSampleRate(options.sampleRate);
|
|
1194
|
+
if (!patterns.length && rate === null) return () => true;
|
|
1195
|
+
return (input) => {
|
|
1196
|
+
try {
|
|
1197
|
+
if (isIgnoredError(patterns, input.type ?? "", input.message ?? "")) return false;
|
|
1198
|
+
if (rate === null || input.crash) return true;
|
|
1199
|
+
const severity = input.severity ?? "error";
|
|
1200
|
+
if (severity !== "error") return true;
|
|
1201
|
+
return random() < rate;
|
|
1202
|
+
} catch {
|
|
1203
|
+
return true;
|
|
1204
|
+
}
|
|
1205
|
+
};
|
|
1206
|
+
}
|
|
977
1207
|
|
|
978
1208
|
// src/breadcrumbs.ts
|
|
979
1209
|
import { trace as trace2 } from "@opentelemetry/api";
|
|
@@ -985,6 +1215,267 @@ import {
|
|
|
985
1215
|
trace
|
|
986
1216
|
} from "@opentelemetry/api";
|
|
987
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
|
+
|
|
988
1479
|
// src/sinks.ts
|
|
989
1480
|
import {
|
|
990
1481
|
appendFileSync,
|
|
@@ -993,7 +1484,7 @@ import {
|
|
|
993
1484
|
statSync,
|
|
994
1485
|
unlinkSync
|
|
995
1486
|
} from "fs";
|
|
996
|
-
import { join, resolve } from "path";
|
|
1487
|
+
import { join as join2, resolve } from "path";
|
|
997
1488
|
var sinkStats = sharedSlot("sinks", () => ({ dropped: 0 }));
|
|
998
1489
|
function otlpSink(options = {}) {
|
|
999
1490
|
let context = null;
|
|
@@ -1247,14 +1738,14 @@ function formatPretty(event, color = false) {
|
|
|
1247
1738
|
return lines.join("\n");
|
|
1248
1739
|
}
|
|
1249
1740
|
function consoleSink(options = {}) {
|
|
1250
|
-
const
|
|
1741
|
+
const format2 = options.format ?? (process.env.NODE_ENV === "production" ? "json" : "pretty");
|
|
1251
1742
|
const color = options.color ?? (process.stdout?.isTTY === true && !process.env.NO_COLOR);
|
|
1252
1743
|
const write = options.write ?? ((line) => console.log(line));
|
|
1253
1744
|
return {
|
|
1254
1745
|
name: "console",
|
|
1255
1746
|
write(event) {
|
|
1256
1747
|
write(
|
|
1257
|
-
|
|
1748
|
+
format2 === "json" ? JSON.stringify({
|
|
1258
1749
|
...event.attributes,
|
|
1259
1750
|
level: event.level,
|
|
1260
1751
|
message: event.message
|
|
@@ -1284,7 +1775,7 @@ function fileSink(options = {}) {
|
|
|
1284
1775
|
const existing = files().filter((file) => file.name !== current);
|
|
1285
1776
|
const excess = existing.length + 1 - maxFiles;
|
|
1286
1777
|
for (const file of existing.slice(0, Math.max(0, excess)))
|
|
1287
|
-
unlinkSync(
|
|
1778
|
+
unlinkSync(join2(dir, file.name));
|
|
1288
1779
|
};
|
|
1289
1780
|
const open = (nextDay) => {
|
|
1290
1781
|
mkdirSync(dir, { recursive: true });
|
|
@@ -1292,7 +1783,7 @@ function fileSink(options = {}) {
|
|
|
1292
1783
|
const sameDay = files().filter((file) => file.day === day);
|
|
1293
1784
|
index = sameDay.length ? sameDay[sameDay.length - 1].index : 0;
|
|
1294
1785
|
try {
|
|
1295
|
-
currentBytes = statSync(
|
|
1786
|
+
currentBytes = statSync(join2(dir, fileName(day, index))).size;
|
|
1296
1787
|
} catch {
|
|
1297
1788
|
currentBytes = 0;
|
|
1298
1789
|
}
|
|
@@ -1334,7 +1825,7 @@ function fileSink(options = {}) {
|
|
|
1334
1825
|
currentBytes = 0;
|
|
1335
1826
|
prune();
|
|
1336
1827
|
}
|
|
1337
|
-
appendFileSync(
|
|
1828
|
+
appendFileSync(join2(dir, fileName(day, index)), line);
|
|
1338
1829
|
currentBytes += bytes;
|
|
1339
1830
|
} catch (error) {
|
|
1340
1831
|
disable(error);
|
|
@@ -1430,13 +1921,13 @@ function emit(level, message, attributes, extra = {}, options = {}) {
|
|
|
1430
1921
|
const exceptions = Object.fromEntries(
|
|
1431
1922
|
Object.entries(user).filter(([key]) => key.startsWith("exception."))
|
|
1432
1923
|
);
|
|
1433
|
-
const raw = {
|
|
1924
|
+
const raw = stackAttributesLast({
|
|
1434
1925
|
"autter.event.id": randomUUID(),
|
|
1435
1926
|
...extra,
|
|
1436
1927
|
...exceptions,
|
|
1437
1928
|
...operationAttributes(state),
|
|
1438
1929
|
...user
|
|
1439
|
-
};
|
|
1930
|
+
});
|
|
1440
1931
|
const enrichers = shared.config?.options?.enrich;
|
|
1441
1932
|
if (summary && state && enrichers?.length) {
|
|
1442
1933
|
const event2 = { level, message, attributes: raw };
|
|
@@ -1546,29 +2037,32 @@ function createRuntimeLogger(attributes = {}) {
|
|
|
1546
2037
|
debug: (message, extra) => emit("debug", message, merge(attrs, extra)),
|
|
1547
2038
|
info: (message, extra) => emit("info", message, merge(attrs, extra)),
|
|
1548
2039
|
warn: (message, extra) => emit("warning", message, merge(attrs, extra)),
|
|
1549
|
-
error: (error, extra) =>
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
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
|
+
}
|
|
1562
2056
|
};
|
|
1563
2057
|
}
|
|
1564
2058
|
var runtimeLogger = createRuntimeLogger();
|
|
1565
|
-
function captureExceptionAsLog(error, attributes = {}) {
|
|
2059
|
+
function captureExceptionAsLog(error, attributes = {}, level = "error") {
|
|
1566
2060
|
const isErr = isError(error);
|
|
1567
2061
|
const message = isErr ? error.message : String(error);
|
|
1568
2062
|
const stack = isErr && error.stack ? error.stack : new Error().stack?.split("\n").filter((line, i) => i === 0 || !/captureException/.test(line)).join("\n") ?? "";
|
|
1569
|
-
noteOperationError(error);
|
|
2063
|
+
if (level === "error") noteOperationError(error);
|
|
1570
2064
|
emit(
|
|
1571
|
-
|
|
2065
|
+
level,
|
|
1572
2066
|
message,
|
|
1573
2067
|
{
|
|
1574
2068
|
...attributes,
|
|
@@ -1653,13 +2147,15 @@ function summaryExtra(state, level, http = {}) {
|
|
|
1653
2147
|
"autter.operation.level": maxLevel(state.inline.level, level),
|
|
1654
2148
|
...state.ai ? { "autter.operation.ai": state.ai } : {},
|
|
1655
2149
|
...http,
|
|
1656
|
-
|
|
2150
|
+
// A summary is no occurrence: no cause stacks or debug ids.
|
|
2151
|
+
...err !== void 0 ? errorAttributes(err, { stacks: false }) : {},
|
|
1657
2152
|
...state.inline.logsTruncated ? { "autter.operation.logs_truncated": true } : {},
|
|
1658
2153
|
...state.inline.logs.length ? { "autter.operation.logs": state.inline.logs } : {}
|
|
1659
2154
|
};
|
|
1660
2155
|
}
|
|
1661
2156
|
function emitSummary(state, options = {}) {
|
|
1662
2157
|
if (state.sealed) return;
|
|
2158
|
+
settleDegraded(state);
|
|
1663
2159
|
const err = state.error;
|
|
1664
2160
|
const level = state.outcome === "failed" ? "error" : "info";
|
|
1665
2161
|
emit(
|
|
@@ -1677,6 +2173,10 @@ function emitSummary(state, options = {}) {
|
|
|
1677
2173
|
);
|
|
1678
2174
|
state.sealed = true;
|
|
1679
2175
|
}
|
|
2176
|
+
function settleDegraded(state) {
|
|
2177
|
+
if (state.degraded && state.outcome === "succeeded" && !state.explicitOutcome)
|
|
2178
|
+
state.outcome = "degraded";
|
|
2179
|
+
}
|
|
1680
2180
|
function reportExplicitFailure(state) {
|
|
1681
2181
|
if (state.outcome === "failed" && state.explicitOutcome && !state.errorThrown)
|
|
1682
2182
|
shared.config?.reportOutcome(state.name, state.message ?? "Operation failed", {
|
|
@@ -1716,6 +2216,7 @@ function startOperation(name, fn, attributes, options, internal = {}) {
|
|
|
1716
2216
|
shared.config?.captureThrown?.(error);
|
|
1717
2217
|
throw error;
|
|
1718
2218
|
} finally {
|
|
2219
|
+
settleDegraded(state);
|
|
1719
2220
|
trace.getActiveSpan()?.setAttributes({
|
|
1720
2221
|
...traceAttributes(state),
|
|
1721
2222
|
"autter.operation.outcome": state.outcome,
|
|
@@ -1787,6 +2288,30 @@ var runtimeContext = {
|
|
|
1787
2288
|
noteOperationError(error);
|
|
1788
2289
|
runtimeLogger.error(error, attributes);
|
|
1789
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
|
+
},
|
|
1790
2315
|
/** Current operation id. */
|
|
1791
2316
|
get id() {
|
|
1792
2317
|
return local.getStore()?.id;
|
|
@@ -1816,6 +2341,15 @@ var runtimeContext = {
|
|
|
1816
2341
|
});
|
|
1817
2342
|
}
|
|
1818
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
|
+
);
|
|
1819
2353
|
|
|
1820
2354
|
// src/breadcrumbs.ts
|
|
1821
2355
|
var INVALID_TRACE_ID = "00000000000000000000000000000000";
|
|
@@ -1878,10 +2412,10 @@ function recordLogBreadcrumb(level, message, attributes, state) {
|
|
|
1878
2412
|
}
|
|
1879
2413
|
}
|
|
1880
2414
|
function attributeBudget() {
|
|
1881
|
-
const
|
|
2415
|
+
const limit2 = Number(
|
|
1882
2416
|
process.env.OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT ?? process.env.OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT
|
|
1883
2417
|
);
|
|
1884
|
-
return Number.isFinite(
|
|
2418
|
+
return Number.isFinite(limit2) && limit2 > 0 ? Math.min(BREADCRUMBS_ATTRIBUTE_MAX_CHARS, limit2) : BREADCRUMBS_ATTRIBUTE_MAX_CHARS;
|
|
1885
2419
|
}
|
|
1886
2420
|
function rememberThrownBreadcrumbs(error, state) {
|
|
1887
2421
|
if (state.breadcrumbs && error !== null && (typeof error === "object" || typeof error === "function"))
|
|
@@ -1924,8 +2458,14 @@ export {
|
|
|
1924
2458
|
fingerprintValues,
|
|
1925
2459
|
fingerprintEventAttributes,
|
|
1926
2460
|
splitFingerprint,
|
|
2461
|
+
createErrorFilter,
|
|
1927
2462
|
addBreadcrumb,
|
|
1928
2463
|
breadcrumbEventAttributes,
|
|
2464
|
+
markCaptured,
|
|
2465
|
+
isCaptured,
|
|
2466
|
+
setHandledCapture,
|
|
2467
|
+
startLoggedErrorCapture,
|
|
2468
|
+
stopLoggedErrorCapture,
|
|
1929
2469
|
otlpSink,
|
|
1930
2470
|
consoleSink,
|
|
1931
2471
|
fileSink,
|