apitally 0.9.5 → 0.11.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 +1 -2
- package/dist/common/client.cjs +398 -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 +394 -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 +419 -0
- package/dist/common/requestLogger.cjs.map +1 -0
- package/dist/common/requestLogger.d.cts +63 -0
- package/dist/common/requestLogger.d.ts +63 -0
- package/dist/common/requestLogger.js +395 -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 +500 -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 +496 -30
- package/dist/express/index.js.map +1 -1
- package/dist/express/middleware.cjs +500 -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 +496 -30
- package/dist/express/middleware.js.map +1 -1
- package/dist/fastify/index.cjs +491 -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 +487 -23
- package/dist/fastify/index.js.map +1 -1
- package/dist/fastify/plugin.cjs +491 -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 +487 -23
- package/dist/fastify/plugin.js.map +1 -1
- package/dist/hono/index.cjs +460 -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 +456 -11
- package/dist/hono/index.js.map +1 -1
- package/dist/hono/middleware.cjs +460 -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 +456 -11
- package/dist/hono/middleware.js.map +1 -1
- package/dist/koa/index.cjs +475 -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 +471 -11
- package/dist/koa/index.js.map +1 -1
- package/dist/koa/middleware.cjs +475 -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 +471 -11
- package/dist/koa/middleware.js.map +1 -1
- package/dist/nestjs/index.cjs +536 -41
- package/dist/nestjs/index.cjs.map +1 -1
- package/dist/nestjs/index.d.cts +10 -2
- package/dist/nestjs/index.d.ts +10 -2
- package/dist/nestjs/index.js +534 -37
- package/dist/nestjs/index.js.map +1 -1
- package/package.json +8 -2
package/dist/koa/index.cjs
CHANGED
|
@@ -38,7 +38,7 @@ __export(koa_exports, {
|
|
|
38
38
|
module.exports = __toCommonJS(koa_exports);
|
|
39
39
|
|
|
40
40
|
// src/common/client.ts
|
|
41
|
-
var
|
|
41
|
+
var import_crypto5 = require("crypto");
|
|
42
42
|
var import_fetch_retry = __toESM(require("fetch-retry"), 1);
|
|
43
43
|
|
|
44
44
|
// src/common/consumerRegistry.ts
|
|
@@ -207,8 +207,395 @@ var _RequestCounter = class _RequestCounter {
|
|
|
207
207
|
__name(_RequestCounter, "RequestCounter");
|
|
208
208
|
var RequestCounter = _RequestCounter;
|
|
209
209
|
|
|
210
|
-
// src/common/
|
|
210
|
+
// src/common/requestLogger.ts
|
|
211
|
+
var import_buffer2 = require("buffer");
|
|
212
|
+
var import_crypto2 = require("crypto");
|
|
213
|
+
var import_fs2 = require("fs");
|
|
214
|
+
var import_os2 = require("os");
|
|
215
|
+
var import_path2 = require("path");
|
|
216
|
+
|
|
217
|
+
// src/common/tempGzipFile.ts
|
|
218
|
+
var import_buffer = require("buffer");
|
|
211
219
|
var import_crypto = require("crypto");
|
|
220
|
+
var import_fs = require("fs");
|
|
221
|
+
var import_os = require("os");
|
|
222
|
+
var import_path = require("path");
|
|
223
|
+
var import_zlib = require("zlib");
|
|
224
|
+
var _TempGzipFile = class _TempGzipFile {
|
|
225
|
+
uuid;
|
|
226
|
+
filePath;
|
|
227
|
+
gzip;
|
|
228
|
+
writeStream;
|
|
229
|
+
readyPromise;
|
|
230
|
+
constructor() {
|
|
231
|
+
this.uuid = (0, import_crypto.randomUUID)();
|
|
232
|
+
this.filePath = (0, import_path.join)((0, import_os.tmpdir)(), `apitally-${this.uuid}.gz`);
|
|
233
|
+
this.writeStream = (0, import_fs.createWriteStream)(this.filePath);
|
|
234
|
+
this.readyPromise = new Promise((resolve, reject) => {
|
|
235
|
+
this.writeStream.once("ready", resolve);
|
|
236
|
+
this.writeStream.once("error", reject);
|
|
237
|
+
});
|
|
238
|
+
this.gzip = (0, import_zlib.createGzip)();
|
|
239
|
+
this.gzip.pipe(this.writeStream);
|
|
240
|
+
}
|
|
241
|
+
get size() {
|
|
242
|
+
return this.writeStream.bytesWritten;
|
|
243
|
+
}
|
|
244
|
+
async writeLine(data) {
|
|
245
|
+
await this.readyPromise;
|
|
246
|
+
return new Promise((resolve, reject) => {
|
|
247
|
+
this.gzip.write(import_buffer.Buffer.concat([
|
|
248
|
+
data,
|
|
249
|
+
import_buffer.Buffer.from("\n")
|
|
250
|
+
]), (error) => {
|
|
251
|
+
if (error) {
|
|
252
|
+
reject(error);
|
|
253
|
+
} else {
|
|
254
|
+
resolve();
|
|
255
|
+
}
|
|
256
|
+
});
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
async getContent() {
|
|
260
|
+
return new Promise((resolve, reject) => {
|
|
261
|
+
(0, import_fs.readFile)(this.filePath, (error, data) => {
|
|
262
|
+
if (error) {
|
|
263
|
+
reject(error);
|
|
264
|
+
} else {
|
|
265
|
+
resolve(data);
|
|
266
|
+
}
|
|
267
|
+
});
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
async close() {
|
|
271
|
+
return new Promise((resolve) => {
|
|
272
|
+
this.gzip.end(() => {
|
|
273
|
+
resolve();
|
|
274
|
+
});
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
async delete() {
|
|
278
|
+
await this.close();
|
|
279
|
+
(0, import_fs.unlinkSync)(this.filePath);
|
|
280
|
+
}
|
|
281
|
+
};
|
|
282
|
+
__name(_TempGzipFile, "TempGzipFile");
|
|
283
|
+
var TempGzipFile = _TempGzipFile;
|
|
284
|
+
|
|
285
|
+
// src/common/requestLogger.ts
|
|
286
|
+
var MAX_BODY_SIZE = 5e4;
|
|
287
|
+
var MAX_FILE_SIZE = 1e6;
|
|
288
|
+
var MAX_FILES = 50;
|
|
289
|
+
var MAX_PENDING_WRITES = 100;
|
|
290
|
+
var BODY_TOO_LARGE = import_buffer2.Buffer.from("<body too large>");
|
|
291
|
+
var BODY_MASKED = import_buffer2.Buffer.from("<masked>");
|
|
292
|
+
var MASKED = "******";
|
|
293
|
+
var ALLOWED_CONTENT_TYPES = [
|
|
294
|
+
"application/json",
|
|
295
|
+
"text/plain"
|
|
296
|
+
];
|
|
297
|
+
var EXCLUDE_PATH_PATTERNS = [
|
|
298
|
+
/\/_?healthz?$/,
|
|
299
|
+
/\/_?health[_-]?checks?$/,
|
|
300
|
+
/\/_?heart[_-]?beats?$/,
|
|
301
|
+
/\/ping$/,
|
|
302
|
+
/\/ready$/,
|
|
303
|
+
/\/live$/
|
|
304
|
+
];
|
|
305
|
+
var MASK_QUERY_PARAM_PATTERNS = [
|
|
306
|
+
/auth/i,
|
|
307
|
+
/api-?key/i,
|
|
308
|
+
/secret/i,
|
|
309
|
+
/token/i,
|
|
310
|
+
/password/i,
|
|
311
|
+
/pwd/i
|
|
312
|
+
];
|
|
313
|
+
var MASK_HEADER_PATTERNS = [
|
|
314
|
+
/auth/i,
|
|
315
|
+
/api-?key/i,
|
|
316
|
+
/secret/i,
|
|
317
|
+
/token/i,
|
|
318
|
+
/cookie/i
|
|
319
|
+
];
|
|
320
|
+
var DEFAULT_CONFIG = {
|
|
321
|
+
enabled: false,
|
|
322
|
+
logQueryParams: true,
|
|
323
|
+
logRequestHeaders: false,
|
|
324
|
+
logRequestBody: false,
|
|
325
|
+
logResponseHeaders: true,
|
|
326
|
+
logResponseBody: false,
|
|
327
|
+
maskQueryParams: [],
|
|
328
|
+
maskHeaders: [],
|
|
329
|
+
excludePaths: []
|
|
330
|
+
};
|
|
331
|
+
var _RequestLogger = class _RequestLogger {
|
|
332
|
+
config;
|
|
333
|
+
enabled;
|
|
334
|
+
suspendUntil = null;
|
|
335
|
+
pendingWrites = [];
|
|
336
|
+
currentFile = null;
|
|
337
|
+
files = [];
|
|
338
|
+
maintainIntervalId;
|
|
339
|
+
constructor(config) {
|
|
340
|
+
this.config = {
|
|
341
|
+
...DEFAULT_CONFIG,
|
|
342
|
+
...config
|
|
343
|
+
};
|
|
344
|
+
this.enabled = this.config.enabled && checkWritableFs();
|
|
345
|
+
if (this.enabled) {
|
|
346
|
+
this.maintainIntervalId = setInterval(() => {
|
|
347
|
+
this.maintain();
|
|
348
|
+
}, 1e3);
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
shouldExcludePath(urlPath) {
|
|
352
|
+
const patterns = [
|
|
353
|
+
...this.config.excludePaths,
|
|
354
|
+
...EXCLUDE_PATH_PATTERNS
|
|
355
|
+
];
|
|
356
|
+
return matchPatterns(urlPath, patterns);
|
|
357
|
+
}
|
|
358
|
+
shouldMaskQueryParam(name) {
|
|
359
|
+
const patterns = [
|
|
360
|
+
...this.config.maskQueryParams,
|
|
361
|
+
...MASK_QUERY_PARAM_PATTERNS
|
|
362
|
+
];
|
|
363
|
+
return matchPatterns(name, patterns);
|
|
364
|
+
}
|
|
365
|
+
shouldMaskHeader(name) {
|
|
366
|
+
const patterns = [
|
|
367
|
+
...this.config.maskHeaders,
|
|
368
|
+
...MASK_HEADER_PATTERNS
|
|
369
|
+
];
|
|
370
|
+
return matchPatterns(name, patterns);
|
|
371
|
+
}
|
|
372
|
+
hasSupportedContentType(headers) {
|
|
373
|
+
var _a2;
|
|
374
|
+
const contentType = (_a2 = headers.find(([k]) => k.toLowerCase() === "content-type")) == null ? void 0 : _a2[1];
|
|
375
|
+
return contentType !== void 0 && ALLOWED_CONTENT_TYPES.some((t) => contentType.startsWith(t));
|
|
376
|
+
}
|
|
377
|
+
maskQueryParams(search) {
|
|
378
|
+
const params = new URLSearchParams(search);
|
|
379
|
+
for (const [key] of params) {
|
|
380
|
+
if (this.shouldMaskQueryParam(key)) {
|
|
381
|
+
params.set(key, MASKED);
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
return params.toString();
|
|
385
|
+
}
|
|
386
|
+
maskHeaders(headers) {
|
|
387
|
+
return headers.map(([k, v]) => [
|
|
388
|
+
k,
|
|
389
|
+
this.shouldMaskHeader(k) ? MASKED : v
|
|
390
|
+
]);
|
|
391
|
+
}
|
|
392
|
+
logRequest(request, response) {
|
|
393
|
+
var _a2, _b;
|
|
394
|
+
if (!this.enabled || this.suspendUntil !== null) return;
|
|
395
|
+
const url = new URL(request.url);
|
|
396
|
+
const path = request.path ?? url.pathname;
|
|
397
|
+
if (this.shouldExcludePath(path) || (((_b = (_a2 = this.config).excludeCallback) == null ? void 0 : _b.call(_a2, request, response)) ?? false)) {
|
|
398
|
+
return;
|
|
399
|
+
}
|
|
400
|
+
url.search = this.config.logQueryParams ? this.maskQueryParams(url.search) : "";
|
|
401
|
+
request.url = url.toString();
|
|
402
|
+
request.headers = this.config.logRequestHeaders ? this.maskHeaders(request.headers) : [];
|
|
403
|
+
response.headers = this.config.logResponseHeaders ? this.maskHeaders(response.headers) : [];
|
|
404
|
+
if (!this.config.logRequestBody || !this.hasSupportedContentType(request.headers)) {
|
|
405
|
+
request.body = void 0;
|
|
406
|
+
} else if (request.body) {
|
|
407
|
+
if (request.body.length > MAX_BODY_SIZE) {
|
|
408
|
+
request.body = BODY_TOO_LARGE;
|
|
409
|
+
} else if (this.config.maskRequestBodyCallback) {
|
|
410
|
+
try {
|
|
411
|
+
request.body = this.config.maskRequestBodyCallback(request) ?? BODY_MASKED;
|
|
412
|
+
if (request.body.length > MAX_BODY_SIZE) {
|
|
413
|
+
request.body = BODY_TOO_LARGE;
|
|
414
|
+
}
|
|
415
|
+
} catch {
|
|
416
|
+
request.body = void 0;
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
if (!this.config.logResponseBody || !this.hasSupportedContentType(response.headers)) {
|
|
421
|
+
response.body = void 0;
|
|
422
|
+
} else if (response.body) {
|
|
423
|
+
if (response.body.length > MAX_BODY_SIZE) {
|
|
424
|
+
response.body = BODY_TOO_LARGE;
|
|
425
|
+
} else if (this.config.maskResponseBodyCallback) {
|
|
426
|
+
try {
|
|
427
|
+
response.body = this.config.maskResponseBodyCallback(request, response) ?? BODY_MASKED;
|
|
428
|
+
if (response.body.length > MAX_BODY_SIZE) {
|
|
429
|
+
response.body = BODY_TOO_LARGE;
|
|
430
|
+
}
|
|
431
|
+
} catch {
|
|
432
|
+
response.body = void 0;
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
const item = {
|
|
437
|
+
uuid: (0, import_crypto2.randomUUID)(),
|
|
438
|
+
request: skipEmptyValues(request),
|
|
439
|
+
response: skipEmptyValues(response)
|
|
440
|
+
};
|
|
441
|
+
[
|
|
442
|
+
item.request.body,
|
|
443
|
+
item.response.body
|
|
444
|
+
].forEach((body) => {
|
|
445
|
+
if (body) {
|
|
446
|
+
body.toJSON = function() {
|
|
447
|
+
return this.toString("base64");
|
|
448
|
+
};
|
|
449
|
+
}
|
|
450
|
+
});
|
|
451
|
+
this.pendingWrites.push(JSON.stringify(item));
|
|
452
|
+
if (this.pendingWrites.length > MAX_PENDING_WRITES) {
|
|
453
|
+
this.pendingWrites.shift();
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
async writeToFile() {
|
|
457
|
+
if (!this.enabled || this.pendingWrites.length === 0) {
|
|
458
|
+
return;
|
|
459
|
+
}
|
|
460
|
+
if (!this.currentFile) {
|
|
461
|
+
this.currentFile = new TempGzipFile();
|
|
462
|
+
}
|
|
463
|
+
while (this.pendingWrites.length > 0) {
|
|
464
|
+
const item = this.pendingWrites.shift();
|
|
465
|
+
if (item) {
|
|
466
|
+
await this.currentFile.writeLine(import_buffer2.Buffer.from(item));
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
getFile() {
|
|
471
|
+
return this.files.shift();
|
|
472
|
+
}
|
|
473
|
+
retryFileLater(file) {
|
|
474
|
+
this.files.unshift(file);
|
|
475
|
+
}
|
|
476
|
+
async rotateFile() {
|
|
477
|
+
if (this.currentFile) {
|
|
478
|
+
await this.currentFile.close();
|
|
479
|
+
this.files.push(this.currentFile);
|
|
480
|
+
this.currentFile = null;
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
async maintain() {
|
|
484
|
+
await this.writeToFile();
|
|
485
|
+
if (this.currentFile && this.currentFile.size > MAX_FILE_SIZE) {
|
|
486
|
+
await this.rotateFile();
|
|
487
|
+
}
|
|
488
|
+
while (this.files.length > MAX_FILES) {
|
|
489
|
+
const file = this.files.shift();
|
|
490
|
+
file == null ? void 0 : file.delete();
|
|
491
|
+
}
|
|
492
|
+
if (this.suspendUntil !== null && this.suspendUntil < Date.now()) {
|
|
493
|
+
this.suspendUntil = null;
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
async clear() {
|
|
497
|
+
this.pendingWrites = [];
|
|
498
|
+
await this.rotateFile();
|
|
499
|
+
this.files.forEach((file) => {
|
|
500
|
+
file.delete();
|
|
501
|
+
});
|
|
502
|
+
this.files = [];
|
|
503
|
+
}
|
|
504
|
+
async close() {
|
|
505
|
+
this.enabled = false;
|
|
506
|
+
await this.clear();
|
|
507
|
+
if (this.maintainIntervalId) {
|
|
508
|
+
clearInterval(this.maintainIntervalId);
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
};
|
|
512
|
+
__name(_RequestLogger, "RequestLogger");
|
|
513
|
+
var RequestLogger = _RequestLogger;
|
|
514
|
+
function convertHeaders(headers) {
|
|
515
|
+
if (headers instanceof Headers) {
|
|
516
|
+
return Array.from(headers.entries());
|
|
517
|
+
}
|
|
518
|
+
return Object.entries(headers).flatMap(([key, value]) => {
|
|
519
|
+
if (value === void 0) {
|
|
520
|
+
return [];
|
|
521
|
+
}
|
|
522
|
+
if (Array.isArray(value)) {
|
|
523
|
+
return value.map((v) => [
|
|
524
|
+
key,
|
|
525
|
+
v
|
|
526
|
+
]);
|
|
527
|
+
}
|
|
528
|
+
return [
|
|
529
|
+
[
|
|
530
|
+
key,
|
|
531
|
+
value.toString()
|
|
532
|
+
]
|
|
533
|
+
];
|
|
534
|
+
});
|
|
535
|
+
}
|
|
536
|
+
__name(convertHeaders, "convertHeaders");
|
|
537
|
+
function convertBody(body, contentType) {
|
|
538
|
+
if (!body || !contentType) {
|
|
539
|
+
return;
|
|
540
|
+
}
|
|
541
|
+
try {
|
|
542
|
+
if (contentType.startsWith("application/json")) {
|
|
543
|
+
if (isValidJsonString(body)) {
|
|
544
|
+
return import_buffer2.Buffer.from(body);
|
|
545
|
+
} else {
|
|
546
|
+
return import_buffer2.Buffer.from(JSON.stringify(body));
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
if (contentType.startsWith("text/") && typeof body === "string") {
|
|
550
|
+
return import_buffer2.Buffer.from(body);
|
|
551
|
+
}
|
|
552
|
+
} catch (error) {
|
|
553
|
+
return;
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
__name(convertBody, "convertBody");
|
|
557
|
+
function isValidJsonString(body) {
|
|
558
|
+
if (typeof body !== "string") {
|
|
559
|
+
return false;
|
|
560
|
+
}
|
|
561
|
+
try {
|
|
562
|
+
JSON.parse(body);
|
|
563
|
+
return true;
|
|
564
|
+
} catch {
|
|
565
|
+
return false;
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
__name(isValidJsonString, "isValidJsonString");
|
|
569
|
+
function matchPatterns(value, patterns) {
|
|
570
|
+
return patterns.some((pattern) => {
|
|
571
|
+
return pattern.test(value);
|
|
572
|
+
});
|
|
573
|
+
}
|
|
574
|
+
__name(matchPatterns, "matchPatterns");
|
|
575
|
+
function skipEmptyValues(data) {
|
|
576
|
+
return Object.fromEntries(Object.entries(data).filter(([_, v]) => {
|
|
577
|
+
if (v == null || Number.isNaN(v)) return false;
|
|
578
|
+
if (Array.isArray(v) || import_buffer2.Buffer.isBuffer(v) || typeof v === "string") {
|
|
579
|
+
return v.length > 0;
|
|
580
|
+
}
|
|
581
|
+
return true;
|
|
582
|
+
}));
|
|
583
|
+
}
|
|
584
|
+
__name(skipEmptyValues, "skipEmptyValues");
|
|
585
|
+
function checkWritableFs() {
|
|
586
|
+
try {
|
|
587
|
+
const testPath = (0, import_path2.join)((0, import_os2.tmpdir)(), `apitally-${(0, import_crypto2.randomUUID)()}`);
|
|
588
|
+
(0, import_fs2.writeFileSync)(testPath, "test");
|
|
589
|
+
(0, import_fs2.unlinkSync)(testPath);
|
|
590
|
+
return true;
|
|
591
|
+
} catch (error) {
|
|
592
|
+
return false;
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
__name(checkWritableFs, "checkWritableFs");
|
|
596
|
+
|
|
597
|
+
// src/common/serverErrorCounter.ts
|
|
598
|
+
var import_crypto3 = require("crypto");
|
|
212
599
|
var MAX_MSG_LENGTH = 2048;
|
|
213
600
|
var MAX_STACKTRACE_LENGTH = 65536;
|
|
214
601
|
var _ServerErrorCounter = class _ServerErrorCounter {
|
|
@@ -260,7 +647,7 @@ var _ServerErrorCounter = class _ServerErrorCounter {
|
|
|
260
647
|
serverError.msg.trim(),
|
|
261
648
|
serverError.traceback.trim()
|
|
262
649
|
].join("|");
|
|
263
|
-
return (0,
|
|
650
|
+
return (0, import_crypto3.createHash)("md5").update(hashInput).digest("hex");
|
|
264
651
|
}
|
|
265
652
|
getTruncatedMessage(msg) {
|
|
266
653
|
msg = msg.trim();
|
|
@@ -306,7 +693,7 @@ __name(_ServerErrorCounter, "ServerErrorCounter");
|
|
|
306
693
|
var ServerErrorCounter = _ServerErrorCounter;
|
|
307
694
|
|
|
308
695
|
// src/common/validationErrorCounter.ts
|
|
309
|
-
var
|
|
696
|
+
var import_crypto4 = require("crypto");
|
|
310
697
|
var _ValidationErrorCounter = class _ValidationErrorCounter {
|
|
311
698
|
errorCounts;
|
|
312
699
|
errorDetails;
|
|
@@ -350,7 +737,7 @@ var _ValidationErrorCounter = class _ValidationErrorCounter {
|
|
|
350
737
|
validationError.msg.trim(),
|
|
351
738
|
validationError.type
|
|
352
739
|
].join("|");
|
|
353
|
-
return (0,
|
|
740
|
+
return (0, import_crypto4.createHash)("md5").update(hashInput).digest("hex");
|
|
354
741
|
}
|
|
355
742
|
};
|
|
356
743
|
__name(_ValidationErrorCounter, "ValidationErrorCounter");
|
|
@@ -379,11 +766,12 @@ var _ApitallyClient = class _ApitallyClient {
|
|
|
379
766
|
startupData;
|
|
380
767
|
startupDataSent = false;
|
|
381
768
|
requestCounter;
|
|
769
|
+
requestLogger;
|
|
382
770
|
validationErrorCounter;
|
|
383
771
|
serverErrorCounter;
|
|
384
772
|
consumerRegistry;
|
|
385
773
|
logger;
|
|
386
|
-
constructor({ clientId, env = "dev", logger }) {
|
|
774
|
+
constructor({ clientId, env = "dev", requestLoggingConfig, logger }) {
|
|
387
775
|
if (_ApitallyClient.instance) {
|
|
388
776
|
throw new Error("Apitally client is already initialized");
|
|
389
777
|
}
|
|
@@ -396,9 +784,10 @@ var _ApitallyClient = class _ApitallyClient {
|
|
|
396
784
|
_ApitallyClient.instance = this;
|
|
397
785
|
this.clientId = clientId;
|
|
398
786
|
this.env = env;
|
|
399
|
-
this.instanceUuid = (0,
|
|
787
|
+
this.instanceUuid = (0, import_crypto5.randomUUID)();
|
|
400
788
|
this.syncDataQueue = [];
|
|
401
789
|
this.requestCounter = new RequestCounter();
|
|
790
|
+
this.requestLogger = new RequestLogger(requestLoggingConfig);
|
|
402
791
|
this.validationErrorCounter = new ValidationErrorCounter();
|
|
403
792
|
this.serverErrorCounter = new ServerErrorCounter();
|
|
404
793
|
this.consumerRegistry = new ConsumerRegistry();
|
|
@@ -420,6 +809,8 @@ var _ApitallyClient = class _ApitallyClient {
|
|
|
420
809
|
async handleShutdown() {
|
|
421
810
|
this.stopSync();
|
|
422
811
|
await this.sendSyncData();
|
|
812
|
+
await this.sendLogData();
|
|
813
|
+
await this.requestLogger.close();
|
|
423
814
|
_ApitallyClient.instance = void 0;
|
|
424
815
|
}
|
|
425
816
|
getHubUrlPrefix() {
|
|
@@ -466,7 +857,8 @@ var _ApitallyClient = class _ApitallyClient {
|
|
|
466
857
|
async sync() {
|
|
467
858
|
try {
|
|
468
859
|
const promises = [
|
|
469
|
-
this.sendSyncData()
|
|
860
|
+
this.sendSyncData(),
|
|
861
|
+
this.sendLogData()
|
|
470
862
|
];
|
|
471
863
|
if (!this.startupDataSent) {
|
|
472
864
|
promises.push(this.sendStartupData());
|
|
@@ -494,7 +886,7 @@ var _ApitallyClient = class _ApitallyClient {
|
|
|
494
886
|
this.logger.debug("Sending startup data to Apitally Hub");
|
|
495
887
|
const payload = {
|
|
496
888
|
instance_uuid: this.instanceUuid,
|
|
497
|
-
message_uuid: (0,
|
|
889
|
+
message_uuid: (0, import_crypto5.randomUUID)(),
|
|
498
890
|
...this.startupData
|
|
499
891
|
};
|
|
500
892
|
try {
|
|
@@ -516,7 +908,7 @@ var _ApitallyClient = class _ApitallyClient {
|
|
|
516
908
|
const newPayload = {
|
|
517
909
|
time_offset: 0,
|
|
518
910
|
instance_uuid: this.instanceUuid,
|
|
519
|
-
message_uuid: (0,
|
|
911
|
+
message_uuid: (0, import_crypto5.randomUUID)(),
|
|
520
912
|
requests: this.requestCounter.getAndResetRequests(),
|
|
521
913
|
validation_errors: this.validationErrorCounter.getAndResetValidationErrors(),
|
|
522
914
|
server_errors: this.serverErrorCounter.getAndResetServerErrors(),
|
|
@@ -535,8 +927,7 @@ var _ApitallyClient = class _ApitallyClient {
|
|
|
535
927
|
const timeOffset = Date.now() - time;
|
|
536
928
|
if (timeOffset <= MAX_QUEUE_TIME) {
|
|
537
929
|
if (i > 0) {
|
|
538
|
-
|
|
539
|
-
await new Promise((resolve) => setTimeout(resolve, waitMs));
|
|
930
|
+
await this.randomDelay();
|
|
540
931
|
}
|
|
541
932
|
payload.time_offset = timeOffset / 1e3;
|
|
542
933
|
await this.sendData("sync", payload);
|
|
@@ -555,6 +946,52 @@ var _ApitallyClient = class _ApitallyClient {
|
|
|
555
946
|
}
|
|
556
947
|
}
|
|
557
948
|
}
|
|
949
|
+
async sendLogData() {
|
|
950
|
+
this.logger.debug("Sending request log data to Apitally Hub");
|
|
951
|
+
await this.requestLogger.rotateFile();
|
|
952
|
+
const fetchWithRetry = (0, import_fetch_retry.default)(fetch, {
|
|
953
|
+
retries: 3,
|
|
954
|
+
retryDelay: 1e3,
|
|
955
|
+
retryOn: [
|
|
956
|
+
408,
|
|
957
|
+
429,
|
|
958
|
+
500,
|
|
959
|
+
502,
|
|
960
|
+
503,
|
|
961
|
+
504
|
|
962
|
+
]
|
|
963
|
+
});
|
|
964
|
+
let i = 0;
|
|
965
|
+
let logFile;
|
|
966
|
+
while (logFile = this.requestLogger.getFile()) {
|
|
967
|
+
if (i > 0) {
|
|
968
|
+
await this.randomDelay();
|
|
969
|
+
}
|
|
970
|
+
try {
|
|
971
|
+
const response = await fetchWithRetry(`${this.getHubUrlPrefix()}log?uuid=${logFile.uuid}`, {
|
|
972
|
+
method: "POST",
|
|
973
|
+
body: await logFile.getContent()
|
|
974
|
+
});
|
|
975
|
+
if (response.status === 402 && response.headers.has("Retry-After")) {
|
|
976
|
+
const retryAfter = parseInt(response.headers.get("Retry-After") ?? "0");
|
|
977
|
+
if (retryAfter > 0) {
|
|
978
|
+
this.requestLogger.suspendUntil = Date.now() + retryAfter * 1e3;
|
|
979
|
+
this.requestLogger.clear();
|
|
980
|
+
return;
|
|
981
|
+
}
|
|
982
|
+
}
|
|
983
|
+
if (!response.ok) {
|
|
984
|
+
throw new HTTPError(response);
|
|
985
|
+
}
|
|
986
|
+
logFile.delete();
|
|
987
|
+
} catch (error) {
|
|
988
|
+
this.requestLogger.retryFileLater(logFile);
|
|
989
|
+
break;
|
|
990
|
+
}
|
|
991
|
+
i++;
|
|
992
|
+
if (i >= 10) break;
|
|
993
|
+
}
|
|
994
|
+
}
|
|
558
995
|
handleHubError(error) {
|
|
559
996
|
if (error instanceof HTTPError) {
|
|
560
997
|
if (error.response.status === 404) {
|
|
@@ -569,6 +1006,10 @@ var _ApitallyClient = class _ApitallyClient {
|
|
|
569
1006
|
}
|
|
570
1007
|
return false;
|
|
571
1008
|
}
|
|
1009
|
+
async randomDelay() {
|
|
1010
|
+
const delay = 100 + Math.random() * 200;
|
|
1011
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
1012
|
+
}
|
|
572
1013
|
};
|
|
573
1014
|
__name(_ApitallyClient, "ApitallyClient");
|
|
574
1015
|
__publicField(_ApitallyClient, "instance");
|
|
@@ -624,19 +1065,20 @@ var getMiddleware = /* @__PURE__ */ __name((client) => {
|
|
|
624
1065
|
}
|
|
625
1066
|
throw error;
|
|
626
1067
|
} finally {
|
|
1068
|
+
const responseTime = performance.now() - startTime;
|
|
1069
|
+
const consumer = getConsumer(ctx);
|
|
1070
|
+
client.consumerRegistry.addOrUpdateConsumer(consumer);
|
|
627
1071
|
if (!path) {
|
|
628
1072
|
path = getPath(ctx);
|
|
629
1073
|
}
|
|
630
1074
|
if (path) {
|
|
631
1075
|
try {
|
|
632
|
-
const consumer = getConsumer(ctx);
|
|
633
|
-
client.consumerRegistry.addOrUpdateConsumer(consumer);
|
|
634
1076
|
client.requestCounter.addRequest({
|
|
635
1077
|
consumer: consumer == null ? void 0 : consumer.identifier,
|
|
636
1078
|
method: ctx.request.method,
|
|
637
1079
|
path,
|
|
638
1080
|
statusCode: statusCode || ctx.response.status,
|
|
639
|
-
responseTime
|
|
1081
|
+
responseTime,
|
|
640
1082
|
requestSize: ctx.request.length,
|
|
641
1083
|
responseSize: ctx.response.length
|
|
642
1084
|
});
|
|
@@ -647,6 +1089,24 @@ var getMiddleware = /* @__PURE__ */ __name((client) => {
|
|
|
647
1089
|
});
|
|
648
1090
|
}
|
|
649
1091
|
}
|
|
1092
|
+
if (client.requestLogger.enabled) {
|
|
1093
|
+
client.requestLogger.logRequest({
|
|
1094
|
+
timestamp: Date.now() / 1e3,
|
|
1095
|
+
method: ctx.request.method,
|
|
1096
|
+
path,
|
|
1097
|
+
url: ctx.request.href,
|
|
1098
|
+
headers: convertHeaders(ctx.request.headers),
|
|
1099
|
+
size: Number(ctx.request.headers["content-length"]),
|
|
1100
|
+
consumer: consumer == null ? void 0 : consumer.identifier,
|
|
1101
|
+
body: convertBody(ctx.request.body, ctx.request.get("content-type"))
|
|
1102
|
+
}, {
|
|
1103
|
+
statusCode: statusCode || ctx.response.status,
|
|
1104
|
+
responseTime: responseTime / 1e3,
|
|
1105
|
+
headers: convertHeaders(ctx.response.headers),
|
|
1106
|
+
size: Number(ctx.response.headers["content-length"]),
|
|
1107
|
+
body: convertBody(ctx.response.body, ctx.response.get("content-type"))
|
|
1108
|
+
});
|
|
1109
|
+
}
|
|
650
1110
|
}
|
|
651
1111
|
};
|
|
652
1112
|
}, "getMiddleware");
|