apitally 0.11.5 → 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.
Files changed (60) hide show
  1. package/README.md +2 -2
  2. package/dist/common/client.cjs +116 -105
  3. package/dist/common/client.cjs.map +1 -1
  4. package/dist/common/client.js +111 -100
  5. package/dist/common/client.js.map +1 -1
  6. package/dist/common/requestLogger.cjs +55 -2
  7. package/dist/common/requestLogger.cjs.map +1 -1
  8. package/dist/common/requestLogger.d.cts +2 -1
  9. package/dist/common/requestLogger.d.ts +2 -1
  10. package/dist/common/requestLogger.js +55 -2
  11. package/dist/common/requestLogger.js.map +1 -1
  12. package/dist/common/sentry.cjs +55 -0
  13. package/dist/common/sentry.cjs.map +1 -0
  14. package/dist/common/sentry.d.cts +6 -0
  15. package/dist/common/sentry.d.ts +6 -0
  16. package/dist/common/sentry.js +22 -0
  17. package/dist/common/sentry.js.map +1 -0
  18. package/dist/common/serverErrorCounter.cjs +58 -45
  19. package/dist/common/serverErrorCounter.cjs.map +1 -1
  20. package/dist/common/serverErrorCounter.d.cts +3 -6
  21. package/dist/common/serverErrorCounter.d.ts +3 -6
  22. package/dist/common/serverErrorCounter.js +53 -45
  23. package/dist/common/serverErrorCounter.js.map +1 -1
  24. package/dist/express/index.cjs +117 -106
  25. package/dist/express/index.cjs.map +1 -1
  26. package/dist/express/index.js +112 -101
  27. package/dist/express/index.js.map +1 -1
  28. package/dist/express/middleware.cjs +117 -106
  29. package/dist/express/middleware.cjs.map +1 -1
  30. package/dist/express/middleware.js +112 -101
  31. package/dist/express/middleware.js.map +1 -1
  32. package/dist/fastify/index.cjs +117 -106
  33. package/dist/fastify/index.cjs.map +1 -1
  34. package/dist/fastify/index.js +112 -101
  35. package/dist/fastify/index.js.map +1 -1
  36. package/dist/fastify/plugin.cjs +117 -106
  37. package/dist/fastify/plugin.cjs.map +1 -1
  38. package/dist/fastify/plugin.js +112 -101
  39. package/dist/fastify/plugin.js.map +1 -1
  40. package/dist/hono/index.cjs +117 -106
  41. package/dist/hono/index.cjs.map +1 -1
  42. package/dist/hono/index.js +112 -101
  43. package/dist/hono/index.js.map +1 -1
  44. package/dist/hono/middleware.cjs +117 -106
  45. package/dist/hono/middleware.cjs.map +1 -1
  46. package/dist/hono/middleware.js +112 -101
  47. package/dist/hono/middleware.js.map +1 -1
  48. package/dist/koa/index.cjs +119 -106
  49. package/dist/koa/index.cjs.map +1 -1
  50. package/dist/koa/index.js +114 -101
  51. package/dist/koa/index.js.map +1 -1
  52. package/dist/koa/middleware.cjs +119 -106
  53. package/dist/koa/middleware.cjs.map +1 -1
  54. package/dist/koa/middleware.js +114 -101
  55. package/dist/koa/middleware.js.map +1 -1
  56. package/dist/nestjs/index.cjs +117 -106
  57. package/dist/nestjs/index.cjs.map +1 -1
  58. package/dist/nestjs/index.js +112 -101
  59. package/dist/nestjs/index.js.map +1 -1
  60. package/package.json +1 -1
@@ -210,14 +210,116 @@ var RequestCounter = _RequestCounter;
210
210
  // src/common/requestLogger.ts
211
211
  var import_async_lock = __toESM(require("async-lock"), 1);
212
212
  var import_buffer2 = require("buffer");
213
- var import_crypto2 = require("crypto");
213
+ var import_crypto3 = require("crypto");
214
214
  var import_fs2 = require("fs");
215
215
  var import_os2 = require("os");
216
216
  var import_path2 = require("path");
217
217
 
