@aidenappleby/monitor-js 1.1.0 → 1.2.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 +109 -0
- package/dist/index.d.mts +93 -4
- package/dist/index.d.ts +93 -4
- package/dist/index.js +346 -39
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +341 -38
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -21,22 +21,91 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
21
21
|
var index_exports = {};
|
|
22
22
|
__export(index_exports, {
|
|
23
23
|
Monitor: () => Monitor,
|
|
24
|
-
attachAxiosMonitor: () => attachAxiosMonitor
|
|
24
|
+
attachAxiosMonitor: () => attachAxiosMonitor,
|
|
25
|
+
isValidCorrelationId: () => isValidCorrelationId,
|
|
26
|
+
newJobId: () => newJobId,
|
|
27
|
+
newRequestId: () => newRequestId,
|
|
28
|
+
newTraceId: () => newTraceId
|
|
25
29
|
});
|
|
26
30
|
module.exports = __toCommonJS(index_exports);
|
|
27
31
|
|
|
32
|
+
// src/ids.ts
|
|
33
|
+
var CORRELATION_ID = /^([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}|[0-9a-fA-F]{8,64})$/;
|
|
34
|
+
function isValidCorrelationId(id) {
|
|
35
|
+
return id === "" || CORRELATION_ID.test(id);
|
|
36
|
+
}
|
|
37
|
+
function randomBytes(n) {
|
|
38
|
+
const out = new Uint8Array(n);
|
|
39
|
+
const c = globalThis.crypto;
|
|
40
|
+
if (c && typeof c.getRandomValues === "function") {
|
|
41
|
+
c.getRandomValues(out);
|
|
42
|
+
return out;
|
|
43
|
+
}
|
|
44
|
+
for (let i = 0; i < n; i++) out[i] = Math.floor(Math.random() * 256);
|
|
45
|
+
return out;
|
|
46
|
+
}
|
|
47
|
+
function hex(bytes) {
|
|
48
|
+
return Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
|
|
49
|
+
}
|
|
50
|
+
function newRequestId() {
|
|
51
|
+
return hex(randomBytes(8));
|
|
52
|
+
}
|
|
53
|
+
function newJobId() {
|
|
54
|
+
return hex(randomBytes(8));
|
|
55
|
+
}
|
|
56
|
+
function newTraceId() {
|
|
57
|
+
const b = randomBytes(16);
|
|
58
|
+
b[6] = b[6] & 15 | 64;
|
|
59
|
+
b[8] = b[8] & 63 | 128;
|
|
60
|
+
const h = hex(b);
|
|
61
|
+
return `${h.slice(0, 8)}-${h.slice(8, 12)}-${h.slice(12, 16)}-${h.slice(16, 20)}-${h.slice(20)}`;
|
|
62
|
+
}
|
|
63
|
+
|
|
28
64
|
// src/client.ts
|
|
29
65
|
var DEFAULT_FLUSH_INTERVAL = 2e3;
|
|
30
66
|
var DEFAULT_BATCH_SIZE = 20;
|
|
31
67
|
var MAX_QUEUE_SIZE = 500;
|
|
32
|
-
var
|
|
68
|
+
var MAX_LINE_BYTES = 1e6;
|
|
69
|
+
var MAX_FIELD_CHARS = 4096;
|
|
70
|
+
var KEEPALIVE_MAX_BYTES = 6e4;
|
|
71
|
+
var BASE_BACKOFF_MS = 1e3;
|
|
72
|
+
var MAX_BACKOFF_MS = 6e4;
|
|
73
|
+
var GROUPING_KEYS = ["error", "error_message", "message", "path", "uri", "method", "reason", "status_code"];
|
|
74
|
+
function classify(status) {
|
|
75
|
+
if (status >= 200 && status < 400) return "delivered";
|
|
76
|
+
if (status === 408 || status === 429) return "retryable";
|
|
77
|
+
if (status === 401 || status === 403 || status === 404 || status === 405) return "misconfigured";
|
|
78
|
+
if (status >= 400 && status < 500) return "rejected";
|
|
79
|
+
return "retryable";
|
|
80
|
+
}
|
|
81
|
+
function bisectBudget(n) {
|
|
82
|
+
let depth = 0;
|
|
83
|
+
for (let x = n; x > 1; x = Math.ceil(x / 2)) depth++;
|
|
84
|
+
return 4 * depth + 4;
|
|
85
|
+
}
|
|
86
|
+
var encoder;
|
|
87
|
+
function byteLength(s) {
|
|
88
|
+
if (typeof TextEncoder === "undefined") return s.length * 3;
|
|
89
|
+
encoder ??= new TextEncoder();
|
|
90
|
+
return encoder.encode(s).length;
|
|
91
|
+
}
|
|
92
|
+
function normalizeLevel(level) {
|
|
93
|
+
const l = (level || "info").toLowerCase();
|
|
94
|
+
return l === "warning" ? "warn" : l;
|
|
95
|
+
}
|
|
96
|
+
var Monitor = class _Monitor {
|
|
33
97
|
config;
|
|
34
98
|
ignoreErrors = [];
|
|
99
|
+
onDrop;
|
|
35
100
|
queue = [];
|
|
36
101
|
timer = null;
|
|
37
102
|
userId = "";
|
|
38
|
-
jobId
|
|
103
|
+
jobId;
|
|
39
104
|
active = false;
|
|
105
|
+
backoffUntil = 0;
|
|
106
|
+
failures = 0;
|
|
107
|
+
warnedMisconfigured = false;
|
|
108
|
+
counters = { enqueued: 0, flushed: 0, dropped: 0, quarantined: 0 };
|
|
40
109
|
constructor(config) {
|
|
41
110
|
this.config = {
|
|
42
111
|
service: config.service,
|
|
@@ -48,6 +117,8 @@ var Monitor = class {
|
|
|
48
117
|
debug: config.debug ?? false
|
|
49
118
|
};
|
|
50
119
|
this.ignoreErrors = config.ignoreErrors ?? [];
|
|
120
|
+
this.onDrop = config.onDrop;
|
|
121
|
+
this.jobId = newJobId();
|
|
51
122
|
this.start();
|
|
52
123
|
if (config.captureErrors !== false) {
|
|
53
124
|
this.installErrorHandler();
|
|
@@ -64,29 +135,47 @@ var Monitor = class {
|
|
|
64
135
|
clearUser() {
|
|
65
136
|
this.userId = "";
|
|
66
137
|
}
|
|
67
|
-
/**
|
|
138
|
+
/**
|
|
139
|
+
* Set a persistent job ID (session-level identifier). It must be a UUID or
|
|
140
|
+
* 8-64 hex characters — see `isValidCorrelationId`; anything else is
|
|
141
|
+
* cleared from each event and kept in data.invalid_job_id.
|
|
142
|
+
*/
|
|
68
143
|
setJobId(jobId) {
|
|
69
144
|
this.jobId = jobId;
|
|
70
145
|
}
|
|
71
146
|
/** Emit an event at a specific level */
|
|
72
147
|
emit(name, level, opts) {
|
|
73
148
|
if (!this.active) return;
|
|
149
|
+
let data = opts?.data ?? {};
|
|
150
|
+
const repair = (field, value) => {
|
|
151
|
+
if (isValidCorrelationId(value)) return value;
|
|
152
|
+
data = { ...data, [`invalid_${field}`]: value.slice(0, 128) };
|
|
153
|
+
if (this.config.debug) {
|
|
154
|
+
console.warn(`[monitor] cleared invalid ${field} ${JSON.stringify(value)} (monitor-core accepts a UUID or 8-64 hex characters)`);
|
|
155
|
+
}
|
|
156
|
+
return "";
|
|
157
|
+
};
|
|
158
|
+
const jobId = repair("job_id", this.jobId);
|
|
159
|
+
const requestId = repair("request_id", opts?.requestId ?? "");
|
|
160
|
+
const traceId = repair("trace_id", opts?.traceId ?? "");
|
|
74
161
|
const event = {
|
|
75
162
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
76
163
|
service: this.config.service,
|
|
77
164
|
env: this.config.env,
|
|
78
|
-
job_id:
|
|
79
|
-
request_id:
|
|
80
|
-
trace_id:
|
|
165
|
+
job_id: jobId,
|
|
166
|
+
request_id: requestId,
|
|
167
|
+
trace_id: traceId,
|
|
81
168
|
user_id: opts?.userId ?? this.userId,
|
|
82
|
-
name,
|
|
83
|
-
level,
|
|
84
|
-
data
|
|
169
|
+
name: name || "event.unnamed",
|
|
170
|
+
level: normalizeLevel(level),
|
|
171
|
+
data
|
|
85
172
|
};
|
|
86
173
|
if (this.queue.length >= MAX_QUEUE_SIZE) {
|
|
87
174
|
this.queue.shift();
|
|
175
|
+
this.recordDrop(1);
|
|
88
176
|
}
|
|
89
177
|
this.queue.push(event);
|
|
178
|
+
this.counters.enqueued++;
|
|
90
179
|
if (this.config.debug) {
|
|
91
180
|
console.debug(`[monitor] ${level} ${name}`, opts?.data);
|
|
92
181
|
}
|
|
@@ -114,28 +203,17 @@ var Monitor = class {
|
|
|
114
203
|
fatal(name, opts) {
|
|
115
204
|
this.emit(name, "fatal", opts);
|
|
116
205
|
}
|
|
206
|
+
/**
|
|
207
|
+
* Lifetime counters. Surface them wherever loss would otherwise go
|
|
208
|
+
* unnoticed: the system that would report dropped telemetry is the one
|
|
209
|
+
* dropping it.
|
|
210
|
+
*/
|
|
211
|
+
stats() {
|
|
212
|
+
return { ...this.counters, queued: this.queue.length };
|
|
213
|
+
}
|
|
117
214
|
/** Flush all queued events to the ingest endpoint */
|
|
118
215
|
flush() {
|
|
119
|
-
|
|
120
|
-
const batch = this.queue.splice(0);
|
|
121
|
-
const payload = batch.map((e) => JSON.stringify(e)).join("\n");
|
|
122
|
-
if (typeof fetch === "undefined") return;
|
|
123
|
-
fetch(this.config.ingestUrl, {
|
|
124
|
-
method: "POST",
|
|
125
|
-
headers: {
|
|
126
|
-
"Content-Type": "application/x-ndjson",
|
|
127
|
-
"X-Api-Key": this.config.apiKey
|
|
128
|
-
},
|
|
129
|
-
body: payload,
|
|
130
|
-
keepalive: true
|
|
131
|
-
}).catch((err) => {
|
|
132
|
-
if (this.config.debug) {
|
|
133
|
-
console.warn("[monitor] flush failed:", err);
|
|
134
|
-
}
|
|
135
|
-
if (this.queue.length + batch.length <= MAX_QUEUE_SIZE) {
|
|
136
|
-
this.queue = batch.concat(this.queue);
|
|
137
|
-
}
|
|
138
|
-
});
|
|
216
|
+
this.flushQueue(false);
|
|
139
217
|
}
|
|
140
218
|
/** Stop the monitor and flush remaining events */
|
|
141
219
|
shutdown() {
|
|
@@ -143,14 +221,144 @@ var Monitor = class {
|
|
|
143
221
|
clearInterval(this.timer);
|
|
144
222
|
this.timer = null;
|
|
145
223
|
}
|
|
146
|
-
this.
|
|
224
|
+
this.flushQueue(true);
|
|
147
225
|
this.removeListeners();
|
|
148
226
|
this.active = false;
|
|
149
227
|
}
|
|
228
|
+
/**
|
|
229
|
+
* @param unloading the page (or process) is going away: ignore the backoff,
|
|
230
|
+
* since this is the last chance these events get.
|
|
231
|
+
*/
|
|
232
|
+
flushQueue(unloading) {
|
|
233
|
+
if (this.queue.length === 0) return;
|
|
234
|
+
if (typeof fetch === "undefined") return;
|
|
235
|
+
if (!unloading && Date.now() < this.backoffUntil) return;
|
|
236
|
+
const batch = this.queue.splice(0);
|
|
237
|
+
this.send(batch, { remaining: bisectBudget(batch.length) });
|
|
238
|
+
}
|
|
239
|
+
send(events, budget) {
|
|
240
|
+
const lines = [];
|
|
241
|
+
const sent = [];
|
|
242
|
+
for (const e of events) {
|
|
243
|
+
const line = this.serialize(e);
|
|
244
|
+
if (line === null) {
|
|
245
|
+
this.recordDrop(1);
|
|
246
|
+
continue;
|
|
247
|
+
}
|
|
248
|
+
lines.push(line);
|
|
249
|
+
sent.push(e);
|
|
250
|
+
}
|
|
251
|
+
if (lines.length === 0) return;
|
|
252
|
+
const body = lines.join("\n");
|
|
253
|
+
let request;
|
|
254
|
+
try {
|
|
255
|
+
request = fetch(this.config.ingestUrl, {
|
|
256
|
+
method: "POST",
|
|
257
|
+
headers: {
|
|
258
|
+
"Content-Type": "application/x-ndjson",
|
|
259
|
+
"X-Api-Key": this.config.apiKey
|
|
260
|
+
},
|
|
261
|
+
body,
|
|
262
|
+
keepalive: byteLength(body) <= KEEPALIVE_MAX_BYTES
|
|
263
|
+
});
|
|
264
|
+
} catch (err) {
|
|
265
|
+
request = Promise.reject(err);
|
|
266
|
+
}
|
|
267
|
+
request.then(
|
|
268
|
+
(res) => this.handleResponse(res, sent, budget),
|
|
269
|
+
(err) => {
|
|
270
|
+
if (this.config.debug) {
|
|
271
|
+
console.warn("[monitor] flush failed:", err);
|
|
272
|
+
}
|
|
273
|
+
this.retryLater(sent);
|
|
274
|
+
}
|
|
275
|
+
);
|
|
276
|
+
}
|
|
277
|
+
handleResponse(res, events, budget) {
|
|
278
|
+
const status = typeof res?.status === "number" ? res.status : 0;
|
|
279
|
+
const outcome = res?.ok ? "delivered" : classify(status);
|
|
280
|
+
switch (outcome) {
|
|
281
|
+
case "delivered":
|
|
282
|
+
this.counters.flushed += events.length;
|
|
283
|
+
this.failures = 0;
|
|
284
|
+
this.backoffUntil = 0;
|
|
285
|
+
return;
|
|
286
|
+
case "rejected":
|
|
287
|
+
if (events.length > 1 && budget.remaining > 0) {
|
|
288
|
+
budget.remaining--;
|
|
289
|
+
const mid = events.length >> 1;
|
|
290
|
+
this.send(events.slice(0, mid), budget);
|
|
291
|
+
this.send(events.slice(mid), budget);
|
|
292
|
+
return;
|
|
293
|
+
}
|
|
294
|
+
this.counters.quarantined += events.length;
|
|
295
|
+
this.recordDrop(events.length);
|
|
296
|
+
if (this.config.debug) {
|
|
297
|
+
console.warn(`[monitor] ingest rejected ${events.length} event(s) as malformed (status ${status}):`, events.map((e) => e.name));
|
|
298
|
+
}
|
|
299
|
+
return;
|
|
300
|
+
case "misconfigured":
|
|
301
|
+
this.recordDrop(events.length);
|
|
302
|
+
if (!this.warnedMisconfigured) {
|
|
303
|
+
this.warnedMisconfigured = true;
|
|
304
|
+
console.warn(`[monitor] ingest refused events with status ${status} \u2014 check ingestUrl and apiKey. Events are being dropped.`);
|
|
305
|
+
}
|
|
306
|
+
return;
|
|
307
|
+
default:
|
|
308
|
+
this.retryLater(events);
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
/** Put events back at the front of the queue and back off before retrying. */
|
|
312
|
+
retryLater(events) {
|
|
313
|
+
this.failures++;
|
|
314
|
+
const ceiling = Math.min(MAX_BACKOFF_MS, BASE_BACKOFF_MS * 2 ** Math.min(this.failures - 1, 16));
|
|
315
|
+
this.backoffUntil = Date.now() + 50 + Math.random() * ceiling;
|
|
316
|
+
const room = MAX_QUEUE_SIZE - this.queue.length;
|
|
317
|
+
const keep = room <= 0 ? [] : events.length > room ? events.slice(events.length - room) : events;
|
|
318
|
+
this.recordDrop(events.length - keep.length);
|
|
319
|
+
if (keep.length > 0) {
|
|
320
|
+
this.queue = keep.concat(this.queue);
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
/**
|
|
324
|
+
* One NDJSON line for e, or null if it cannot be serialized. Never throws:
|
|
325
|
+
* flush runs inside emit's auto-flush, and emit must never throw into the
|
|
326
|
+
* caller.
|
|
327
|
+
*/
|
|
328
|
+
serialize(e) {
|
|
329
|
+
try {
|
|
330
|
+
const line = JSON.stringify(e);
|
|
331
|
+
if (line.length <= MAX_LINE_BYTES / 3) return line;
|
|
332
|
+
const size = byteLength(line);
|
|
333
|
+
if (size <= MAX_LINE_BYTES) return line;
|
|
334
|
+
const kept = { truncated: true, original_size_bytes: size };
|
|
335
|
+
for (const k of GROUPING_KEYS) {
|
|
336
|
+
const v = e.data[k];
|
|
337
|
+
if (typeof v === "string") kept[k] = v.slice(0, MAX_FIELD_CHARS);
|
|
338
|
+
else if (typeof v === "number" || typeof v === "boolean") kept[k] = v;
|
|
339
|
+
}
|
|
340
|
+
const shrunk = JSON.stringify({ ...e, data: kept });
|
|
341
|
+
return byteLength(shrunk) <= MAX_LINE_BYTES ? shrunk : null;
|
|
342
|
+
} catch {
|
|
343
|
+
return null;
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
recordDrop(n) {
|
|
347
|
+
if (n <= 0) return;
|
|
348
|
+
this.counters.dropped += n;
|
|
349
|
+
if (this.onDrop) {
|
|
350
|
+
try {
|
|
351
|
+
this.onDrop(this.counters.dropped);
|
|
352
|
+
} catch {
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
}
|
|
150
356
|
start() {
|
|
151
357
|
if (this.active) return;
|
|
152
358
|
this.active = true;
|
|
153
|
-
|
|
359
|
+
const t = setInterval(() => this.flush(), this.config.flushInterval);
|
|
360
|
+
if (typeof t.unref === "function") t.unref();
|
|
361
|
+
this.timer = t;
|
|
154
362
|
if (typeof document !== "undefined") {
|
|
155
363
|
document.addEventListener("visibilitychange", this.handleVisibilityChange);
|
|
156
364
|
}
|
|
@@ -160,11 +368,11 @@ var Monitor = class {
|
|
|
160
368
|
}
|
|
161
369
|
handleVisibilityChange = () => {
|
|
162
370
|
if (document.visibilityState === "hidden") {
|
|
163
|
-
this.
|
|
371
|
+
this.flushQueue(true);
|
|
164
372
|
}
|
|
165
373
|
};
|
|
166
374
|
handlePageHide = () => {
|
|
167
|
-
this.
|
|
375
|
+
this.flushQueue(true);
|
|
168
376
|
};
|
|
169
377
|
shouldIgnoreError(message, stack) {
|
|
170
378
|
if (this.ignoreErrors.length === 0) return false;
|
|
@@ -181,6 +389,20 @@ var Monitor = class {
|
|
|
181
389
|
}
|
|
182
390
|
return false;
|
|
183
391
|
}
|
|
392
|
+
/**
|
|
393
|
+
* The route a browser error happened on.
|
|
394
|
+
*
|
|
395
|
+
* Deliberately `pathname` only — never the search string or hash. Query
|
|
396
|
+
* parameters routinely carry tokens, emails and other personal data, and this
|
|
397
|
+
* value is both stored on the event and folded into the server-side issue
|
|
398
|
+
* fingerprint, so anything included here is retained and grouped on.
|
|
399
|
+
*
|
|
400
|
+
* Returns undefined outside a browser so the Node handlers stay unaffected.
|
|
401
|
+
*/
|
|
402
|
+
currentPath() {
|
|
403
|
+
if (typeof window === "undefined" || !window.location) return void 0;
|
|
404
|
+
return window.location.pathname;
|
|
405
|
+
}
|
|
184
406
|
errorHandler = (event) => {
|
|
185
407
|
const stack = event.error?.stack;
|
|
186
408
|
if (this.shouldIgnoreError(event.message ?? "", stack)) return;
|
|
@@ -190,7 +412,8 @@ var Monitor = class {
|
|
|
190
412
|
filename: event.filename,
|
|
191
413
|
lineno: event.lineno,
|
|
192
414
|
colno: event.colno,
|
|
193
|
-
stack
|
|
415
|
+
stack,
|
|
416
|
+
path: this.currentPath()
|
|
194
417
|
}
|
|
195
418
|
});
|
|
196
419
|
};
|
|
@@ -202,18 +425,90 @@ var Monitor = class {
|
|
|
202
425
|
this.emit("client.error.unhandled_rejection", "error", {
|
|
203
426
|
data: {
|
|
204
427
|
message,
|
|
205
|
-
stack
|
|
428
|
+
stack,
|
|
429
|
+
path: this.currentPath()
|
|
206
430
|
}
|
|
207
431
|
});
|
|
208
432
|
};
|
|
433
|
+
// --- Node process handlers ---
|
|
434
|
+
// Adding an uncaughtException or unhandledRejection listener changes what Node
|
|
435
|
+
// does. With no listener, either one prints the error and exits with code 1;
|
|
436
|
+
// with any listener, Node assumes it was handled and keeps running — in
|
|
437
|
+
// whatever state the failure left it. So when this SDK is the only listener,
|
|
438
|
+
// it reports the error and then does what Node would have done. When the app
|
|
439
|
+
// has a listener of its own, the app has already decided; the SDK only reports.
|
|
440
|
+
/** Grace for the final batch to leave before a Node-style crash exits. */
|
|
441
|
+
static NODE_CRASH_GRACE_MS = 1500;
|
|
442
|
+
/** Rejections already reported, so the re-raise below is not reported twice. */
|
|
443
|
+
reportedRejections = /* @__PURE__ */ new WeakSet();
|
|
444
|
+
nodeExceptionHandler = (err) => {
|
|
445
|
+
const alreadyReported = typeof err === "object" && err !== null && this.reportedRejections.has(err);
|
|
446
|
+
const e = err;
|
|
447
|
+
const message = e?.message ?? String(err);
|
|
448
|
+
const stack = e?.stack;
|
|
449
|
+
if (!alreadyReported && !this.shouldIgnoreError(message, stack)) {
|
|
450
|
+
this.emit("client.error.uncaught", "error", {
|
|
451
|
+
data: {
|
|
452
|
+
message,
|
|
453
|
+
stack
|
|
454
|
+
}
|
|
455
|
+
});
|
|
456
|
+
}
|
|
457
|
+
if (this.isSoleListener("uncaughtException")) {
|
|
458
|
+
this.crashLikeNode(err);
|
|
459
|
+
}
|
|
460
|
+
};
|
|
461
|
+
nodeRejectionHandler = (reason) => {
|
|
462
|
+
const r = reason;
|
|
463
|
+
const message = r?.message ?? String(reason);
|
|
464
|
+
const stack = r?.stack;
|
|
465
|
+
if (!this.shouldIgnoreError(message, stack)) {
|
|
466
|
+
this.emit("client.error.unhandled_rejection", "error", {
|
|
467
|
+
data: {
|
|
468
|
+
message,
|
|
469
|
+
stack
|
|
470
|
+
}
|
|
471
|
+
});
|
|
472
|
+
}
|
|
473
|
+
if (this.isSoleListener("unhandledRejection")) {
|
|
474
|
+
if (typeof reason === "object" && reason !== null) {
|
|
475
|
+
this.reportedRejections.add(reason);
|
|
476
|
+
}
|
|
477
|
+
this.reraise(reason);
|
|
478
|
+
}
|
|
479
|
+
};
|
|
480
|
+
/** Hand an unhandled rejection back to Node as an uncaught exception. */
|
|
481
|
+
reraise(reason) {
|
|
482
|
+
process?.nextTick?.(() => {
|
|
483
|
+
throw reason;
|
|
484
|
+
});
|
|
485
|
+
}
|
|
486
|
+
/** True when this instance's own handler is the only listener for the event. */
|
|
487
|
+
isSoleListener(event) {
|
|
488
|
+
const count = typeof process === "undefined" ? void 0 : process.listenerCount;
|
|
489
|
+
if (typeof count !== "function") {
|
|
490
|
+
return false;
|
|
491
|
+
}
|
|
492
|
+
return count.call(process, event) <= 1;
|
|
493
|
+
}
|
|
494
|
+
/** Print the error as Node would, give the batch a moment to leave, exit 1. */
|
|
495
|
+
crashLikeNode(err) {
|
|
496
|
+
console.error(err);
|
|
497
|
+
this.flush();
|
|
498
|
+
setTimeout(() => process?.exit?.(1), _Monitor.NODE_CRASH_GRACE_MS);
|
|
499
|
+
}
|
|
209
500
|
installErrorHandler() {
|
|
210
501
|
if (typeof window !== "undefined") {
|
|
211
502
|
window.addEventListener("error", this.errorHandler);
|
|
503
|
+
} else if (typeof process !== "undefined" && typeof process.on === "function") {
|
|
504
|
+
process.on("uncaughtException", this.nodeExceptionHandler);
|
|
212
505
|
}
|
|
213
506
|
}
|
|
214
507
|
installRejectionHandler() {
|
|
215
508
|
if (typeof window !== "undefined") {
|
|
216
509
|
window.addEventListener("unhandledrejection", this.rejectionHandler);
|
|
510
|
+
} else if (typeof process !== "undefined" && typeof process.on === "function") {
|
|
511
|
+
process.on("unhandledRejection", this.nodeRejectionHandler);
|
|
217
512
|
}
|
|
218
513
|
}
|
|
219
514
|
removeListeners() {
|
|
@@ -225,10 +520,18 @@ var Monitor = class {
|
|
|
225
520
|
if (typeof document !== "undefined") {
|
|
226
521
|
document.removeEventListener("visibilitychange", this.handleVisibilityChange);
|
|
227
522
|
}
|
|
523
|
+
if (typeof process !== "undefined" && typeof process.removeListener === "function") {
|
|
524
|
+
process.removeListener("uncaughtException", this.nodeExceptionHandler);
|
|
525
|
+
process.removeListener("unhandledRejection", this.nodeRejectionHandler);
|
|
526
|
+
}
|
|
228
527
|
}
|
|
229
528
|
};
|
|
230
529
|
|
|
231
530
|
// src/axios.ts
|
|
531
|
+
function stripQuery(url) {
|
|
532
|
+
const i = url.search(/[?#]/);
|
|
533
|
+
return i === -1 ? url : url.slice(0, i);
|
|
534
|
+
}
|
|
232
535
|
function attachAxiosMonitor(axiosInstance, monitor, opts) {
|
|
233
536
|
const minStatus = opts?.minStatus ?? 400;
|
|
234
537
|
const reportSuccess = opts?.reportSuccess ?? false;
|
|
@@ -239,7 +542,7 @@ function attachAxiosMonitor(axiosInstance, monitor, opts) {
|
|
|
239
542
|
});
|
|
240
543
|
axiosInstance.interceptors.response.use(
|
|
241
544
|
(response) => {
|
|
242
|
-
const url = response.config?.url ?? "";
|
|
545
|
+
const url = stripQuery(response.config?.url ?? "");
|
|
243
546
|
if (ignorePaths.some((p) => url.includes(p))) return response;
|
|
244
547
|
const statusCode = response.status ?? 0;
|
|
245
548
|
const requestId = response.headers?.["x-request-id"] ?? "";
|
|
@@ -274,7 +577,7 @@ function attachAxiosMonitor(axiosInstance, monitor, opts) {
|
|
|
274
577
|
return response;
|
|
275
578
|
},
|
|
276
579
|
(error) => {
|
|
277
|
-
const url = error.config?.url ?? "";
|
|
580
|
+
const url = stripQuery(error.config?.url ?? "");
|
|
278
581
|
if (ignorePaths.some((p) => url.includes(p))) {
|
|
279
582
|
return Promise.reject(error);
|
|
280
583
|
}
|
|
@@ -315,6 +618,10 @@ function attachAxiosMonitor(axiosInstance, monitor, opts) {
|
|
|
315
618
|
// Annotate the CommonJS export names for ESM import in node:
|
|
316
619
|
0 && (module.exports = {
|
|
317
620
|
Monitor,
|
|
318
|
-
attachAxiosMonitor
|
|
621
|
+
attachAxiosMonitor,
|
|
622
|
+
isValidCorrelationId,
|
|
623
|
+
newJobId,
|
|
624
|
+
newRequestId,
|
|
625
|
+
newTraceId
|
|
319
626
|
});
|
|
320
627
|
//# sourceMappingURL=index.js.map
|