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