218
+ // src/common/sentry.ts
219
+ var sentry;
220
+ (async () => {
221
+ try {
222
+ sentry = await import("@sentry/node");
223
+ } catch (e) {
224
+ }
225
+ })();
226
+ function getSentryEventId() {
227
+ if (sentry && sentry.lastEventId) {
228
+ return sentry.lastEventId();
229
+ }
230
+ return void 0;
231
+ }
232
+ __name(getSentryEventId, "getSentryEventId");
233
+
234
+ // src/common/serverErrorCounter.ts
235
+ var import_crypto = require("crypto");
236
+ var MAX_MSG_LENGTH = 2048;
237
+ var MAX_STACKTRACE_LENGTH = 65536;
238
+ var _ServerErrorCounter = class _ServerErrorCounter {
239
+ errorCounts;
240
+ errorDetails;
241
+ sentryEventIds;
242
+ constructor() {
243
+ this.errorCounts = /* @__PURE__ */ new Map();
244
+ this.errorDetails = /* @__PURE__ */ new Map();
245
+ this.sentryEventIds = /* @__PURE__ */ new Map();
246
+ }
247
+ addServerError(serverError) {
248
+ const key = this.getKey(serverError);
249
+ if (!this.errorDetails.has(key)) {
250
+ this.errorDetails.set(key, serverError);
251
+ }
252
+ this.errorCounts.set(key, (this.errorCounts.get(key) || 0) + 1);
253
+ const sentryEventId = getSentryEventId();
254
+ if (sentryEventId) {
255
+ this.sentryEventIds.set(key, sentryEventId);
256
+ }
257
+ }
258
+ getAndResetServerErrors() {
259
+ const data = [];
260
+ this.errorCounts.forEach((count, key) => {
261
+ const serverError = this.errorDetails.get(key);
262
+ if (serverError) {
263
+ data.push({
264
+ consumer: serverError.consumer || null,
265
+ method: serverError.method,
266
+ path: serverError.path,
267
+ type: serverError.type,
268
+ msg: truncateExceptionMessage(serverError.msg),
269
+ traceback: truncateExceptionStackTrace(serverError.traceback),
270
+ sentry_event_id: this.sentryEventIds.get(key) || null,
271
+ error_count: count
272
+ });
273
+ }
274
+ });
275
+ this.errorCounts.clear();
276
+ this.errorDetails.clear();
277
+ return data;
278
+ }
279
+ getKey(serverError) {
280
+ const hashInput = [
281
+ serverError.consumer || "",
282
+ serverError.method.toUpperCase(),
283
+ serverError.path,
284
+ serverError.type,
285
+ serverError.msg.trim(),
286
+ serverError.traceback.trim()
287
+ ].join("|");
288
+ return (0, import_crypto.createHash)("md5").update(hashInput).digest("hex");
289
+ }
290
+ };
291
+ __name(_ServerErrorCounter, "ServerErrorCounter");
292
+ var ServerErrorCounter = _ServerErrorCounter;
293
+ function truncateExceptionMessage(msg) {
294
+ if (msg.length <= MAX_MSG_LENGTH) {
295
+ return msg;
296
+ }
297
+ const suffix = "... (truncated)";
298
+ const cutoff = MAX_MSG_LENGTH - suffix.length;
299
+ return msg.substring(0, cutoff) + suffix;
300
+ }
301
+ __name(truncateExceptionMessage, "truncateExceptionMessage");
302
+ function truncateExceptionStackTrace(stack) {
303
+ const suffix = "... (truncated) ...";
304
+ const cutoff = MAX_STACKTRACE_LENGTH - suffix.length;
305
+ const lines = stack.trim().split("\n");
306
+ const truncatedLines = [];
307
+ let length = 0;
308
+ for (const line of lines) {
309
+ if (length + line.length + 1 > cutoff) {
310
+ truncatedLines.push(suffix);
311
+ break;
312
+ }
313
+ truncatedLines.push(line);
314
+ length += line.length + 1;
315
+ }
316
+ return truncatedLines.join("\n");
317
+ }
318
+ __name(truncateExceptionStackTrace, "truncateExceptionStackTrace");
319
+
218
320
  // src/common/tempGzipFile.ts
219
321
  var import_buffer = require("buffer");
220
- var import_crypto = require("crypto");
322
+ var import_crypto2 = require("crypto");
221
323
  var import_fs = require("fs");
