apitally 0.11.4 → 0.12.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 +27 -14
- package/dist/common/client.cjs +130 -110
- package/dist/common/client.cjs.map +1 -1
- package/dist/common/client.d.cts +2 -0
- package/dist/common/client.d.ts +2 -0
- package/dist/common/client.js +125 -105
- package/dist/common/client.js.map +1 -1
- package/dist/common/consumerRegistry.cjs.map +1 -1
- package/dist/common/consumerRegistry.js.map +1 -1
- package/dist/common/logging.cjs.map +1 -1
- package/dist/common/logging.js.map +1 -1
- package/dist/common/packageVersions.cjs.map +1 -1
- package/dist/common/packageVersions.js.map +1 -1
- package/dist/common/paramValidation.cjs.map +1 -1
- package/dist/common/paramValidation.js.map +1 -1
- package/dist/common/requestCounter.cjs.map +1 -1
- package/dist/common/requestCounter.js.map +1 -1
- package/dist/common/requestLogger.cjs +63 -7
- package/dist/common/requestLogger.cjs.map +1 -1
- package/dist/common/requestLogger.d.cts +3 -1
- package/dist/common/requestLogger.d.ts +3 -1
- package/dist/common/requestLogger.js +63 -7
- package/dist/common/requestLogger.js.map +1 -1
- package/dist/common/sentry.cjs +55 -0
- package/dist/common/sentry.cjs.map +1 -0
- package/dist/common/sentry.d.cts +6 -0
- package/dist/common/sentry.d.ts +6 -0
- package/dist/common/sentry.js +22 -0
- package/dist/common/sentry.js.map +1 -0
- package/dist/common/serverErrorCounter.cjs +58 -45
- package/dist/common/serverErrorCounter.cjs.map +1 -1
- package/dist/common/serverErrorCounter.d.cts +3 -6
- package/dist/common/serverErrorCounter.d.ts +3 -6
- package/dist/common/serverErrorCounter.js +53 -45
- package/dist/common/serverErrorCounter.js.map +1 -1
- package/dist/common/tempGzipFile.cjs +2 -2
- package/dist/common/tempGzipFile.cjs.map +1 -1
- package/dist/common/tempGzipFile.js +2 -2
- package/dist/common/tempGzipFile.js.map +1 -1
- package/dist/common/types.cjs.map +1 -1
- package/dist/common/validationErrorCounter.cjs.map +1 -1
- package/dist/common/validationErrorCounter.js.map +1 -1
- package/dist/express/index.cjs +139 -112
- package/dist/express/index.cjs.map +1 -1
- package/dist/express/index.js +134 -107
- package/dist/express/index.js.map +1 -1
- package/dist/express/middleware.cjs +139 -112
- package/dist/express/middleware.cjs.map +1 -1
- package/dist/express/middleware.js +134 -107
- package/dist/express/middleware.js.map +1 -1
- package/dist/express/utils.cjs.map +1 -1
- package/dist/express/utils.js.map +1 -1
- package/dist/fastify/index.cjs +136 -113
- package/dist/fastify/index.cjs.map +1 -1
- package/dist/fastify/index.js +131 -108
- package/dist/fastify/index.js.map +1 -1
- package/dist/fastify/plugin.cjs +136 -113
- package/dist/fastify/plugin.cjs.map +1 -1
- package/dist/fastify/plugin.js +131 -108
- package/dist/fastify/plugin.js.map +1 -1
- package/dist/hono/index.cjs +137 -112
- package/dist/hono/index.cjs.map +1 -1
- package/dist/hono/index.js +132 -107
- package/dist/hono/index.js.map +1 -1
- package/dist/hono/middleware.cjs +137 -112
- package/dist/hono/middleware.cjs.map +1 -1
- package/dist/hono/middleware.js +132 -107
- package/dist/hono/middleware.js.map +1 -1
- package/dist/koa/index.cjs +137 -111
- package/dist/koa/index.cjs.map +1 -1
- package/dist/koa/index.js +132 -106
- package/dist/koa/index.js.map +1 -1
- package/dist/koa/middleware.cjs +137 -111
- package/dist/koa/middleware.cjs.map +1 -1
- package/dist/koa/middleware.js +132 -106
- package/dist/koa/middleware.js.map +1 -1
- package/dist/nestjs/index.cjs +139 -112
- package/dist/nestjs/index.cjs.map +1 -1
- package/dist/nestjs/index.js +134 -107
- package/dist/nestjs/index.js.map +1 -1
- package/package.json +1 -1
package/dist/nestjs/index.js
CHANGED
|
@@ -194,6 +194,108 @@ import { unlinkSync as unlinkSync2, writeFileSync } from "fs";
|
|
|
194
194
|
import { tmpdir as tmpdir2 } from "os";
|
|
195
195
|
import { join as join2 } from "path";
|
|
196
196
|
|
|
197
|
+
// src/common/sentry.ts
|
|
198
|
+
var sentry;
|
|
199
|
+
(async () => {
|
|
200
|
+
try {
|
|
201
|
+
sentry = await import("@sentry/node");
|
|
202
|
+
} catch (e) {
|
|
203
|
+
}
|
|
204
|
+
})();
|
|
205
|
+
function getSentryEventId() {
|
|
206
|
+
if (sentry && sentry.lastEventId) {
|
|
207
|
+
return sentry.lastEventId();
|
|
208
|
+
}
|
|
209
|
+
return void 0;
|
|
210
|
+
}
|
|
211
|
+
__name(getSentryEventId, "getSentryEventId");
|
|
212
|
+
|
|
213
|
+
// src/common/serverErrorCounter.ts
|
|
214
|
+
import { createHash } from "crypto";
|
|
215
|
+
var MAX_MSG_LENGTH = 2048;
|
|
216
|
+
var MAX_STACKTRACE_LENGTH = 65536;
|
|
217
|
+
var _ServerErrorCounter = class _ServerErrorCounter {
|
|
218
|
+
errorCounts;
|
|
219
|
+
errorDetails;
|
|
220
|
+
sentryEventIds;
|
|
221
|
+
constructor() {
|
|
222
|
+
this.errorCounts = /* @__PURE__ */ new Map();
|
|
223
|
+
this.errorDetails = /* @__PURE__ */ new Map();
|
|
224
|
+
this.sentryEventIds = /* @__PURE__ */ new Map();
|
|
225
|
+
}
|
|
226
|
+
addServerError(serverError) {
|
|
227
|
+
const key = this.getKey(serverError);
|
|
228
|
+
if (!this.errorDetails.has(key)) {
|
|
229
|
+
this.errorDetails.set(key, serverError);
|
|
230
|
+
}
|
|
231
|
+
this.errorCounts.set(key, (this.errorCounts.get(key) || 0) + 1);
|
|
232
|
+
const sentryEventId = getSentryEventId();
|
|
233
|
+
if (sentryEventId) {
|
|
234
|
+
this.sentryEventIds.set(key, sentryEventId);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
getAndResetServerErrors() {
|
|
238
|
+
const data = [];
|
|
239
|
+
this.errorCounts.forEach((count, key) => {
|
|
240
|
+
const serverError = this.errorDetails.get(key);
|
|
241
|
+
if (serverError) {
|
|
242
|
+
data.push({
|
|
243
|
+
consumer: serverError.consumer || null,
|
|
244
|
+
method: serverError.method,
|
|
245
|
+
path: serverError.path,
|
|
246
|
+
type: serverError.type,
|
|
247
|
+
msg: truncateExceptionMessage(serverError.msg),
|
|
248
|
+
traceback: truncateExceptionStackTrace(serverError.traceback),
|
|
249
|
+
sentry_event_id: this.sentryEventIds.get(key) || null,
|
|
250
|
+
error_count: count
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
});
|
|
254
|
+
this.errorCounts.clear();
|
|
255
|
+
this.errorDetails.clear();
|
|
256
|
+
return data;
|
|
257
|
+
}
|
|
258
|
+
getKey(serverError) {
|
|
259
|
+
const hashInput = [
|
|
260
|
+
serverError.consumer || "",
|
|
261
|
+
serverError.method.toUpperCase(),
|
|
262
|
+
serverError.path,
|
|
263
|
+
serverError.type,
|
|
264
|
+
serverError.msg.trim(),
|
|
265
|
+
serverError.traceback.trim()
|
|
266
|
+
].join("|");
|
|
267
|
+
return createHash("md5").update(hashInput).digest("hex");
|
|
268
|
+
}
|
|
269
|
+
};
|
|
270
|
+
__name(_ServerErrorCounter, "ServerErrorCounter");
|
|
271
|
+
var ServerErrorCounter = _ServerErrorCounter;
|
|
272
|
+
function truncateExceptionMessage(msg) {
|
|
273
|
+
if (msg.length <= MAX_MSG_LENGTH) {
|
|
274
|
+
return msg;
|
|
275
|
+
}
|
|
276
|
+
const suffix = "... (truncated)";
|
|
277
|
+
const cutoff = MAX_MSG_LENGTH - suffix.length;
|
|
278
|
+
return msg.substring(0, cutoff) + suffix;
|
|
279
|
+
}
|
|
280
|
+
__name(truncateExceptionMessage, "truncateExceptionMessage");
|
|
281
|
+
function truncateExceptionStackTrace(stack) {
|
|
282
|
+
const suffix = "... (truncated) ...";
|
|
283
|
+
const cutoff = MAX_STACKTRACE_LENGTH - suffix.length;
|
|
284
|
+
const lines = stack.trim().split("\n");
|
|
285
|
+
const truncatedLines = [];
|
|
286
|
+
let length = 0;
|
|
287
|
+
for (const line of lines) {
|
|
288
|
+
if (length + line.length + 1 > cutoff) {
|
|
289
|
+
truncatedLines.push(suffix);
|
|
290
|
+
break;
|
|
291
|
+
}
|
|
292
|
+
truncatedLines.push(line);
|
|
293
|
+
length += line.length + 1;
|
|
294
|
+
}
|
|
295
|
+
return truncatedLines.join("\n");
|
|
296
|
+
}
|
|
297
|
+
__name(truncateExceptionStackTrace, "truncateExceptionStackTrace");
|
|
298
|
+
|
|
197
299
|
// src/common/tempGzipFile.ts
|
|
198
300
|
import { Buffer as Buffer2 } from "buffer";
|
|
199
301
|
import { randomUUID } from "crypto";
|
|
@@ -216,9 +318,9 @@ var _TempGzipFile = class _TempGzipFile {
|
|
|
216
318
|
this.writeStream.once("ready", resolve);
|
|
217
319
|
this.writeStream.once("error", reject);
|
|
218
320
|
});
|
|
219
|
-
this.closedPromise = new Promise((resolve) => {
|
|
321
|
+
this.closedPromise = new Promise((resolve, reject) => {
|
|
220
322
|
this.writeStream.once("close", resolve);
|
|
221
|
-
this.writeStream.once("error",
|
|
323
|
+
this.writeStream.once("error", reject);
|
|
222
324
|
});
|
|
223
325
|
this.gzip = createGzip();
|
|
224
326
|
this.gzip.pipe(this.writeStream);
|
|
@@ -316,6 +418,7 @@ var DEFAULT_CONFIG = {
|
|
|
316
418
|
logRequestBody: false,
|
|
317
419
|
logResponseHeaders: true,
|
|
318
420
|
logResponseBody: false,
|
|
421
|
+
logException: true,
|
|
319
422
|
maskQueryParams: [],
|
|
320
423
|
maskHeaders: [],
|
|
321
424
|
excludePaths: []
|
|
@@ -368,7 +471,10 @@ var _RequestLogger = class _RequestLogger {
|
|
|
368
471
|
hasSupportedContentType(headers) {
|
|
369
472
|
var _a3;
|
|
370
473
|
const contentType = (_a3 = headers.find(([k]) => k.toLowerCase() === "content-type")) == null ? void 0 : _a3[1];
|
|
371
|
-
return
|
|
474
|
+
return this.isSupportedContentType(contentType);
|
|
475
|
+
}
|
|
476
|
+
isSupportedContentType(contentType) {
|
|
477
|
+
return typeof contentType === "string" && ALLOWED_CONTENT_TYPES.some((t) => contentType.startsWith(t));
|
|
372
478
|
}
|
|
373
479
|
maskQueryParams(search) {
|
|
374
480
|
const params = new URLSearchParams(search);
|
|
@@ -385,7 +491,7 @@ var _RequestLogger = class _RequestLogger {
|
|
|
385
491
|
this.shouldMaskHeader(k) ? MASKED : v
|
|
386
492
|
]);
|
|
387
493
|
}
|
|
388
|
-
logRequest(request, response) {
|
|
494
|
+
logRequest(request, response, error) {
|
|
389
495
|
var _a3, _b, _c;
|
|
390
496
|
if (!this.enabled || this.suspendUntil !== null) return;
|
|
391
497
|
const url = new URL(request.url);
|
|
@@ -396,8 +502,6 @@ var _RequestLogger = class _RequestLogger {
|
|
|
396
502
|
}
|
|
397
503
|
url.search = this.config.logQueryParams ? this.maskQueryParams(url.search) : "";
|
|
398
504
|
request.url = url.toString();
|
|
399
|
-
request.headers = this.config.logRequestHeaders ? this.maskHeaders(request.headers) : [];
|
|
400
|
-
response.headers = this.config.logResponseHeaders ? this.maskHeaders(response.headers) : [];
|
|
401
505
|
if (!this.config.logRequestBody || !this.hasSupportedContentType(request.headers)) {
|
|
402
506
|
request.body = void 0;
|
|
403
507
|
} else if (request.body) {
|
|
@@ -430,10 +534,18 @@ var _RequestLogger = class _RequestLogger {
|
|
|
430
534
|
}
|
|
431
535
|
}
|
|
432
536
|
}
|
|
537
|
+
request.headers = this.config.logRequestHeaders ? this.maskHeaders(request.headers) : [];
|
|
538
|
+
response.headers = this.config.logResponseHeaders ? this.maskHeaders(response.headers) : [];
|
|
433
539
|
const item = {
|
|
434
540
|
uuid: randomUUID2(),
|
|
435
541
|
request: skipEmptyValues(request),
|
|
436
|
-
response: skipEmptyValues(response)
|
|
542
|
+
response: skipEmptyValues(response),
|
|
543
|
+
exception: error && this.config.logException ? {
|
|
544
|
+
type: error.name,
|
|
545
|
+
message: truncateExceptionMessage(error.message),
|
|
546
|
+
stacktrace: truncateExceptionStackTrace(error.stack || ""),
|
|
547
|
+
sentryEventId: getSentryEventId()
|
|
548
|
+
} : null
|
|
437
549
|
};
|
|
438
550
|
[
|
|
439
551
|
item.request.body,
|
|
@@ -595,104 +707,6 @@ function checkWritableFs() {
|
|
|
595
707
|
}
|
|
596
708
|
__name(checkWritableFs, "checkWritableFs");
|
|
597
709
|
|
|
598
|
-
// src/common/serverErrorCounter.ts
|
|
599
|
-
import { createHash } from "crypto";
|
|
600
|
-
var MAX_MSG_LENGTH = 2048;
|
|
601
|
-
var MAX_STACKTRACE_LENGTH = 65536;
|
|
602
|
-
var _ServerErrorCounter = class _ServerErrorCounter {
|
|
603
|
-
errorCounts;
|
|
604
|
-
errorDetails;
|
|
605
|
-
sentryEventIds;
|
|
606
|
-
sentry;
|
|
607
|
-
constructor() {
|
|
608
|
-
this.errorCounts = /* @__PURE__ */ new Map();
|
|
609
|
-
this.errorDetails = /* @__PURE__ */ new Map();
|
|
610
|
-
this.sentryEventIds = /* @__PURE__ */ new Map();
|
|
611
|
-
this.tryImportSentry();
|
|
612
|
-
}
|
|
613
|
-
addServerError(serverError) {
|
|
614
|
-
const key = this.getKey(serverError);
|
|
615
|
-
if (!this.errorDetails.has(key)) {
|
|
616
|
-
this.errorDetails.set(key, serverError);
|
|
617
|
-
}
|
|
618
|
-
this.errorCounts.set(key, (this.errorCounts.get(key) || 0) + 1);
|
|
619
|
-
this.captureSentryEventId(key);
|
|
620
|
-
}
|
|
621
|
-
getAndResetServerErrors() {
|
|
622
|
-
const data = [];
|
|
623
|
-
this.errorCounts.forEach((count, key) => {
|
|
624
|
-
const serverError = this.errorDetails.get(key);
|
|
625
|
-
if (serverError) {
|
|
626
|
-
data.push({
|
|
627
|
-
consumer: serverError.consumer || null,
|
|
628
|
-
method: serverError.method,
|
|
629
|
-
path: serverError.path,
|
|
630
|
-
type: serverError.type,
|
|
631
|
-
msg: this.getTruncatedMessage(serverError.msg),
|
|
632
|
-
traceback: this.getTruncatedStack(serverError.traceback),
|
|
633
|
-
sentry_event_id: this.sentryEventIds.get(key) || null,
|
|
634
|
-
error_count: count
|
|
635
|
-
});
|
|
636
|
-
}
|
|
637
|
-
});
|
|
638
|
-
this.errorCounts.clear();
|
|
639
|
-
this.errorDetails.clear();
|
|
640
|
-
return data;
|
|
641
|
-
}
|
|
642
|
-
getKey(serverError) {
|
|
643
|
-
const hashInput = [
|
|
644
|
-
serverError.consumer || "",
|
|
645
|
-
serverError.method.toUpperCase(),
|
|
646
|
-
serverError.path,
|
|
647
|
-
serverError.type,
|
|
648
|
-
serverError.msg.trim(),
|
|
649
|
-
serverError.traceback.trim()
|
|
650
|
-
].join("|");
|
|
651
|
-
return createHash("md5").update(hashInput).digest("hex");
|
|
652
|
-
}
|
|
653
|
-
getTruncatedMessage(msg) {
|
|
654
|
-
msg = msg.trim();
|
|
655
|
-
if (msg.length <= MAX_MSG_LENGTH) {
|
|
656
|
-
return msg;
|
|
657
|
-
}
|
|
658
|
-
const suffix = "... (truncated)";
|
|
659
|
-
const cutoff = MAX_MSG_LENGTH - suffix.length;
|
|
660
|
-
return msg.substring(0, cutoff) + suffix;
|
|
661
|
-
}
|
|
662
|
-
getTruncatedStack(stack) {
|
|
663
|
-
const suffix = "... (truncated) ...";
|
|
664
|
-
const cutoff = MAX_STACKTRACE_LENGTH - suffix.length;
|
|
665
|
-
const lines = stack.trim().split("\n");
|
|
666
|
-
const truncatedLines = [];
|
|
667
|
-
let length = 0;
|
|
668
|
-
for (const line of lines) {
|
|
669
|
-
if (length + line.length + 1 > cutoff) {
|
|
670
|
-
truncatedLines.push(suffix);
|
|
671
|
-
break;
|
|
672
|
-
}
|
|
673
|
-
truncatedLines.push(line);
|
|
674
|
-
length += line.length + 1;
|
|
675
|
-
}
|
|
676
|
-
return truncatedLines.join("\n");
|
|
677
|
-
}
|
|
678
|
-
captureSentryEventId(serverErrorKey) {
|
|
679
|
-
if (this.sentry && this.sentry.lastEventId) {
|
|
680
|
-
const eventId = this.sentry.lastEventId();
|
|
681
|
-
if (eventId) {
|
|
682
|
-
this.sentryEventIds.set(serverErrorKey, eventId);
|
|
683
|
-
}
|
|
684
|
-
}
|
|
685
|
-
}
|
|
686
|
-
async tryImportSentry() {
|
|
687
|
-
try {
|
|
688
|
-
this.sentry = await import("@sentry/node");
|
|
689
|
-
} catch (e) {
|
|
690
|
-
}
|
|
691
|
-
}
|
|
692
|
-
};
|
|
693
|
-
__name(_ServerErrorCounter, "ServerErrorCounter");
|
|
694
|
-
var ServerErrorCounter = _ServerErrorCounter;
|
|
695
|
-
|
|
696
710
|
// src/common/validationErrorCounter.ts
|
|
697
711
|
import { createHash as createHash2 } from "crypto";
|
|
698
712
|
var _ValidationErrorCounter = class _ValidationErrorCounter {
|
|
@@ -766,6 +780,7 @@ var _ApitallyClient = class _ApitallyClient {
|
|
|
766
780
|
syncIntervalId;
|
|
767
781
|
startupData;
|
|
768
782
|
startupDataSent = false;
|
|
783
|
+
enabled = true;
|
|
769
784
|
requestCounter;
|
|
770
785
|
requestLogger;
|
|
771
786
|
validationErrorCounter;
|
|
@@ -802,12 +817,16 @@ var _ApitallyClient = class _ApitallyClient {
|
|
|
802
817
|
}
|
|
803
818
|
return _ApitallyClient.instance;
|
|
804
819
|
}
|
|
820
|
+
isEnabled() {
|
|
821
|
+
return this.enabled;
|
|
822
|
+
}
|
|
805
823
|
static async shutdown() {
|
|
806
824
|
if (_ApitallyClient.instance) {
|
|
807
825
|
await _ApitallyClient.instance.handleShutdown();
|
|
808
826
|
}
|
|
809
827
|
}
|
|
810
828
|
async handleShutdown() {
|
|
829
|
+
this.enabled = false;
|
|
811
830
|
this.stopSync();
|
|
812
831
|
await this.sendSyncData();
|
|
813
832
|
await this.sendLogData();
|
|
@@ -991,6 +1010,7 @@ var _ApitallyClient = class _ApitallyClient {
|
|
|
991
1010
|
if (error instanceof HTTPError) {
|
|
992
1011
|
if (error.response.status === 404) {
|
|
993
1012
|
this.logger.error(`Invalid Apitally client ID: '${this.clientId}'`);
|
|
1013
|
+
this.enabled = false;
|
|
994
1014
|
this.stopSync();
|
|
995
1015
|
return true;
|
|
996
1016
|
}
|
|
@@ -1165,6 +1185,10 @@ var useApitally = /* @__PURE__ */ __name((app, config) => {
|
|
|
1165
1185
|
var getMiddleware = /* @__PURE__ */ __name((app, client) => {
|
|
1166
1186
|
let errorHandlerConfigured = false;
|
|
1167
1187
|
return (req, res, next) => {
|
|
1188
|
+
if (!client.isEnabled()) {
|
|
1189
|
+
next();
|
|
1190
|
+
return;
|
|
1191
|
+
}
|
|
1168
1192
|
if (!errorHandlerConfigured) {
|
|
1169
1193
|
app.use((err, req2, res2, next2) => {
|
|
1170
1194
|
res2.locals.serverError = err;
|
|
@@ -1176,7 +1200,10 @@ var getMiddleware = /* @__PURE__ */ __name((app, client) => {
|
|
|
1176
1200
|
const startTime = performance.now();
|
|
1177
1201
|
const originalSend = res.send;
|
|
1178
1202
|
res.send = (body) => {
|
|
1179
|
-
|
|
1203
|
+
const contentType = res.get("content-type");
|
|
1204
|
+
if (client.requestLogger.isSupportedContentType(contentType)) {
|
|
1205
|
+
res.locals.body = body;
|
|
1206
|
+
}
|
|
1180
1207
|
return originalSend.call(res, body);
|
|
1181
1208
|
};
|
|
1182
1209
|
res.on("finish", () => {
|
|
@@ -1250,7 +1277,7 @@ var getMiddleware = /* @__PURE__ */ __name((app, client) => {
|
|
|
1250
1277
|
headers: convertHeaders(res.getHeaders()),
|
|
1251
1278
|
size: Number(res.get("content-length")),
|
|
1252
1279
|
body: convertBody(res.locals.body, res.get("content-type"))
|
|
1253
|
-
});
|
|
1280
|
+
}, res.locals.serverError);
|
|
1254
1281
|
}
|
|
1255
1282
|
} catch (error) {
|
|
1256
1283
|
client.logger.error("Error while logging request in Apitally middleware.", {
|