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/koa/middleware.js
CHANGED
|
@@ -187,6 +187,108 @@ import { unlinkSync as unlinkSync2, writeFileSync } from "fs";
|
|
|
187
187
|
import { tmpdir as tmpdir2 } from "os";
|
|
188
188
|
import { join as join2 } from "path";
|
|
189
189
|
|
|
190
|
+
// src/common/sentry.ts
|
|
191
|
+
var sentry;
|
|
192
|
+
(async () => {
|
|
193
|
+
try {
|
|
194
|
+
sentry = await import("@sentry/node");
|
|
195
|
+
} catch (e) {
|
|
196
|
+
}
|
|
197
|
+
})();
|
|
198
|
+
function getSentryEventId() {
|
|
199
|
+
if (sentry && sentry.lastEventId) {
|
|
200
|
+
return sentry.lastEventId();
|
|
201
|
+
}
|
|
202
|
+
return void 0;
|
|
203
|
+
}
|
|
204
|
+
__name(getSentryEventId, "getSentryEventId");
|
|
205
|
+
|
|
206
|
+
// src/common/serverErrorCounter.ts
|
|
207
|
+
import { createHash } from "crypto";
|
|
208
|
+
var MAX_MSG_LENGTH = 2048;
|
|
209
|
+
var MAX_STACKTRACE_LENGTH = 65536;
|
|
210
|
+
var _ServerErrorCounter = class _ServerErrorCounter {
|
|
211
|
+
errorCounts;
|
|
212
|
+
errorDetails;
|
|
213
|
+
sentryEventIds;
|
|
214
|
+
constructor() {
|
|
215
|
+
this.errorCounts = /* @__PURE__ */ new Map();
|
|
216
|
+
this.errorDetails = /* @__PURE__ */ new Map();
|
|
217
|
+
this.sentryEventIds = /* @__PURE__ */ new Map();
|
|
218
|
+
}
|
|
219
|
+
addServerError(serverError) {
|
|
220
|
+
const key = this.getKey(serverError);
|
|
221
|
+
if (!this.errorDetails.has(key)) {
|
|
222
|
+
this.errorDetails.set(key, serverError);
|
|
223
|
+
}
|
|
224
|
+
this.errorCounts.set(key, (this.errorCounts.get(key) || 0) + 1);
|
|
225
|
+
const sentryEventId = getSentryEventId();
|
|
226
|
+
if (sentryEventId) {
|
|
227
|
+
this.sentryEventIds.set(key, sentryEventId);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
getAndResetServerErrors() {
|
|
231
|
+
const data = [];
|
|
232
|
+
this.errorCounts.forEach((count, key) => {
|
|
233
|
+
const serverError = this.errorDetails.get(key);
|
|
234
|
+
if (serverError) {
|
|
235
|
+
data.push({
|
|
236
|
+
consumer: serverError.consumer || null,
|
|
237
|
+
method: serverError.method,
|
|
238
|
+
path: serverError.path,
|
|
239
|
+
type: serverError.type,
|
|
240
|
+
msg: truncateExceptionMessage(serverError.msg),
|
|
241
|
+
traceback: truncateExceptionStackTrace(serverError.traceback),
|
|
242
|
+
sentry_event_id: this.sentryEventIds.get(key) || null,
|
|
243
|
+
error_count: count
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
});
|
|
247
|
+
this.errorCounts.clear();
|
|
248
|
+
this.errorDetails.clear();
|
|
249
|
+
return data;
|
|
250
|
+
}
|
|
251
|
+
getKey(serverError) {
|
|
252
|
+
const hashInput = [
|
|
253
|
+
serverError.consumer || "",
|
|
254
|
+
serverError.method.toUpperCase(),
|
|
255
|
+
serverError.path,
|
|
256
|
+
serverError.type,
|
|
257
|
+
serverError.msg.trim(),
|
|
258
|
+
serverError.traceback.trim()
|
|
259
|
+
].join("|");
|
|
260
|
+
return createHash("md5").update(hashInput).digest("hex");
|
|
261
|
+
}
|
|
262
|
+
};
|
|
263
|
+
__name(_ServerErrorCounter, "ServerErrorCounter");
|
|
264
|
+
var ServerErrorCounter = _ServerErrorCounter;
|
|
265
|
+
function truncateExceptionMessage(msg) {
|
|
266
|
+
if (msg.length <= MAX_MSG_LENGTH) {
|
|
267
|
+
return msg;
|
|
268
|
+
}
|
|
269
|
+
const suffix = "... (truncated)";
|
|
270
|
+
const cutoff = MAX_MSG_LENGTH - suffix.length;
|
|
271
|
+
return msg.substring(0, cutoff) + suffix;
|
|
272
|
+
}
|
|
273
|
+
__name(truncateExceptionMessage, "truncateExceptionMessage");
|
|
274
|
+
function truncateExceptionStackTrace(stack) {
|
|
275
|
+
const suffix = "... (truncated) ...";
|
|
276
|
+
const cutoff = MAX_STACKTRACE_LENGTH - suffix.length;
|
|
277
|
+
const lines = stack.trim().split("\n");
|
|
278
|
+
const truncatedLines = [];
|
|
279
|
+
let length = 0;
|
|
280
|
+
for (const line of lines) {
|
|
281
|
+
if (length + line.length + 1 > cutoff) {
|
|
282
|
+
truncatedLines.push(suffix);
|
|
283
|
+
break;
|
|
284
|
+
}
|
|
285
|
+
truncatedLines.push(line);
|
|
286
|
+
length += line.length + 1;
|
|
287
|
+
}
|
|
288
|
+
return truncatedLines.join("\n");
|
|
289
|
+
}
|
|
290
|
+
__name(truncateExceptionStackTrace, "truncateExceptionStackTrace");
|
|
291
|
+
|
|
190
292
|
// src/common/tempGzipFile.ts
|
|
191
293
|
import { Buffer as Buffer2 } from "buffer";
|
|
192
294
|
import { randomUUID } from "crypto";
|
|
@@ -209,9 +311,9 @@ var _TempGzipFile = class _TempGzipFile {
|
|
|
209
311
|
this.writeStream.once("ready", resolve);
|
|
210
312
|
this.writeStream.once("error", reject);
|
|
211
313
|
});
|
|
212
|
-
this.closedPromise = new Promise((resolve) => {
|
|
314
|
+
this.closedPromise = new Promise((resolve, reject) => {
|
|
213
315
|
this.writeStream.once("close", resolve);
|
|
214
|
-
this.writeStream.once("error",
|
|
316
|
+
this.writeStream.once("error", reject);
|
|
215
317
|
});
|
|
216
318
|
this.gzip = createGzip();
|
|
217
319
|
this.gzip.pipe(this.writeStream);
|
|
@@ -309,6 +411,7 @@ var DEFAULT_CONFIG = {
|
|
|
309
411
|
logRequestBody: false,
|
|
310
412
|
logResponseHeaders: true,
|
|
311
413
|
logResponseBody: false,
|
|
414
|
+
logException: true,
|
|
312
415
|
maskQueryParams: [],
|
|
313
416
|
maskHeaders: [],
|
|
314
417
|
excludePaths: []
|
|
@@ -361,7 +464,10 @@ var _RequestLogger = class _RequestLogger {
|
|
|
361
464
|
hasSupportedContentType(headers) {
|
|
362
465
|
var _a2;
|
|
363
466
|
const contentType = (_a2 = headers.find(([k]) => k.toLowerCase() === "content-type")) == null ? void 0 : _a2[1];
|
|
364
|
-
return
|
|
467
|
+
return this.isSupportedContentType(contentType);
|
|
468
|
+
}
|
|
469
|
+
isSupportedContentType(contentType) {
|
|
470
|
+
return typeof contentType === "string" && ALLOWED_CONTENT_TYPES.some((t) => contentType.startsWith(t));
|
|
365
471
|
}
|
|
366
472
|
maskQueryParams(search) {
|
|
367
473
|
const params = new URLSearchParams(search);
|
|
@@ -378,7 +484,7 @@ var _RequestLogger = class _RequestLogger {
|
|
|
378
484
|
this.shouldMaskHeader(k) ? MASKED : v
|
|
379
485
|
]);
|
|
380
486
|
}
|
|
381
|
-
logRequest(request, response) {
|
|
487
|
+
logRequest(request, response, error) {
|
|
382
488
|
var _a2, _b, _c;
|
|
383
489
|
if (!this.enabled || this.suspendUntil !== null) return;
|
|
384
490
|
const url = new URL(request.url);
|
|
@@ -389,8 +495,6 @@ var _RequestLogger = class _RequestLogger {
|
|
|
389
495
|
}
|
|
390
496
|
url.search = this.config.logQueryParams ? this.maskQueryParams(url.search) : "";
|
|
391
497
|
request.url = url.toString();
|
|
392
|
-
request.headers = this.config.logRequestHeaders ? this.maskHeaders(request.headers) : [];
|
|
393
|
-
response.headers = this.config.logResponseHeaders ? this.maskHeaders(response.headers) : [];
|
|
394
498
|
if (!this.config.logRequestBody || !this.hasSupportedContentType(request.headers)) {
|
|
395
499
|
request.body = void 0;
|
|
396
500
|
} else if (request.body) {
|
|
@@ -423,10 +527,18 @@ var _RequestLogger = class _RequestLogger {
|
|
|
423
527
|
}
|
|
424
528
|
}
|
|
425
529
|
}
|
|
530
|
+
request.headers = this.config.logRequestHeaders ? this.maskHeaders(request.headers) : [];
|
|
531
|
+
response.headers = this.config.logResponseHeaders ? this.maskHeaders(response.headers) : [];
|
|
426
532
|
const item = {
|
|
427
533
|
uuid: randomUUID2(),
|
|
428
534
|
request: skipEmptyValues(request),
|
|
429
|
-
response: skipEmptyValues(response)
|
|
535
|
+
response: skipEmptyValues(response),
|
|
536
|
+
exception: error && this.config.logException ? {
|
|
537
|
+
type: error.name,
|
|
538
|
+
message: truncateExceptionMessage(error.message),
|
|
539
|
+
stacktrace: truncateExceptionStackTrace(error.stack || ""),
|
|
540
|
+
sentryEventId: getSentryEventId()
|
|
541
|
+
} : null
|
|
430
542
|
};
|
|
431
543
|
[
|
|
432
544
|
item.request.body,
|
|
@@ -588,104 +700,6 @@ function checkWritableFs() {
|
|
|
588
700
|
}
|
|
589
701
|
__name(checkWritableFs, "checkWritableFs");
|
|
590
702
|
|
|
591
|
-
// src/common/serverErrorCounter.ts
|
|
592
|
-
import { createHash } from "crypto";
|
|
593
|
-
var MAX_MSG_LENGTH = 2048;
|
|
594
|
-
var MAX_STACKTRACE_LENGTH = 65536;
|
|
595
|
-
var _ServerErrorCounter = class _ServerErrorCounter {
|
|
596
|
-
errorCounts;
|
|
597
|
-
errorDetails;
|
|
598
|
-
sentryEventIds;
|
|
599
|
-
sentry;
|
|
600
|
-
constructor() {
|
|
601
|
-
this.errorCounts = /* @__PURE__ */ new Map();
|
|
602
|
-
this.errorDetails = /* @__PURE__ */ new Map();
|
|
603
|
-
this.sentryEventIds = /* @__PURE__ */ new Map();
|
|
604
|
-
this.tryImportSentry();
|
|
605
|
-
}
|
|
606
|
-
addServerError(serverError) {
|
|
607
|
-
const key = this.getKey(serverError);
|
|
608
|
-
if (!this.errorDetails.has(key)) {
|
|
609
|
-
this.errorDetails.set(key, serverError);
|
|
610
|
-
}
|
|
611
|
-
this.errorCounts.set(key, (this.errorCounts.get(key) || 0) + 1);
|
|
612
|
-
this.captureSentryEventId(key);
|
|
613
|
-
}
|
|
614
|
-
getAndResetServerErrors() {
|
|
615
|
-
const data = [];
|
|
616
|
-
this.errorCounts.forEach((count, key) => {
|
|
617
|
-
const serverError = this.errorDetails.get(key);
|
|
618
|
-
if (serverError) {
|
|
619
|
-
data.push({
|
|
620
|
-
consumer: serverError.consumer || null,
|
|
621
|
-
method: serverError.method,
|
|
622
|
-
path: serverError.path,
|
|
623
|
-
type: serverError.type,
|
|
624
|
-
msg: this.getTruncatedMessage(serverError.msg),
|
|
625
|
-
traceback: this.getTruncatedStack(serverError.traceback),
|
|
626
|
-
sentry_event_id: this.sentryEventIds.get(key) || null,
|
|
627
|
-
error_count: count
|
|
628
|
-
});
|
|
629
|
-
}
|
|
630
|
-
});
|
|
631
|
-
this.errorCounts.clear();
|
|
632
|
-
this.errorDetails.clear();
|
|
633
|
-
return data;
|
|
634
|
-
}
|
|
635
|
-
getKey(serverError) {
|
|
636
|
-
const hashInput = [
|
|
637
|
-
serverError.consumer || "",
|
|
638
|
-
serverError.method.toUpperCase(),
|
|
639
|
-
serverError.path,
|
|
640
|
-
serverError.type,
|
|
641
|
-
serverError.msg.trim(),
|
|
642
|
-
serverError.traceback.trim()
|
|
643
|
-
].join("|");
|
|
644
|
-
return createHash("md5").update(hashInput).digest("hex");
|
|
645
|
-
}
|
|
646
|
-
getTruncatedMessage(msg) {
|
|
647
|
-
msg = msg.trim();
|
|
648
|
-
if (msg.length <= MAX_MSG_LENGTH) {
|
|
649
|
-
return msg;
|
|
650
|
-
}
|
|
651
|
-
const suffix = "... (truncated)";
|
|
652
|
-
const cutoff = MAX_MSG_LENGTH - suffix.length;
|
|
653
|
-
return msg.substring(0, cutoff) + suffix;
|
|
654
|
-
}
|
|
655
|
-
getTruncatedStack(stack) {
|
|
656
|
-
const suffix = "... (truncated) ...";
|
|
657
|
-
const cutoff = MAX_STACKTRACE_LENGTH - suffix.length;
|
|
658
|
-
const lines = stack.trim().split("\n");
|
|
659
|
-
const truncatedLines = [];
|
|
660
|
-
let length = 0;
|
|
661
|
-
for (const line of lines) {
|
|
662
|
-
if (length + line.length + 1 > cutoff) {
|
|
663
|
-
truncatedLines.push(suffix);
|
|
664
|
-
break;
|
|
665
|
-
}
|
|
666
|
-
truncatedLines.push(line);
|
|
667
|
-
length += line.length + 1;
|
|
668
|
-
}
|
|
669
|
-
return truncatedLines.join("\n");
|
|
670
|
-
}
|
|
671
|
-
captureSentryEventId(serverErrorKey) {
|
|
672
|
-
if (this.sentry && this.sentry.lastEventId) {
|
|
673
|
-
const eventId = this.sentry.lastEventId();
|
|
674
|
-
if (eventId) {
|
|
675
|
-
this.sentryEventIds.set(serverErrorKey, eventId);
|
|
676
|
-
}
|
|
677
|
-
}
|
|
678
|
-
}
|
|
679
|
-
async tryImportSentry() {
|
|
680
|
-
try {
|
|
681
|
-
this.sentry = await import("@sentry/node");
|
|
682
|
-
} catch (e) {
|
|
683
|
-
}
|
|
684
|
-
}
|
|
685
|
-
};
|
|
686
|
-
__name(_ServerErrorCounter, "ServerErrorCounter");
|
|
687
|
-
var ServerErrorCounter = _ServerErrorCounter;
|
|
688
|
-
|
|
689
703
|
// src/common/validationErrorCounter.ts
|
|
690
704
|
import { createHash as createHash2 } from "crypto";
|
|
691
705
|
var _ValidationErrorCounter = class _ValidationErrorCounter {
|
|
@@ -759,6 +773,7 @@ var _ApitallyClient = class _ApitallyClient {
|
|
|
759
773
|
syncIntervalId;
|
|
760
774
|
startupData;
|
|
761
775
|
startupDataSent = false;
|
|
776
|
+
enabled = true;
|
|
762
777
|
requestCounter;
|
|
763
778
|
requestLogger;
|
|
764
779
|
validationErrorCounter;
|
|
@@ -795,12 +810,16 @@ var _ApitallyClient = class _ApitallyClient {
|
|
|
795
810
|
}
|
|
796
811
|
return _ApitallyClient.instance;
|
|
797
812
|
}
|
|
813
|
+
isEnabled() {
|
|
814
|
+
return this.enabled;
|
|
815
|
+
}
|
|
798
816
|
static async shutdown() {
|
|
799
817
|
if (_ApitallyClient.instance) {
|
|
800
818
|
await _ApitallyClient.instance.handleShutdown();
|
|
801
819
|
}
|
|
802
820
|
}
|
|
803
821
|
async handleShutdown() {
|
|
822
|
+
this.enabled = false;
|
|
804
823
|
this.stopSync();
|
|
805
824
|
await this.sendSyncData();
|
|
806
825
|
await this.sendLogData();
|
|
@@ -984,6 +1003,7 @@ var _ApitallyClient = class _ApitallyClient {
|
|
|
984
1003
|
if (error instanceof HTTPError) {
|
|
985
1004
|
if (error.response.status === 404) {
|
|
986
1005
|
this.logger.error(`Invalid Apitally client ID: '${this.clientId}'`);
|
|
1006
|
+
this.enabled = false;
|
|
987
1007
|
this.stopSync();
|
|
988
1008
|
return true;
|
|
989
1009
|
}
|
|
@@ -1032,8 +1052,13 @@ var useApitally = /* @__PURE__ */ __name((app, config) => {
|
|
|
1032
1052
|
var getMiddleware = /* @__PURE__ */ __name((client) => {
|
|
1033
1053
|
return async (ctx, next) => {
|
|
1034
1054
|
var _a2;
|
|
1055
|
+
if (!client.isEnabled()) {
|
|
1056
|
+
await next();
|
|
1057
|
+
return;
|
|
1058
|
+
}
|
|
1035
1059
|
let path;
|
|
1036
1060
|
let statusCode;
|
|
1061
|
+
let serverError;
|
|
1037
1062
|
const startTime = performance.now();
|
|
1038
1063
|
try {
|
|
1039
1064
|
await next();
|
|
@@ -1041,6 +1066,7 @@ var getMiddleware = /* @__PURE__ */ __name((client) => {
|
|
|
1041
1066
|
path = getPath(ctx);
|
|
1042
1067
|
statusCode = error.statusCode || error.status || 500;
|
|
1043
1068
|
if (path && statusCode === 500 && error instanceof Error) {
|
|
1069
|
+
serverError = error;
|
|
1044
1070
|
client.serverErrorCounter.addServerError({
|
|
1045
1071
|
consumer: (_a2 = getConsumer(ctx)) == null ? void 0 : _a2.identifier,
|
|
1046
1072
|
method: ctx.request.method,
|
|
@@ -1092,7 +1118,7 @@ var getMiddleware = /* @__PURE__ */ __name((client) => {
|
|
|
1092
1118
|
headers: convertHeaders(ctx.response.headers),
|
|
1093
1119
|
size: Number(ctx.response.headers["content-length"]),
|
|
1094
1120
|
body: convertBody(ctx.response.body, ctx.response.get("content-type"))
|
|
1095
|
-
});
|
|
1121
|
+
}, serverError);
|
|
1096
1122
|
}
|
|
1097
1123
|
}
|
|
1098
1124
|
};
|