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/express/index.js
CHANGED
|
@@ -190,6 +190,108 @@ import { unlinkSync as unlinkSync2, writeFileSync } from "fs";
|
|
|
190
190
|
import { tmpdir as tmpdir2 } from "os";
|
|
191
191
|
import { join as join2 } from "path";
|
|
192
192
|
|
|
193
|
+
// src/common/sentry.ts
|
|
194
|
+
var sentry;
|
|
195
|
+
(async () => {
|
|
196
|
+
try {
|
|
197
|
+
sentry = await import("@sentry/node");
|
|
198
|
+
} catch (e) {
|
|
199
|
+
}
|
|
200
|
+
})();
|
|
201
|
+
function getSentryEventId() {
|
|
202
|
+
if (sentry && sentry.lastEventId) {
|
|
203
|
+
return sentry.lastEventId();
|
|
204
|
+
}
|
|
205
|
+
return void 0;
|
|
206
|
+
}
|
|
207
|
+
__name(getSentryEventId, "getSentryEventId");
|
|
208
|
+
|
|
209
|
+
// src/common/serverErrorCounter.ts
|
|
210
|
+
import { createHash } from "crypto";
|
|
211
|
+
var MAX_MSG_LENGTH = 2048;
|
|
212
|
+
var MAX_STACKTRACE_LENGTH = 65536;
|
|
213
|
+
var _ServerErrorCounter = class _ServerErrorCounter {
|
|
214
|
+
errorCounts;
|
|
215
|
+
errorDetails;
|
|
216
|
+
sentryEventIds;
|
|
217
|
+
constructor() {
|
|
218
|
+
this.errorCounts = /* @__PURE__ */ new Map();
|
|
219
|
+
this.errorDetails = /* @__PURE__ */ new Map();
|
|
220
|
+
this.sentryEventIds = /* @__PURE__ */ new Map();
|
|
221
|
+
}
|
|
222
|
+
addServerError(serverError) {
|
|
223
|
+
const key = this.getKey(serverError);
|
|
224
|
+
if (!this.errorDetails.has(key)) {
|
|
225
|
+
this.errorDetails.set(key, serverError);
|
|
226
|
+
}
|
|
227
|
+
this.errorCounts.set(key, (this.errorCounts.get(key) || 0) + 1);
|
|
228
|
+
const sentryEventId = getSentryEventId();
|
|
229
|
+
if (sentryEventId) {
|
|
230
|
+
this.sentryEventIds.set(key, sentryEventId);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
getAndResetServerErrors() {
|
|
234
|
+
const data = [];
|
|
235
|
+
this.errorCounts.forEach((count, key) => {
|
|
236
|
+
const serverError = this.errorDetails.get(key);
|
|
237
|
+
if (serverError) {
|
|
238
|
+
data.push({
|
|
239
|
+
consumer: serverError.consumer || null,
|
|
240
|
+
method: serverError.method,
|
|
241
|
+
path: serverError.path,
|
|
242
|
+
type: serverError.type,
|
|
243
|
+
msg: truncateExceptionMessage(serverError.msg),
|
|
244
|
+
traceback: truncateExceptionStackTrace(serverError.traceback),
|
|
245
|
+
sentry_event_id: this.sentryEventIds.get(key) || null,
|
|
246
|
+
error_count: count
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
});
|
|
250
|
+
this.errorCounts.clear();
|
|
251
|
+
this.errorDetails.clear();
|
|
252
|
+
return data;
|
|
253
|
+
}
|
|
254
|
+
getKey(serverError) {
|
|
255
|
+
const hashInput = [
|
|
256
|
+
serverError.consumer || "",
|
|
257
|
+
serverError.method.toUpperCase(),
|
|
258
|
+
serverError.path,
|
|
259
|
+
serverError.type,
|
|
260
|
+
serverError.msg.trim(),
|
|
261
|
+
serverError.traceback.trim()
|
|
262
|
+
].join("|");
|
|
263
|
+
return createHash("md5").update(hashInput).digest("hex");
|
|
264
|
+
}
|
|
265
|
+
};
|
|
266
|
+
__name(_ServerErrorCounter, "ServerErrorCounter");
|
|
267
|
+
var ServerErrorCounter = _ServerErrorCounter;
|
|
268
|
+
function truncateExceptionMessage(msg) {
|
|
269
|
+
if (msg.length <= MAX_MSG_LENGTH) {
|
|
270
|
+
return msg;
|
|
271
|
+
}
|
|
272
|
+
const suffix = "... (truncated)";
|
|
273
|
+
const cutoff = MAX_MSG_LENGTH - suffix.length;
|
|
274
|
+
return msg.substring(0, cutoff) + suffix;
|
|
275
|
+
}
|
|
276
|
+
__name(truncateExceptionMessage, "truncateExceptionMessage");
|
|
277
|
+
function truncateExceptionStackTrace(stack) {
|
|
278
|
+
const suffix = "... (truncated) ...";
|
|
279
|
+
const cutoff = MAX_STACKTRACE_LENGTH - suffix.length;
|
|
280
|
+
const lines = stack.trim().split("\n");
|
|
281
|
+
const truncatedLines = [];
|
|
282
|
+
let length = 0;
|
|
283
|
+
for (const line of lines) {
|
|
284
|
+
if (length + line.length + 1 > cutoff) {
|
|
285
|
+
truncatedLines.push(suffix);
|
|
286
|
+
break;
|
|
287
|
+
}
|
|
288
|
+
truncatedLines.push(line);
|
|
289
|
+
length += line.length + 1;
|
|
290
|
+
}
|
|
291
|
+
return truncatedLines.join("\n");
|
|
292
|
+
}
|
|
293
|
+
__name(truncateExceptionStackTrace, "truncateExceptionStackTrace");
|
|
294
|
+
|
|
193
295
|
// src/common/tempGzipFile.ts
|
|
194
296
|
import { Buffer as Buffer2 } from "buffer";
|
|
195
297
|
import { randomUUID } from "crypto";
|
|
@@ -212,9 +314,9 @@ var _TempGzipFile = class _TempGzipFile {
|
|
|
212
314
|
this.writeStream.once("ready", resolve);
|
|
213
315
|
this.writeStream.once("error", reject);
|
|
214
316
|
});
|
|
215
|
-
this.closedPromise = new Promise((resolve) => {
|
|
317
|
+
this.closedPromise = new Promise((resolve, reject) => {
|
|
216
318
|
this.writeStream.once("close", resolve);
|
|
217
|
-
this.writeStream.once("error",
|
|
319
|
+
this.writeStream.once("error", reject);
|
|
218
320
|
});
|
|
219
321
|
this.gzip = createGzip();
|
|
220
322
|
this.gzip.pipe(this.writeStream);
|
|
@@ -312,6 +414,7 @@ var DEFAULT_CONFIG = {
|
|
|
312
414
|
logRequestBody: false,
|
|
313
415
|
logResponseHeaders: true,
|
|
314
416
|
logResponseBody: false,
|
|
417
|
+
logException: true,
|
|
315
418
|
maskQueryParams: [],
|
|
316
419
|
maskHeaders: [],
|
|
317
420
|
excludePaths: []
|
|
@@ -364,7 +467,10 @@ var _RequestLogger = class _RequestLogger {
|
|
|
364
467
|
hasSupportedContentType(headers) {
|
|
365
468
|
var _a2;
|
|
366
469
|
const contentType = (_a2 = headers.find(([k]) => k.toLowerCase() === "content-type")) == null ? void 0 : _a2[1];
|
|
367
|
-
return
|
|
470
|
+
return this.isSupportedContentType(contentType);
|
|
471
|
+
}
|
|
472
|
+
isSupportedContentType(contentType) {
|
|
473
|
+
return typeof contentType === "string" && ALLOWED_CONTENT_TYPES.some((t) => contentType.startsWith(t));
|
|
368
474
|
}
|
|
369
475
|
maskQueryParams(search) {
|
|
370
476
|
const params = new URLSearchParams(search);
|
|
@@ -381,7 +487,7 @@ var _RequestLogger = class _RequestLogger {
|
|
|
381
487
|
this.shouldMaskHeader(k) ? MASKED : v
|
|
382
488
|
]);
|
|
383
489
|
}
|
|
384
|
-
logRequest(request, response) {
|
|
490
|
+
logRequest(request, response, error) {
|
|
385
491
|
var _a2, _b, _c;
|
|
386
492
|
if (!this.enabled || this.suspendUntil !== null) return;
|
|
387
493
|
const url = new URL(request.url);
|
|
@@ -392,8 +498,6 @@ var _RequestLogger = class _RequestLogger {
|
|
|
392
498
|
}
|
|
393
499
|
url.search = this.config.logQueryParams ? this.maskQueryParams(url.search) : "";
|
|
394
500
|
request.url = url.toString();
|
|
395
|
-
request.headers = this.config.logRequestHeaders ? this.maskHeaders(request.headers) : [];
|
|
396
|
-
response.headers = this.config.logResponseHeaders ? this.maskHeaders(response.headers) : [];
|
|
397
501
|
if (!this.config.logRequestBody || !this.hasSupportedContentType(request.headers)) {
|
|
398
502
|
request.body = void 0;
|
|
399
503
|
} else if (request.body) {
|
|
@@ -426,10 +530,18 @@ var _RequestLogger = class _RequestLogger {
|
|
|
426
530
|
}
|
|
427
531
|
}
|
|
428
532
|
}
|
|
533
|
+
request.headers = this.config.logRequestHeaders ? this.maskHeaders(request.headers) : [];
|
|
534
|
+
response.headers = this.config.logResponseHeaders ? this.maskHeaders(response.headers) : [];
|
|
429
535
|
const item = {
|
|
430
536
|
uuid: randomUUID2(),
|
|
431
537
|
request: skipEmptyValues(request),
|
|
432
|
-
response: skipEmptyValues(response)
|
|
538
|
+
response: skipEmptyValues(response),
|
|
539
|
+
exception: error && this.config.logException ? {
|
|
540
|
+
type: error.name,
|
|
541
|
+
message: truncateExceptionMessage(error.message),
|
|
542
|
+
stacktrace: truncateExceptionStackTrace(error.stack || ""),
|
|
543
|
+
sentryEventId: getSentryEventId()
|
|
544
|
+
} : null
|
|
433
545
|
};
|
|
434
546
|
[
|
|
435
547
|
item.request.body,
|
|
@@ -591,104 +703,6 @@ function checkWritableFs() {
|
|
|
591
703
|
}
|
|
592
704
|
__name(checkWritableFs, "checkWritableFs");
|
|
593
705
|
|
|
594
|
-
// src/common/serverErrorCounter.ts
|
|
595
|
-
import { createHash } from "crypto";
|
|
596
|
-
var MAX_MSG_LENGTH = 2048;
|
|
597
|
-
var MAX_STACKTRACE_LENGTH = 65536;
|
|
598
|
-
var _ServerErrorCounter = class _ServerErrorCounter {
|
|
599
|
-
errorCounts;
|
|
600
|
-
errorDetails;
|
|
601
|
-
sentryEventIds;
|
|
602
|
-
sentry;
|
|
603
|
-
constructor() {
|
|
604
|
-
this.errorCounts = /* @__PURE__ */ new Map();
|
|
605
|
-
this.errorDetails = /* @__PURE__ */ new Map();
|
|
606
|
-
this.sentryEventIds = /* @__PURE__ */ new Map();
|
|
607
|
-
this.tryImportSentry();
|
|
608
|
-
}
|
|
609
|
-
addServerError(serverError) {
|
|
610
|
-
const key = this.getKey(serverError);
|
|
611
|
-
if (!this.errorDetails.has(key)) {
|
|
612
|
-
this.errorDetails.set(key, serverError);
|
|
613
|
-
}
|
|
614
|
-
this.errorCounts.set(key, (this.errorCounts.get(key) || 0) + 1);
|
|
615
|
-
this.captureSentryEventId(key);
|
|
616
|
-
}
|
|
617
|
-
getAndResetServerErrors() {
|
|
618
|
-
const data = [];
|
|
619
|
-
this.errorCounts.forEach((count, key) => {
|
|
620
|
-
const serverError = this.errorDetails.get(key);
|
|
621
|
-
if (serverError) {
|
|
622
|
-
data.push({
|
|
623
|
-
consumer: serverError.consumer || null,
|
|
624
|
-
method: serverError.method,
|
|
625
|
-
path: serverError.path,
|
|
626
|
-
type: serverError.type,
|
|
627
|
-
msg: this.getTruncatedMessage(serverError.msg),
|
|
628
|
-
traceback: this.getTruncatedStack(serverError.traceback),
|
|
629
|
-
sentry_event_id: this.sentryEventIds.get(key) || null,
|
|
630
|
-
error_count: count
|
|
631
|
-
});
|
|
632
|
-
}
|
|
633
|
-
});
|
|
634
|
-
this.errorCounts.clear();
|
|
635
|
-
this.errorDetails.clear();
|
|
636
|
-
return data;
|
|
637
|
-
}
|
|
638
|
-
getKey(serverError) {
|
|
639
|
-
const hashInput = [
|
|
640
|
-
serverError.consumer || "",
|
|
641
|
-
serverError.method.toUpperCase(),
|
|
642
|
-
serverError.path,
|
|
643
|
-
serverError.type,
|
|
644
|
-
serverError.msg.trim(),
|
|
645
|
-
serverError.traceback.trim()
|
|
646
|
-
].join("|");
|
|
647
|
-
return createHash("md5").update(hashInput).digest("hex");
|
|
648
|
-
}
|
|
649
|
-
getTruncatedMessage(msg) {
|
|
650
|
-
msg = msg.trim();
|
|
651
|
-
if (msg.length <= MAX_MSG_LENGTH) {
|
|
652
|
-
return msg;
|
|
653
|
-
}
|
|
654
|
-
const suffix = "... (truncated)";
|
|
655
|
-
const cutoff = MAX_MSG_LENGTH - suffix.length;
|
|
656
|
-
return msg.substring(0, cutoff) + suffix;
|
|
657
|
-
}
|
|
658
|
-
getTruncatedStack(stack) {
|
|
659
|
-
const suffix = "... (truncated) ...";
|
|
660
|
-
const cutoff = MAX_STACKTRACE_LENGTH - suffix.length;
|
|
661
|
-
const lines = stack.trim().split("\n");
|
|
662
|
-
const truncatedLines = [];
|
|
663
|
-
let length = 0;
|
|
664
|
-
for (const line of lines) {
|
|
665
|
-
if (length + line.length + 1 > cutoff) {
|
|
666
|
-
truncatedLines.push(suffix);
|
|
667
|
-
break;
|
|
668
|
-
}
|
|
669
|
-
truncatedLines.push(line);
|
|
670
|
-
length += line.length + 1;
|
|
671
|
-
}
|
|
672
|
-
return truncatedLines.join("\n");
|
|
673
|
-
}
|
|
674
|
-
captureSentryEventId(serverErrorKey) {
|
|
675
|
-
if (this.sentry && this.sentry.lastEventId) {
|
|
676
|
-
const eventId = this.sentry.lastEventId();
|
|
677
|
-
if (eventId) {
|
|
678
|
-
this.sentryEventIds.set(serverErrorKey, eventId);
|
|
679
|
-
}
|
|
680
|
-
}
|
|
681
|
-
}
|
|
682
|
-
async tryImportSentry() {
|
|
683
|
-
try {
|
|
684
|
-
this.sentry = await import("@sentry/node");
|
|
685
|
-
} catch (e) {
|
|
686
|
-
}
|
|
687
|
-
}
|
|
688
|
-
};
|
|
689
|
-
__name(_ServerErrorCounter, "ServerErrorCounter");
|
|
690
|
-
var ServerErrorCounter = _ServerErrorCounter;
|
|
691
|
-
|
|
692
706
|
// src/common/validationErrorCounter.ts
|
|
693
707
|
import { createHash as createHash2 } from "crypto";
|
|
694
708
|
var _ValidationErrorCounter = class _ValidationErrorCounter {
|
|
@@ -762,6 +776,7 @@ var _ApitallyClient = class _ApitallyClient {
|
|
|
762
776
|
syncIntervalId;
|
|
763
777
|
startupData;
|
|
764
778
|
startupDataSent = false;
|
|
779
|
+
enabled = true;
|
|
765
780
|
requestCounter;
|
|
766
781
|
requestLogger;
|
|
767
782
|
validationErrorCounter;
|
|
@@ -798,12 +813,16 @@ var _ApitallyClient = class _ApitallyClient {
|
|
|
798
813
|
}
|
|
799
814
|
return _ApitallyClient.instance;
|
|
800
815
|
}
|
|
816
|
+
isEnabled() {
|
|
817
|
+
return this.enabled;
|
|
818
|
+
}
|
|
801
819
|
static async shutdown() {
|
|
802
820
|
if (_ApitallyClient.instance) {
|
|
803
821
|
await _ApitallyClient.instance.handleShutdown();
|
|
804
822
|
}
|
|
805
823
|
}
|
|
806
824
|
async handleShutdown() {
|
|
825
|
+
this.enabled = false;
|
|
807
826
|
this.stopSync();
|
|
808
827
|
await this.sendSyncData();
|
|
809
828
|
await this.sendLogData();
|
|
@@ -987,6 +1006,7 @@ var _ApitallyClient = class _ApitallyClient {
|
|
|
987
1006
|
if (error instanceof HTTPError) {
|
|
988
1007
|
if (error.response.status === 404) {
|
|
989
1008
|
this.logger.error(`Invalid Apitally client ID: '${this.clientId}'`);
|
|
1009
|
+
this.enabled = false;
|
|
990
1010
|
this.stopSync();
|
|
991
1011
|
return true;
|
|
992
1012
|
}
|
|
@@ -1161,6 +1181,10 @@ var useApitally = /* @__PURE__ */ __name((app, config) => {
|
|
|
1161
1181
|
var getMiddleware = /* @__PURE__ */ __name((app, client) => {
|
|
1162
1182
|
let errorHandlerConfigured = false;
|
|
1163
1183
|
return (req, res, next) => {
|
|
1184
|
+
if (!client.isEnabled()) {
|
|
1185
|
+
next();
|
|
1186
|
+
return;
|
|
1187
|
+
}
|
|
1164
1188
|
if (!errorHandlerConfigured) {
|
|
1165
1189
|
app.use((err, req2, res2, next2) => {
|
|
1166
1190
|
res2.locals.serverError = err;
|
|
@@ -1172,7 +1196,10 @@ var getMiddleware = /* @__PURE__ */ __name((app, client) => {
|
|
|
1172
1196
|
const startTime = performance.now();
|
|
1173
1197
|
const originalSend = res.send;
|
|
1174
1198
|
res.send = (body) => {
|
|
1175
|
-
|
|
1199
|
+
const contentType = res.get("content-type");
|
|
1200
|
+
if (client.requestLogger.isSupportedContentType(contentType)) {
|
|
1201
|
+
res.locals.body = body;
|
|
1202
|
+
}
|
|
1176
1203
|
return originalSend.call(res, body);
|
|
1177
1204
|
};
|
|
1178
1205
|
res.on("finish", () => {
|
|
@@ -1246,7 +1273,7 @@ var getMiddleware = /* @__PURE__ */ __name((app, client) => {
|
|
|
1246
1273
|
headers: convertHeaders(res.getHeaders()),
|
|
1247
1274
|
size: Number(res.get("content-length")),
|
|
1248
1275
|
body: convertBody(res.locals.body, res.get("content-type"))
|
|
1249
|
-
});
|
|
1276
|
+
}, res.locals.serverError);
|
|
1250
1277
|
}
|
|
1251
1278
|
} catch (error) {
|
|
1252
1279
|
client.logger.error("Error while logging request in Apitally middleware.", {
|