222
324
  var import_os = require("os");
223
325
  var import_path = require("path");
@@ -230,7 +332,7 @@ var _TempGzipFile = class _TempGzipFile {
230
332
  readyPromise;
231
333
  closedPromise;
232
334
  constructor() {
233
- this.uuid = (0, import_crypto.randomUUID)();
335
+ this.uuid = (0, import_crypto2.randomUUID)();
234
336
  this.filePath = (0, import_path.join)((0, import_os.tmpdir)(), `apitally-${this.uuid}.gz`);
235
337
  this.writeStream = (0, import_fs.createWriteStream)(this.filePath);
236
338
  this.readyPromise = new Promise((resolve, reject) => {
@@ -337,6 +439,7 @@ var DEFAULT_CONFIG = {
337
439
  logRequestBody: false,
338
440
  logResponseHeaders: true,
339
441
  logResponseBody: false,
442
+ logException: true,
340
443
  maskQueryParams: [],
341
444
  maskHeaders: [],
342
445
  excludePaths: []
@@ -409,7 +512,7 @@ var _RequestLogger = class _RequestLogger {
409
512
  this.shouldMaskHeader(k) ? MASKED : v
410
513
  ]);
411
514
  }
412
- logRequest(request, response) {
515
+ logRequest(request, response, error) {
413
516
  var _a2, _b, _c;
414
517
  if (!this.enabled || this.suspendUntil !== null) return;
415
518
  const url = new URL(request.url);
@@ -455,9 +558,15 @@ var _RequestLogger = class _RequestLogger {
455
558
  request.headers = this.config.logRequestHeaders ? this.maskHeaders(request.headers) : [];
456
559
  response.headers = this.config.logResponseHeaders ? this.maskHeaders(response.headers) : [];
457
560
  const item = {
458
- uuid: (0, import_crypto2.randomUUID)(),
561
+ uuid: (0, import_crypto3.randomUUID)(),
459
562
  request: skipEmptyValues(request),
460
- response: skipEmptyValues(response)
563
+ response: skipEmptyValues(response),
564
+ exception: error && this.config.logException ? {
565
+ type: error.name,
566
+ message: truncateExceptionMessage(error.message),
567
+ stacktrace: truncateExceptionStackTrace(error.stack || ""),
568
+ sentryEventId: getSentryEventId()
569
+ } : null
461
570
  };
462
571
  [
463
572
  item.request.body,
@@ -609,7 +718,7 @@ function skipEmptyValues(data) {
609
718
  __name(skipEmptyValues, "skipEmptyValues");
610
719
  function checkWritableFs() {
611
720
  try {
612
- const testPath = (0, import_path2.join)((0, import_os2.tmpdir)(), `apitally-${(0, import_crypto2.randomUUID)()}`);
721
+ const testPath = (0, import_path2.join)((0, import_os2.tmpdir)(), `apitally-${(0, import_crypto3.randomUUID)()}`);
613
722
  (0, import_fs2.writeFileSync)(testPath, "test");
614
723
  (0, import_fs2.unlinkSync)(testPath);
615
724
  return true;
@@ -619,104 +728,6 @@ function checkWritableFs() {
619
728
  }
620
729
  __name(checkWritableFs, "checkWritableFs");
621
730
 
622
- // src/common/serverErrorCounter.ts
623
- var import_crypto3 = require("crypto");
624
- var MAX_MSG_LENGTH = 2048;
625
- var MAX_STACKTRACE_LENGTH = 65536;
626
- var _ServerErrorCounter = class _ServerErrorCounter {
627
- errorCounts;
628
- errorDetails;
629
- sentryEventIds;
630
- sentry;
631
- constructor() {
632
- this.errorCounts = /* @__PURE__ */ new Map();
633
- this.errorDetails = /* @__PURE__ */ new Map();
634
- this.sentryEventIds = /* @__PURE__ */ new Map();
635
- this.tryImportSentry();
636
- }
637
- addServerError(serverError) {
638
- const key = this.getKey(serverError);
639
- if (!this.errorDetails.has(key)) {
640
- this.errorDetails.set(key, serverError);
641
- }
642
- this.errorCounts.set(key, (this.errorCounts.get(key) || 0) + 1);
643
- this.captureSentryEventId(key);
644
- }
645
- getAndResetServerErrors() {
646
- const data = [];
647
- this.errorCounts.forEach((count, key) => {
648
- const serverError = this.errorDetails.get(key);
649
- if (serverError) {
650
- data.push({
651
- consumer: serverError.consumer || null,
652
- method: serverError.method,
653
- path: serverError.path,
654
- type: serverError.type,
655
- msg: this.getTruncatedMessage(serverError.msg),
656
- traceback: this.getTruncatedStack(serverError.traceback),
657
- sentry_event_id: this.sentryEventIds.get(key) || null,
658
- error_count: count
659
- });
660
- }
661
- });
662
- this.errorCounts.clear();
663
- this.errorDetails.clear();
664
- return data;
665
- }
666
- getKey(serverError) {
667
- const hashInput = [
668
- serverError.consumer || "",
669
- serverError.method.toUpperCase(),
670
- serverError.path,
671
- serverError.type,
672
- serverError.msg.trim(),
673
- serverError.traceback.trim()
674
- ].join("|");
675
- return (0, import_crypto3.createHash)("md5").update(hashInput).digest("hex");
676
- }
677
- getTruncatedMessage(msg) {
678
- msg = msg.trim();
679
- if (msg.length <= MAX_MSG_LENGTH) {
680
- return msg;
681
- }
682
- const suffix = "... (truncated)";
683
- const cutoff = MAX_MSG_LENGTH - suffix.length;
684
- return msg.substring(0, cutoff) + suffix;
685
- }
686
- getTruncatedStack(stack) {
687
- const suffix = "... (truncated) ...";
688
- const cutoff = MAX_STACKTRACE_LENGTH - suffix.length;
689
- const lines = stack.trim().split("\n");
690
- const truncatedLines = [];
691
- let length = 0;
692
- for (const line of lines) {
693
- if (length + line.length + 1 > cutoff) {
694
- truncatedLines.push(suffix);
695
- break;
696
- }
697
- truncatedLines.push(line);
698
- length += line.length + 1;
699
- }
700
- return truncatedLines.join("\n");
701
- }
702
- captureSentryEventId(serverErrorKey) {
703
- if (this.sentry && this.sentry.lastEventId) {
704
- const eventId = this.sentry.lastEventId();
705
- if (eventId) {
706
- this.sentryEventIds.set(serverErrorKey, eventId);
707
- }
708
- }
709
- }
710
- async tryImportSentry() {
711
- try {
712
- this.sentry = await import("@sentry/node");
713
- } catch (e) {
714
- }
715
- }
716
- };
717
- __name(_ServerErrorCounter, "ServerErrorCounter");
718
- var ServerErrorCounter = _ServerErrorCounter;
719
-
720
731
  // src/common/validationErrorCounter.ts
721
732
  var import_crypto4 = require("crypto");
722
733
  var _ValidationErrorCounter = class _ValidationErrorCounter {
@@ -1076,6 +1087,7 @@ var getMiddleware = /* @__PURE__ */ __name((client) => {
1076
1087
  }
1077
1088
  let path;
1078
1089
  let statusCode;
1090
+ let serverError;
1079
1091
  const startTime = performance.now();
1080
1092
  try {
1081
1093
  await next();
@@ -1083,6 +1095,7 @@ var getMiddleware = /* @__PURE__ */ __name((client) => {
1083
1095
  path = getPath(ctx);
1084
1096
  statusCode = error.statusCode || error.status || 500;
1085
1097
  if (path && statusCode === 500 && error instanceof Error) {
1098
+ serverError = error;
1086
1099
  client.serverErrorCounter.addServerError({
1087
1100
  consumer: (_a2 = getConsumer(ctx)) == null ? void 0 : _a2.identifier,
1088
1101
  method: ctx.request.method,
@@ -1134,7 +1147,7 @@ var getMiddleware = /* @__PURE__ */ __name((client) => {
1134
1147
  headers: convertHeaders(ctx.response.headers),
1135
1148
  size: Number(ctx.response.headers["content-length"]),
1136
1149
  body: convertBody(ctx.response.body, ctx.response.get("content-type"))
1137
- });
1150
+ }, serverError);
1138
1151
  }
1139
1152
  }
1140
1153
  };