apitally 0.10.0 → 0.11.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 +1 -2
- package/dist/common/client.cjs +408 -12
- package/dist/common/client.cjs.map +1 -1
- package/dist/common/client.d.cts +8 -1
- package/dist/common/client.d.ts +8 -1
- package/dist/common/client.js +404 -8
- package/dist/common/client.js.map +1 -1
- package/dist/common/consumerRegistry.d.cts +4 -0
- package/dist/common/consumerRegistry.d.ts +4 -0
- package/dist/common/requestCounter.d.cts +4 -0
- package/dist/common/requestCounter.d.ts +4 -0
- package/dist/common/requestLogger.cjs +429 -0
- package/dist/common/requestLogger.cjs.map +1 -0
- package/dist/common/requestLogger.d.cts +64 -0
- package/dist/common/requestLogger.d.ts +64 -0
- package/dist/common/requestLogger.js +405 -0
- package/dist/common/requestLogger.js.map +1 -0
- package/dist/common/serverErrorCounter.d.cts +4 -0
- package/dist/common/serverErrorCounter.d.ts +4 -0
- package/dist/common/tempGzipFile.cjs +93 -0
- package/dist/common/tempGzipFile.cjs.map +1 -0
- package/dist/common/tempGzipFile.d.cts +17 -0
- package/dist/common/tempGzipFile.d.ts +17 -0
- package/dist/common/tempGzipFile.js +74 -0
- package/dist/common/tempGzipFile.js.map +1 -0
- package/dist/common/types.cjs.map +1 -1
- package/dist/common/types.d.cts +5 -0
- package/dist/common/types.d.ts +5 -0
- package/dist/common/validationErrorCounter.d.cts +4 -0
- package/dist/common/validationErrorCounter.d.ts +4 -0
- package/dist/express/index.cjs +510 -34
- package/dist/express/index.cjs.map +1 -1
- package/dist/express/index.d.cts +4 -0
- package/dist/express/index.d.ts +4 -0
- package/dist/express/index.js +506 -30
- package/dist/express/index.js.map +1 -1
- package/dist/express/middleware.cjs +510 -34
- package/dist/express/middleware.cjs.map +1 -1
- package/dist/express/middleware.d.cts +4 -0
- package/dist/express/middleware.d.ts +4 -0
- package/dist/express/middleware.js +506 -30
- package/dist/express/middleware.js.map +1 -1
- package/dist/fastify/index.cjs +501 -27
- package/dist/fastify/index.cjs.map +1 -1
- package/dist/fastify/index.d.cts +4 -0
- package/dist/fastify/index.d.ts +4 -0
- package/dist/fastify/index.js +497 -23
- package/dist/fastify/index.js.map +1 -1
- package/dist/fastify/plugin.cjs +501 -27
- package/dist/fastify/plugin.cjs.map +1 -1
- package/dist/fastify/plugin.d.cts +4 -0
- package/dist/fastify/plugin.d.ts +4 -0
- package/dist/fastify/plugin.js +497 -23
- package/dist/fastify/plugin.js.map +1 -1
- package/dist/hono/index.cjs +470 -15
- package/dist/hono/index.cjs.map +1 -1
- package/dist/hono/index.d.cts +4 -0
- package/dist/hono/index.d.ts +4 -0
- package/dist/hono/index.js +466 -11
- package/dist/hono/index.js.map +1 -1
- package/dist/hono/middleware.cjs +470 -15
- package/dist/hono/middleware.cjs.map +1 -1
- package/dist/hono/middleware.d.cts +4 -0
- package/dist/hono/middleware.d.ts +4 -0
- package/dist/hono/middleware.js +466 -11
- package/dist/hono/middleware.js.map +1 -1
- package/dist/koa/index.cjs +485 -15
- package/dist/koa/index.cjs.map +1 -1
- package/dist/koa/index.d.cts +4 -0
- package/dist/koa/index.d.ts +4 -0
- package/dist/koa/index.js +481 -11
- package/dist/koa/index.js.map +1 -1
- package/dist/koa/middleware.cjs +485 -15
- package/dist/koa/middleware.cjs.map +1 -1
- package/dist/koa/middleware.d.cts +4 -0
- package/dist/koa/middleware.d.ts +4 -0
- package/dist/koa/middleware.js +481 -11
- package/dist/koa/middleware.js.map +1 -1
- package/dist/nestjs/index.cjs +510 -34
- package/dist/nestjs/index.cjs.map +1 -1
- package/dist/nestjs/index.d.cts +4 -0
- package/dist/nestjs/index.d.ts +4 -0
- package/dist/nestjs/index.js +506 -30
- package/dist/nestjs/index.js.map +1 -1
- package/package.json +8 -2
package/dist/hono/middleware.cjs
CHANGED
|
@@ -40,7 +40,7 @@ var import_handler = require("hono/utils/handler");
|
|
|
40
40
|
var import_perf_hooks = require("perf_hooks");
|
|
41
41
|
|
|
42
42
|
// src/common/client.ts
|
|
43
|
-
var
|
|
43
|
+
var import_crypto5 = require("crypto");
|
|
44
44
|
var import_fetch_retry = __toESM(require("fetch-retry"), 1);
|
|
45
45
|
|
|
46
46
|
// src/common/consumerRegistry.ts
|
|
@@ -209,8 +209,373 @@ var _RequestCounter = class _RequestCounter {
|
|
|
209
209
|
__name(_RequestCounter, "RequestCounter");
|
|
210
210
|
var RequestCounter = _RequestCounter;
|
|
211
211
|
|
|
212
|
-
// src/common/
|
|
212
|
+
// src/common/requestLogger.ts
|
|
213
|
+
var import_buffer2 = require("buffer");
|
|
214
|
+
var import_crypto2 = require("crypto");
|
|
215
|
+
var import_fs2 = require("fs");
|
|
216
|
+
var import_os2 = require("os");
|
|
217
|
+
var import_path2 = require("path");
|
|
218
|
+
|
|
219
|
+
// src/common/tempGzipFile.ts
|
|
220
|
+
var import_buffer = require("buffer");
|
|
213
221
|
var import_crypto = require("crypto");
|
|
222
|
+
var import_fs = require("fs");
|
|
223
|
+
var import_os = require("os");
|
|
224
|
+
var import_path = require("path");
|
|
225
|
+
var import_zlib = require("zlib");
|
|
226
|
+
var _TempGzipFile = class _TempGzipFile {
|
|
227
|
+
uuid;
|
|
228
|
+
filePath;
|
|
229
|
+
gzip;
|
|
230
|
+
writeStream;
|
|
231
|
+
readyPromise;
|
|
232
|
+
constructor() {
|
|
233
|
+
this.uuid = (0, import_crypto.randomUUID)();
|
|
234
|
+
this.filePath = (0, import_path.join)((0, import_os.tmpdir)(), `apitally-${this.uuid}.gz`);
|
|
235
|
+
this.writeStream = (0, import_fs.createWriteStream)(this.filePath);
|
|
236
|
+
this.readyPromise = new Promise((resolve, reject) => {
|
|
237
|
+
this.writeStream.once("ready", resolve);
|
|
238
|
+
this.writeStream.once("error", reject);
|
|
239
|
+
});
|
|
240
|
+
this.gzip = (0, import_zlib.createGzip)();
|
|
241
|
+
this.gzip.pipe(this.writeStream);
|
|
242
|
+
}
|
|
243
|
+
get size() {
|
|
244
|
+
return this.writeStream.bytesWritten;
|
|
245
|
+
}
|
|
246
|
+
async writeLine(data) {
|
|
247
|
+
await this.readyPromise;
|
|
248
|
+
return new Promise((resolve, reject) => {
|
|
249
|
+
this.gzip.write(import_buffer.Buffer.concat([
|
|
250
|
+
data,
|
|
251
|
+
import_buffer.Buffer.from("\n")
|
|
252
|
+
]), (error) => {
|
|
253
|
+
if (error) {
|
|
254
|
+
reject(error);
|
|
255
|
+
} else {
|
|
256
|
+
resolve();
|
|
257
|
+
}
|
|
258
|
+
});
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
async getContent() {
|
|
262
|
+
return new Promise((resolve, reject) => {
|
|
263
|
+
(0, import_fs.readFile)(this.filePath, (error, data) => {
|
|
264
|
+
if (error) {
|
|
265
|
+
reject(error);
|
|
266
|
+
} else {
|
|
267
|
+
resolve(data);
|
|
268
|
+
}
|
|
269
|
+
});
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
async close() {
|
|
273
|
+
return new Promise((resolve) => {
|
|
274
|
+
this.gzip.end(() => {
|
|
275
|
+
resolve();
|
|
276
|
+
});
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
async delete() {
|
|
280
|
+
await this.close();
|
|
281
|
+
(0, import_fs.unlinkSync)(this.filePath);
|
|
282
|
+
}
|
|
283
|
+
};
|
|
284
|
+
__name(_TempGzipFile, "TempGzipFile");
|
|
285
|
+
var TempGzipFile = _TempGzipFile;
|
|
286
|
+
|
|
287
|
+
// src/common/requestLogger.ts
|
|
288
|
+
var MAX_BODY_SIZE = 5e4;
|
|
289
|
+
var MAX_FILE_SIZE = 1e6;
|
|
290
|
+
var MAX_FILES = 50;
|
|
291
|
+
var MAX_PENDING_WRITES = 100;
|
|
292
|
+
var BODY_TOO_LARGE = import_buffer2.Buffer.from("<body too large>");
|
|
293
|
+
var BODY_MASKED = import_buffer2.Buffer.from("<masked>");
|
|
294
|
+
var MASKED = "******";
|
|
295
|
+
var ALLOWED_CONTENT_TYPES = [
|
|
296
|
+
"application/json",
|
|
297
|
+
"text/plain"
|
|
298
|
+
];
|
|
299
|
+
var EXCLUDE_PATH_PATTERNS = [
|
|
300
|
+
/\/_?healthz?$/i,
|
|
301
|
+
/\/_?health[_-]?checks?$/i,
|
|
302
|
+
/\/_?heart[_-]?beats?$/i,
|
|
303
|
+
/\/ping$/i,
|
|
304
|
+
/\/ready$/i,
|
|
305
|
+
/\/live$/i
|
|
306
|
+
];
|
|
307
|
+
var EXCLUDE_USER_AGENT_PATTERNS = [
|
|
308
|
+
/health[-_ ]?check/i,
|
|
309
|
+
/microsoft-azure-application-lb/i,
|
|
310
|
+
/googlehc/i,
|
|
311
|
+
/kube-probe/i
|
|
312
|
+
];
|
|
313
|
+
var MASK_QUERY_PARAM_PATTERNS = [
|
|
314
|
+
/auth/i,
|
|
315
|
+
/api-?key/i,
|
|
316
|
+
/secret/i,
|
|
317
|
+
/token/i,
|
|
318
|
+
/password/i,
|
|
319
|
+
/pwd/i
|
|
320
|
+
];
|
|
321
|
+
var MASK_HEADER_PATTERNS = [
|
|
322
|
+
/auth/i,
|
|
323
|
+
/api-?key/i,
|
|
324
|
+
/secret/i,
|
|
325
|
+
/token/i,
|
|
326
|
+
/cookie/i
|
|
327
|
+
];
|
|
328
|
+
var DEFAULT_CONFIG = {
|
|
329
|
+
enabled: false,
|
|
330
|
+
logQueryParams: true,
|
|
331
|
+
logRequestHeaders: false,
|
|
332
|
+
logRequestBody: false,
|
|
333
|
+
logResponseHeaders: true,
|
|
334
|
+
logResponseBody: false,
|
|
335
|
+
maskQueryParams: [],
|
|
336
|
+
maskHeaders: [],
|
|
337
|
+
excludePaths: []
|
|
338
|
+
};
|
|
339
|
+
var _RequestLogger = class _RequestLogger {
|
|
340
|
+
config;
|
|
341
|
+
enabled;
|
|
342
|
+
suspendUntil = null;
|
|
343
|
+
pendingWrites = [];
|
|
344
|
+
currentFile = null;
|
|
345
|
+
files = [];
|
|
346
|
+
maintainIntervalId;
|
|
347
|
+
constructor(config) {
|
|
348
|
+
this.config = {
|
|
349
|
+
...DEFAULT_CONFIG,
|
|
350
|
+
...config
|
|
351
|
+
};
|
|
352
|
+
this.enabled = this.config.enabled && checkWritableFs();
|
|
353
|
+
if (this.enabled) {
|
|
354
|
+
this.maintainIntervalId = setInterval(() => {
|
|
355
|
+
this.maintain();
|
|
356
|
+
}, 1e3);
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
shouldExcludePath(urlPath) {
|
|
360
|
+
const patterns = [
|
|
361
|
+
...this.config.excludePaths,
|
|
362
|
+
...EXCLUDE_PATH_PATTERNS
|
|
363
|
+
];
|
|
364
|
+
return matchPatterns(urlPath, patterns);
|
|
365
|
+
}
|
|
366
|
+
shouldExcludeUserAgent(userAgent) {
|
|
367
|
+
return userAgent ? matchPatterns(userAgent, EXCLUDE_USER_AGENT_PATTERNS) : false;
|
|
368
|
+
}
|
|
369
|
+
shouldMaskQueryParam(name) {
|
|
370
|
+
const patterns = [
|
|
371
|
+
...this.config.maskQueryParams,
|
|
372
|
+
...MASK_QUERY_PARAM_PATTERNS
|
|
373
|
+
];
|
|
374
|
+
return matchPatterns(name, patterns);
|
|
375
|
+
}
|
|
376
|
+
shouldMaskHeader(name) {
|
|
377
|
+
const patterns = [
|
|
378
|
+
...this.config.maskHeaders,
|
|
379
|
+
...MASK_HEADER_PATTERNS
|
|
380
|
+
];
|
|
381
|
+
return matchPatterns(name, patterns);
|
|
382
|
+
}
|
|
383
|
+
hasSupportedContentType(headers) {
|
|
384
|
+
var _a2;
|
|
385
|
+
const contentType = (_a2 = headers.find(([k]) => k.toLowerCase() === "content-type")) == null ? void 0 : _a2[1];
|
|
386
|
+
return contentType !== void 0 && ALLOWED_CONTENT_TYPES.some((t) => contentType.startsWith(t));
|
|
387
|
+
}
|
|
388
|
+
maskQueryParams(search) {
|
|
389
|
+
const params = new URLSearchParams(search);
|
|
390
|
+
for (const [key] of params) {
|
|
391
|
+
if (this.shouldMaskQueryParam(key)) {
|
|
392
|
+
params.set(key, MASKED);
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
return params.toString();
|
|
396
|
+
}
|
|
397
|
+
maskHeaders(headers) {
|
|
398
|
+
return headers.map(([k, v]) => [
|
|
399
|
+
k,
|
|
400
|
+
this.shouldMaskHeader(k) ? MASKED : v
|
|
401
|
+
]);
|
|
402
|
+
}
|
|
403
|
+
logRequest(request, response) {
|
|
404
|
+
var _a2, _b, _c;
|
|
405
|
+
if (!this.enabled || this.suspendUntil !== null) return;
|
|
406
|
+
const url = new URL(request.url);
|
|
407
|
+
const path = request.path ?? url.pathname;
|
|
408
|
+
const userAgent = (_a2 = request.headers.find(([k]) => k.toLowerCase() === "user-agent")) == null ? void 0 : _a2[1];
|
|
409
|
+
if (this.shouldExcludePath(path) || this.shouldExcludeUserAgent(userAgent) || (((_c = (_b = this.config).excludeCallback) == null ? void 0 : _c.call(_b, request, response)) ?? false)) {
|
|
410
|
+
return;
|
|
411
|
+
}
|
|
412
|
+
url.search = this.config.logQueryParams ? this.maskQueryParams(url.search) : "";
|
|
413
|
+
request.url = url.toString();
|
|
414
|
+
request.headers = this.config.logRequestHeaders ? this.maskHeaders(request.headers) : [];
|
|
415
|
+
response.headers = this.config.logResponseHeaders ? this.maskHeaders(response.headers) : [];
|
|
416
|
+
if (!this.config.logRequestBody || !this.hasSupportedContentType(request.headers)) {
|
|
417
|
+
request.body = void 0;
|
|
418
|
+
} else if (request.body) {
|
|
419
|
+
if (request.body.length > MAX_BODY_SIZE) {
|
|
420
|
+
request.body = BODY_TOO_LARGE;
|
|
421
|
+
} else if (this.config.maskRequestBodyCallback) {
|
|
422
|
+
try {
|
|
423
|
+
request.body = this.config.maskRequestBodyCallback(request) ?? BODY_MASKED;
|
|
424
|
+
if (request.body.length > MAX_BODY_SIZE) {
|
|
425
|
+
request.body = BODY_TOO_LARGE;
|
|
426
|
+
}
|
|
427
|
+
} catch {
|
|
428
|
+
request.body = void 0;
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
if (!this.config.logResponseBody || !this.hasSupportedContentType(response.headers)) {
|
|
433
|
+
response.body = void 0;
|
|
434
|
+
} else if (response.body) {
|
|
435
|
+
if (response.body.length > MAX_BODY_SIZE) {
|
|
436
|
+
response.body = BODY_TOO_LARGE;
|
|
437
|
+
} else if (this.config.maskResponseBodyCallback) {
|
|
438
|
+
try {
|
|
439
|
+
response.body = this.config.maskResponseBodyCallback(request, response) ?? BODY_MASKED;
|
|
440
|
+
if (response.body.length > MAX_BODY_SIZE) {
|
|
441
|
+
response.body = BODY_TOO_LARGE;
|
|
442
|
+
}
|
|
443
|
+
} catch {
|
|
444
|
+
response.body = void 0;
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
const item = {
|
|
449
|
+
uuid: (0, import_crypto2.randomUUID)(),
|
|
450
|
+
request: skipEmptyValues(request),
|
|
451
|
+
response: skipEmptyValues(response)
|
|
452
|
+
};
|
|
453
|
+
[
|
|
454
|
+
item.request.body,
|
|
455
|
+
item.response.body
|
|
456
|
+
].forEach((body) => {
|
|
457
|
+
if (body) {
|
|
458
|
+
body.toJSON = function() {
|
|
459
|
+
return this.toString("base64");
|
|
460
|
+
};
|
|
461
|
+
}
|
|
462
|
+
});
|
|
463
|
+
this.pendingWrites.push(JSON.stringify(item));
|
|
464
|
+
if (this.pendingWrites.length > MAX_PENDING_WRITES) {
|
|
465
|
+
this.pendingWrites.shift();
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
async writeToFile() {
|
|
469
|
+
if (!this.enabled || this.pendingWrites.length === 0) {
|
|
470
|
+
return;
|
|
471
|
+
}
|
|
472
|
+
if (!this.currentFile) {
|
|
473
|
+
this.currentFile = new TempGzipFile();
|
|
474
|
+
}
|
|
475
|
+
while (this.pendingWrites.length > 0) {
|
|
476
|
+
const item = this.pendingWrites.shift();
|
|
477
|
+
if (item) {
|
|
478
|
+
await this.currentFile.writeLine(import_buffer2.Buffer.from(item));
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
getFile() {
|
|
483
|
+
return this.files.shift();
|
|
484
|
+
}
|
|
485
|
+
retryFileLater(file) {
|
|
486
|
+
this.files.unshift(file);
|
|
487
|
+
}
|
|
488
|
+
async rotateFile() {
|
|
489
|
+
if (this.currentFile) {
|
|
490
|
+
await this.currentFile.close();
|
|
491
|
+
this.files.push(this.currentFile);
|
|
492
|
+
this.currentFile = null;
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
async maintain() {
|
|
496
|
+
await this.writeToFile();
|
|
497
|
+
if (this.currentFile && this.currentFile.size > MAX_FILE_SIZE) {
|
|
498
|
+
await this.rotateFile();
|
|
499
|
+
}
|
|
500
|
+
while (this.files.length > MAX_FILES) {
|
|
501
|
+
const file = this.files.shift();
|
|
502
|
+
file == null ? void 0 : file.delete();
|
|
503
|
+
}
|
|
504
|
+
if (this.suspendUntil !== null && this.suspendUntil < Date.now()) {
|
|
505
|
+
this.suspendUntil = null;
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
async clear() {
|
|
509
|
+
this.pendingWrites = [];
|
|
510
|
+
await this.rotateFile();
|
|
511
|
+
this.files.forEach((file) => {
|
|
512
|
+
file.delete();
|
|
513
|
+
});
|
|
514
|
+
this.files = [];
|
|
515
|
+
}
|
|
516
|
+
async close() {
|
|
517
|
+
this.enabled = false;
|
|
518
|
+
await this.clear();
|
|
519
|
+
if (this.maintainIntervalId) {
|
|
520
|
+
clearInterval(this.maintainIntervalId);
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
};
|
|
524
|
+
__name(_RequestLogger, "RequestLogger");
|
|
525
|
+
var RequestLogger = _RequestLogger;
|
|
526
|
+
function convertHeaders(headers) {
|
|
527
|
+
if (headers instanceof Headers) {
|
|
528
|
+
return Array.from(headers.entries());
|
|
529
|
+
}
|
|
530
|
+
return Object.entries(headers).flatMap(([key, value]) => {
|
|
531
|
+
if (value === void 0) {
|
|
532
|
+
return [];
|
|
533
|
+
}
|
|
534
|
+
if (Array.isArray(value)) {
|
|
535
|
+
return value.map((v) => [
|
|
536
|
+
key,
|
|
537
|
+
v
|
|
538
|
+
]);
|
|
539
|
+
}
|
|
540
|
+
return [
|
|
541
|
+
[
|
|
542
|
+
key,
|
|
543
|
+
value.toString()
|
|
544
|
+
]
|
|
545
|
+
];
|
|
546
|
+
});
|
|
547
|
+
}
|
|
548
|
+
__name(convertHeaders, "convertHeaders");
|
|
549
|
+
function matchPatterns(value, patterns) {
|
|
550
|
+
return patterns.some((pattern) => {
|
|
551
|
+
return pattern.test(value);
|
|
552
|
+
});
|
|
553
|
+
}
|
|
554
|
+
__name(matchPatterns, "matchPatterns");
|
|
555
|
+
function skipEmptyValues(data) {
|
|
556
|
+
return Object.fromEntries(Object.entries(data).filter(([_, v]) => {
|
|
557
|
+
if (v == null || Number.isNaN(v)) return false;
|
|
558
|
+
if (Array.isArray(v) || import_buffer2.Buffer.isBuffer(v) || typeof v === "string") {
|
|
559
|
+
return v.length > 0;
|
|
560
|
+
}
|
|
561
|
+
return true;
|
|
562
|
+
}));
|
|
563
|
+
}
|
|
564
|
+
__name(skipEmptyValues, "skipEmptyValues");
|
|
565
|
+
function checkWritableFs() {
|
|
566
|
+
try {
|
|
567
|
+
const testPath = (0, import_path2.join)((0, import_os2.tmpdir)(), `apitally-${(0, import_crypto2.randomUUID)()}`);
|
|
568
|
+
(0, import_fs2.writeFileSync)(testPath, "test");
|
|
569
|
+
(0, import_fs2.unlinkSync)(testPath);
|
|
570
|
+
return true;
|
|
571
|
+
} catch (error) {
|
|
572
|
+
return false;
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
__name(checkWritableFs, "checkWritableFs");
|
|
576
|
+
|
|
577
|
+
// src/common/serverErrorCounter.ts
|
|
578
|
+
var import_crypto3 = require("crypto");
|
|
214
579
|
var MAX_MSG_LENGTH = 2048;
|
|
215
580
|
var MAX_STACKTRACE_LENGTH = 65536;
|
|
216
581
|
var _ServerErrorCounter = class _ServerErrorCounter {
|
|
@@ -262,7 +627,7 @@ var _ServerErrorCounter = class _ServerErrorCounter {
|
|
|
262
627
|
serverError.msg.trim(),
|
|
263
628
|
serverError.traceback.trim()
|
|
264
629
|
].join("|");
|
|
265
|
-
return (0,
|
|
630
|
+
return (0, import_crypto3.createHash)("md5").update(hashInput).digest("hex");
|
|
266
631
|
}
|
|
267
632
|
getTruncatedMessage(msg) {
|
|
268
633
|
msg = msg.trim();
|
|
@@ -308,7 +673,7 @@ __name(_ServerErrorCounter, "ServerErrorCounter");
|
|
|
308
673
|
var ServerErrorCounter = _ServerErrorCounter;
|
|
309
674
|
|
|
310
675
|
// src/common/validationErrorCounter.ts
|
|
311
|
-
var
|
|
676
|
+
var import_crypto4 = require("crypto");
|
|
312
677
|
var _ValidationErrorCounter = class _ValidationErrorCounter {
|
|
313
678
|
errorCounts;
|
|
314
679
|
errorDetails;
|
|
@@ -352,7 +717,7 @@ var _ValidationErrorCounter = class _ValidationErrorCounter {
|
|
|
352
717
|
validationError.msg.trim(),
|
|
353
718
|
validationError.type
|
|
354
719
|
].join("|");
|
|
355
|
-
return (0,
|
|
720
|
+
return (0, import_crypto4.createHash)("md5").update(hashInput).digest("hex");
|
|
356
721
|
}
|
|
357
722
|
};
|
|
358
723
|
__name(_ValidationErrorCounter, "ValidationErrorCounter");
|
|
@@ -381,11 +746,12 @@ var _ApitallyClient = class _ApitallyClient {
|
|
|
381
746
|
startupData;
|
|
382
747
|
startupDataSent = false;
|
|
383
748
|
requestCounter;
|
|
749
|
+
requestLogger;
|
|
384
750
|
validationErrorCounter;
|
|
385
751
|
serverErrorCounter;
|
|
386
752
|
consumerRegistry;
|
|
387
753
|
logger;
|
|
388
|
-
constructor({ clientId, env = "dev", logger }) {
|
|
754
|
+
constructor({ clientId, env = "dev", requestLoggingConfig, logger }) {
|
|
389
755
|
if (_ApitallyClient.instance) {
|
|
390
756
|
throw new Error("Apitally client is already initialized");
|
|
391
757
|
}
|
|
@@ -398,9 +764,10 @@ var _ApitallyClient = class _ApitallyClient {
|
|
|
398
764
|
_ApitallyClient.instance = this;
|
|
399
765
|
this.clientId = clientId;
|
|
400
766
|
this.env = env;
|
|
401
|
-
this.instanceUuid = (0,
|
|
767
|
+
this.instanceUuid = (0, import_crypto5.randomUUID)();
|
|
402
768
|
this.syncDataQueue = [];
|
|
403
769
|
this.requestCounter = new RequestCounter();
|
|
770
|
+
this.requestLogger = new RequestLogger(requestLoggingConfig);
|
|
404
771
|
this.validationErrorCounter = new ValidationErrorCounter();
|
|
405
772
|
this.serverErrorCounter = new ServerErrorCounter();
|
|
406
773
|
this.consumerRegistry = new ConsumerRegistry();
|
|
@@ -422,6 +789,8 @@ var _ApitallyClient = class _ApitallyClient {
|
|
|
422
789
|
async handleShutdown() {
|
|
423
790
|
this.stopSync();
|
|
424
791
|
await this.sendSyncData();
|
|
792
|
+
await this.sendLogData();
|
|
793
|
+
await this.requestLogger.close();
|
|
425
794
|
_ApitallyClient.instance = void 0;
|
|
426
795
|
}
|
|
427
796
|
getHubUrlPrefix() {
|
|
@@ -468,7 +837,8 @@ var _ApitallyClient = class _ApitallyClient {
|
|
|
468
837
|
async sync() {
|
|
469
838
|
try {
|
|
470
839
|
const promises = [
|
|
471
|
-
this.sendSyncData()
|
|
840
|
+
this.sendSyncData(),
|
|
841
|
+
this.sendLogData()
|
|
472
842
|
];
|
|
473
843
|
if (!this.startupDataSent) {
|
|
474
844
|
promises.push(this.sendStartupData());
|
|
@@ -496,7 +866,7 @@ var _ApitallyClient = class _ApitallyClient {
|
|
|
496
866
|
this.logger.debug("Sending startup data to Apitally Hub");
|
|
497
867
|
const payload = {
|
|
498
868
|
instance_uuid: this.instanceUuid,
|
|
499
|
-
message_uuid: (0,
|
|
869
|
+
message_uuid: (0, import_crypto5.randomUUID)(),
|
|
500
870
|
...this.startupData
|
|
501
871
|
};
|
|
502
872
|
try {
|
|
@@ -518,7 +888,7 @@ var _ApitallyClient = class _ApitallyClient {
|
|
|
518
888
|
const newPayload = {
|
|
519
889
|
time_offset: 0,
|
|
520
890
|
instance_uuid: this.instanceUuid,
|
|
521
|
-
message_uuid: (0,
|
|
891
|
+
message_uuid: (0, import_crypto5.randomUUID)(),
|
|
522
892
|
requests: this.requestCounter.getAndResetRequests(),
|
|
523
893
|
validation_errors: this.validationErrorCounter.getAndResetValidationErrors(),
|
|
524
894
|
server_errors: this.serverErrorCounter.getAndResetServerErrors(),
|
|
@@ -537,8 +907,7 @@ var _ApitallyClient = class _ApitallyClient {
|
|
|
537
907
|
const timeOffset = Date.now() - time;
|
|
538
908
|
if (timeOffset <= MAX_QUEUE_TIME) {
|
|
539
909
|
if (i > 0) {
|
|
540
|
-
|
|
541
|
-
await new Promise((resolve) => setTimeout(resolve, waitMs));
|
|
910
|
+
await this.randomDelay();
|
|
542
911
|
}
|
|
543
912
|
payload.time_offset = timeOffset / 1e3;
|
|
544
913
|
await this.sendData("sync", payload);
|
|
@@ -557,6 +926,52 @@ var _ApitallyClient = class _ApitallyClient {
|
|
|
557
926
|
}
|
|
558
927
|
}
|
|
559
928
|
}
|
|
929
|
+
async sendLogData() {
|
|
930
|
+
this.logger.debug("Sending request log data to Apitally Hub");
|
|
931
|
+
await this.requestLogger.rotateFile();
|
|
932
|
+
const fetchWithRetry = (0, import_fetch_retry.default)(fetch, {
|
|
933
|
+
retries: 3,
|
|
934
|
+
retryDelay: 1e3,
|
|
935
|
+
retryOn: [
|
|
936
|
+
408,
|
|
937
|
+
429,
|
|
938
|
+
500,
|
|
939
|
+
502,
|
|
940
|
+
503,
|
|
941
|
+
504
|
|
942
|
+
]
|
|
943
|
+
});
|
|
944
|
+
let i = 0;
|
|
945
|
+
let logFile;
|
|
946
|
+
while (logFile = this.requestLogger.getFile()) {
|
|
947
|
+
if (i > 0) {
|
|
948
|
+
await this.randomDelay();
|
|
949
|
+
}
|
|
950
|
+
try {
|
|
951
|
+
const response = await fetchWithRetry(`${this.getHubUrlPrefix()}log?uuid=${logFile.uuid}`, {
|
|
952
|
+
method: "POST",
|
|
953
|
+
body: await logFile.getContent()
|
|
954
|
+
});
|
|
955
|
+
if (response.status === 402 && response.headers.has("Retry-After")) {
|
|
956
|
+
const retryAfter = parseInt(response.headers.get("Retry-After") ?? "0");
|
|
957
|
+
if (retryAfter > 0) {
|
|
958
|
+
this.requestLogger.suspendUntil = Date.now() + retryAfter * 1e3;
|
|
959
|
+
this.requestLogger.clear();
|
|
960
|
+
return;
|
|
961
|
+
}
|
|
962
|
+
}
|
|
963
|
+
if (!response.ok) {
|
|
964
|
+
throw new HTTPError(response);
|
|
965
|
+
}
|
|
966
|
+
logFile.delete();
|
|
967
|
+
} catch (error) {
|
|
968
|
+
this.requestLogger.retryFileLater(logFile);
|
|
969
|
+
break;
|
|
970
|
+
}
|
|
971
|
+
i++;
|
|
972
|
+
if (i >= 10) break;
|
|
973
|
+
}
|
|
974
|
+
}
|
|
560
975
|
handleHubError(error) {
|
|
561
976
|
if (error instanceof HTTPError) {
|
|
562
977
|
if (error.response.status === 404) {
|
|
@@ -571,6 +986,10 @@ var _ApitallyClient = class _ApitallyClient {
|
|
|
571
986
|
}
|
|
572
987
|
return false;
|
|
573
988
|
}
|
|
989
|
+
async randomDelay() {
|
|
990
|
+
const delay = 100 + Math.random() * 200;
|
|
991
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
992
|
+
}
|
|
574
993
|
};
|
|
575
994
|
__name(_ApitallyClient, "ApitallyClient");
|
|
576
995
|
__publicField(_ApitallyClient, "instance");
|
|
@@ -604,13 +1023,13 @@ var useApitally = /* @__PURE__ */ __name((app, config) => {
|
|
|
604
1023
|
}, 1e3);
|
|
605
1024
|
}, "useApitally");
|
|
606
1025
|
var getMiddleware = /* @__PURE__ */ __name((client) => {
|
|
607
|
-
const zodInstalled = getPackageVersion("zod") !== null;
|
|
608
1026
|
return async (c, next) => {
|
|
609
1027
|
const startTime = import_perf_hooks.performance.now();
|
|
610
1028
|
await next();
|
|
611
1029
|
let response;
|
|
612
1030
|
const responseTime = import_perf_hooks.performance.now() - startTime;
|
|
613
1031
|
const [responseSize, newResponse] = await measureResponseSize(c.res);
|
|
1032
|
+
const requestSize = c.req.header("Content-Length");
|
|
614
1033
|
const consumer = getConsumer(c);
|
|
615
1034
|
client.consumerRegistry.addOrUpdateConsumer(consumer);
|
|
616
1035
|
client.requestCounter.addRequest({
|
|
@@ -619,11 +1038,11 @@ var getMiddleware = /* @__PURE__ */ __name((client) => {
|
|
|
619
1038
|
path: c.req.routePath,
|
|
620
1039
|
statusCode: c.res.status,
|
|
621
1040
|
responseTime,
|
|
622
|
-
requestSize
|
|
1041
|
+
requestSize,
|
|
623
1042
|
responseSize
|
|
624
1043
|
});
|
|
625
1044
|
response = newResponse;
|
|
626
|
-
if (c.res.status === 400
|
|
1045
|
+
if (c.res.status === 400) {
|
|
627
1046
|
const [responseJson, newResponse2] = await getResponseJson(response);
|
|
628
1047
|
const validationErrors = extractZodErrors(responseJson);
|
|
629
1048
|
validationErrors.forEach((error) => {
|
|
@@ -646,6 +1065,34 @@ var getMiddleware = /* @__PURE__ */ __name((client) => {
|
|
|
646
1065
|
traceback: c.error.stack || ""
|
|
647
1066
|
});
|
|
648
1067
|
}
|
|
1068
|
+
if (client.requestLogger.enabled) {
|
|
1069
|
+
let requestBody;
|
|
1070
|
+
let responseBody;
|
|
1071
|
+
let newResponse2 = response;
|
|
1072
|
+
if (client.requestLogger.config.logRequestBody) {
|
|
1073
|
+
requestBody = Buffer.from(await c.req.arrayBuffer());
|
|
1074
|
+
}
|
|
1075
|
+
if (client.requestLogger.config.logResponseBody) {
|
|
1076
|
+
[responseBody, newResponse2] = await getResponseBody(response);
|
|
1077
|
+
response = newResponse2;
|
|
1078
|
+
}
|
|
1079
|
+
client.requestLogger.logRequest({
|
|
1080
|
+
timestamp: Date.now() / 1e3,
|
|
1081
|
+
method: c.req.method,
|
|
1082
|
+
path: c.req.routePath,
|
|
1083
|
+
url: c.req.url,
|
|
1084
|
+
headers: convertHeaders(c.req.header()),
|
|
1085
|
+
size: Number(requestSize),
|
|
1086
|
+
consumer: consumer == null ? void 0 : consumer.identifier,
|
|
1087
|
+
body: requestBody
|
|
1088
|
+
}, {
|
|
1089
|
+
statusCode: c.res.status,
|
|
1090
|
+
responseTime: responseTime / 1e3,
|
|
1091
|
+
headers: convertHeaders(c.res.headers),
|
|
1092
|
+
size: responseSize,
|
|
1093
|
+
body: responseBody
|
|
1094
|
+
});
|
|
1095
|
+
}
|
|
649
1096
|
c.res = response;
|
|
650
1097
|
};
|
|
651
1098
|
}, "getMiddleware");
|
|
@@ -675,6 +1122,14 @@ var measureResponseSize = /* @__PURE__ */ __name(async (response) => {
|
|
|
675
1122
|
newResponse1
|
|
676
1123
|
];
|
|
677
1124
|
}, "measureResponseSize");
|
|
1125
|
+
var getResponseBody = /* @__PURE__ */ __name(async (response) => {
|
|
1126
|
+
const [newResponse1, newResponse2] = await teeResponse(response);
|
|
1127
|
+
const responseBuffer = Buffer.from(await newResponse2.arrayBuffer());
|
|
1128
|
+
return [
|
|
1129
|
+
responseBuffer,
|
|
1130
|
+
newResponse1
|
|
1131
|
+
];
|
|
1132
|
+
}, "getResponseBody");
|
|
678
1133
|
var getResponseJson = /* @__PURE__ */ __name(async (response) => {
|
|
679
1134
|
const contentType = response.headers.get("content-type");
|
|
680
1135
|
if (contentType && contentType.includes("application/json")) {
|