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/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,405 @@ 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?$/i,
|
|
299
|
+
/\/_?health[_-]?checks?$/i,
|
|
300
|
+
/\/_?heart[_-]?beats?$/i,
|
|
301
|
+
/\/ping$/i,
|
|
302
|
+
/\/ready$/i,
|
|
303
|
+
/\/live$/i
|
|
304
|
+
];
|
|
305
|
+
var EXCLUDE_USER_AGENT_PATTERNS = [
|
|
306
|
+
/health[-_ ]?check/i,
|
|
307
|
+
/microsoft-azure-application-lb/i,
|
|
308
|
+
/googlehc/i,
|
|
309
|
+
/kube-probe/i
|
|
310
|
+
];
|
|
311
|
+
var MASK_QUERY_PARAM_PATTERNS = [
|
|
312
|
+
/auth/i,
|
|
313
|
+
/api-?key/i,
|
|
314
|
+
/secret/i,
|
|
315
|
+
/token/i,
|
|
316
|
+
/password/i,
|
|
317
|
+
/pwd/i
|
|
318
|
+
];
|
|
319
|
+
var MASK_HEADER_PATTERNS = [
|
|
320
|
+
/auth/i,
|
|
321
|
+
/api-?key/i,
|
|
322
|
+
/secret/i,
|
|
323
|
+
/token/i,
|
|
324
|
+
/cookie/i
|
|
325
|
+
];
|
|
326
|
+
var DEFAULT_CONFIG = {
|
|
327
|
+
enabled: false,
|
|
328
|
+
logQueryParams: true,
|
|
329
|
+
logRequestHeaders: false,
|
|
330
|
+
logRequestBody: false,
|
|
331
|
+
logResponseHeaders: true,
|
|
332
|
+
logResponseBody: false,
|
|
333
|
+
maskQueryParams: [],
|
|
334
|
+
maskHeaders: [],
|
|
335
|
+
excludePaths: []
|
|
336
|
+
};
|
|
337
|
+
var _RequestLogger = class _RequestLogger {
|
|
338
|
+
config;
|
|
339
|
+
enabled;
|
|
340
|
+
suspendUntil = null;
|
|
341
|
+
pendingWrites = [];
|
|
342
|
+
currentFile = null;
|
|
343
|
+
files = [];
|
|
344
|
+
maintainIntervalId;
|
|
345
|
+
constructor(config) {
|
|
346
|
+
this.config = {
|
|
347
|
+
...DEFAULT_CONFIG,
|
|
348
|
+
...config
|
|
349
|
+
};
|
|
350
|
+
this.enabled = this.config.enabled && checkWritableFs();
|
|
351
|
+
if (this.enabled) {
|
|
352
|
+
this.maintainIntervalId = setInterval(() => {
|
|
353
|
+
this.maintain();
|
|
354
|
+
}, 1e3);
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
shouldExcludePath(urlPath) {
|
|
358
|
+
const patterns = [
|
|
359
|
+
...this.config.excludePaths,
|
|
360
|
+
...EXCLUDE_PATH_PATTERNS
|
|
361
|
+
];
|
|
362
|
+
return matchPatterns(urlPath, patterns);
|
|
363
|
+
}
|
|
364
|
+
shouldExcludeUserAgent(userAgent) {
|
|
365
|
+
return userAgent ? matchPatterns(userAgent, EXCLUDE_USER_AGENT_PATTERNS) : false;
|
|
366
|
+
}
|
|
367
|
+
shouldMaskQueryParam(name) {
|
|
368
|
+
const patterns = [
|
|
369
|
+
...this.config.maskQueryParams,
|
|
370
|
+
...MASK_QUERY_PARAM_PATTERNS
|
|
371
|
+
];
|
|
372
|
+
return matchPatterns(name, patterns);
|
|
373
|
+
}
|
|
374
|
+
shouldMaskHeader(name) {
|
|
375
|
+
const patterns = [
|
|
376
|
+
...this.config.maskHeaders,
|
|
377
|
+
...MASK_HEADER_PATTERNS
|
|
378
|
+
];
|
|
379
|
+
return matchPatterns(name, patterns);
|
|
380
|
+
}
|
|
381
|
+
hasSupportedContentType(headers) {
|
|
382
|
+
var _a2;
|
|
383
|
+
const contentType = (_a2 = headers.find(([k]) => k.toLowerCase() === "content-type")) == null ? void 0 : _a2[1];
|
|
384
|
+
return contentType !== void 0 && ALLOWED_CONTENT_TYPES.some((t) => contentType.startsWith(t));
|
|
385
|
+
}
|
|
386
|
+
maskQueryParams(search) {
|
|
387
|
+
const params = new URLSearchParams(search);
|
|
388
|
+
for (const [key] of params) {
|
|
389
|
+
if (this.shouldMaskQueryParam(key)) {
|
|
390
|
+
params.set(key, MASKED);
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
return params.toString();
|
|
394
|
+
}
|
|
395
|
+
maskHeaders(headers) {
|
|
396
|
+
return headers.map(([k, v]) => [
|
|
397
|
+
k,
|
|
398
|
+
this.shouldMaskHeader(k) ? MASKED : v
|
|
399
|
+
]);
|
|
400
|
+
}
|
|
401
|
+
logRequest(request, response) {
|
|
402
|
+
var _a2, _b, _c;
|
|
403
|
+
if (!this.enabled || this.suspendUntil !== null) return;
|
|
404
|
+
const url = new URL(request.url);
|
|
405
|
+
const path = request.path ?? url.pathname;
|
|
406
|
+
const userAgent = (_a2 = request.headers.find(([k]) => k.toLowerCase() === "user-agent")) == null ? void 0 : _a2[1];
|
|
407
|
+
if (this.shouldExcludePath(path) || this.shouldExcludeUserAgent(userAgent) || (((_c = (_b = this.config).excludeCallback) == null ? void 0 : _c.call(_b, request, response)) ?? false)) {
|
|
408
|
+
return;
|
|
409
|
+
}
|
|
410
|
+
url.search = this.config.logQueryParams ? this.maskQueryParams(url.search) : "";
|
|
411
|
+
request.url = url.toString();
|
|
412
|
+
request.headers = this.config.logRequestHeaders ? this.maskHeaders(request.headers) : [];
|
|
413
|
+
response.headers = this.config.logResponseHeaders ? this.maskHeaders(response.headers) : [];
|
|
414
|
+
if (!this.config.logRequestBody || !this.hasSupportedContentType(request.headers)) {
|
|
415
|
+
request.body = void 0;
|
|
416
|
+
} else if (request.body) {
|
|
417
|
+
if (request.body.length > MAX_BODY_SIZE) {
|
|
418
|
+
request.body = BODY_TOO_LARGE;
|
|
419
|
+
} else if (this.config.maskRequestBodyCallback) {
|
|
420
|
+
try {
|
|
421
|
+
request.body = this.config.maskRequestBodyCallback(request) ?? BODY_MASKED;
|
|
422
|
+
if (request.body.length > MAX_BODY_SIZE) {
|
|
423
|
+
request.body = BODY_TOO_LARGE;
|
|
424
|
+
}
|
|
425
|
+
} catch {
|
|
426
|
+
request.body = void 0;
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
if (!this.config.logResponseBody || !this.hasSupportedContentType(response.headers)) {
|
|
431
|
+
response.body = void 0;
|
|
432
|
+
} else if (response.body) {
|
|
433
|
+
if (response.body.length > MAX_BODY_SIZE) {
|
|
434
|
+
response.body = BODY_TOO_LARGE;
|
|
435
|
+
} else if (this.config.maskResponseBodyCallback) {
|
|
436
|
+
try {
|
|
437
|
+
response.body = this.config.maskResponseBodyCallback(request, response) ?? BODY_MASKED;
|
|
438
|
+
if (response.body.length > MAX_BODY_SIZE) {
|
|
439
|
+
response.body = BODY_TOO_LARGE;
|
|
440
|
+
}
|
|
441
|
+
} catch {
|
|
442
|
+
response.body = void 0;
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
const item = {
|
|
447
|
+
uuid: (0, import_crypto2.randomUUID)(),
|
|
448
|
+
request: skipEmptyValues(request),
|
|
449
|
+
response: skipEmptyValues(response)
|
|
450
|
+
};
|
|
451
|
+
[
|
|
452
|
+
item.request.body,
|
|
453
|
+
item.response.body
|
|
454
|
+
].forEach((body) => {
|
|
455
|
+
if (body) {
|
|
456
|
+
body.toJSON = function() {
|
|
457
|
+
return this.toString("base64");
|
|
458
|
+
};
|
|
459
|
+
}
|
|
460
|
+
});
|
|
461
|
+
this.pendingWrites.push(JSON.stringify(item));
|
|
462
|
+
if (this.pendingWrites.length > MAX_PENDING_WRITES) {
|
|
463
|
+
this.pendingWrites.shift();
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
async writeToFile() {
|
|
467
|
+
if (!this.enabled || this.pendingWrites.length === 0) {
|
|
468
|
+
return;
|
|
469
|
+
}
|
|
470
|
+
if (!this.currentFile) {
|
|
471
|
+
this.currentFile = new TempGzipFile();
|
|
472
|
+
}
|
|
473
|
+
while (this.pendingWrites.length > 0) {
|
|
474
|
+
const item = this.pendingWrites.shift();
|
|
475
|
+
if (item) {
|
|
476
|
+
await this.currentFile.writeLine(import_buffer2.Buffer.from(item));
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
getFile() {
|
|
481
|
+
return this.files.shift();
|
|
482
|
+
}
|
|
483
|
+
retryFileLater(file) {
|
|
484
|
+
this.files.unshift(file);
|
|
485
|
+
}
|
|
486
|
+
async rotateFile() {
|
|
487
|
+
if (this.currentFile) {
|
|
488
|
+
await this.currentFile.close();
|
|
489
|
+
this.files.push(this.currentFile);
|
|
490
|
+
this.currentFile = null;
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
async maintain() {
|
|
494
|
+
await this.writeToFile();
|
|
495
|
+
if (this.currentFile && this.currentFile.size > MAX_FILE_SIZE) {
|
|
496
|
+
await this.rotateFile();
|
|
497
|
+
}
|
|
498
|
+
while (this.files.length > MAX_FILES) {
|
|
499
|
+
const file = this.files.shift();
|
|
500
|
+
file == null ? void 0 : file.delete();
|
|
501
|
+
}
|
|
502
|
+
if (this.suspendUntil !== null && this.suspendUntil < Date.now()) {
|
|
503
|
+
this.suspendUntil = null;
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
async clear() {
|
|
507
|
+
this.pendingWrites = [];
|
|
508
|
+
await this.rotateFile();
|
|
509
|
+
this.files.forEach((file) => {
|
|
510
|
+
file.delete();
|
|
511
|
+
});
|
|
512
|
+
this.files = [];
|
|
513
|
+
}
|
|
514
|
+
async close() {
|
|
515
|
+
this.enabled = false;
|
|
516
|
+
await this.clear();
|
|
517
|
+
if (this.maintainIntervalId) {
|
|
518
|
+
clearInterval(this.maintainIntervalId);
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
};
|
|
522
|
+
__name(_RequestLogger, "RequestLogger");
|
|
523
|
+
var RequestLogger = _RequestLogger;
|
|
524
|
+
function convertHeaders(headers) {
|
|
525
|
+
if (headers instanceof Headers) {
|
|
526
|
+
return Array.from(headers.entries());
|
|
527
|
+
}
|
|
528
|
+
return Object.entries(headers).flatMap(([key, value]) => {
|
|
529
|
+
if (value === void 0) {
|
|
530
|
+
return [];
|
|
531
|
+
}
|
|
532
|
+
if (Array.isArray(value)) {
|
|
533
|
+
return value.map((v) => [
|
|
534
|
+
key,
|
|
535
|
+
v
|
|
536
|
+
]);
|
|
537
|
+
}
|
|
538
|
+
return [
|
|
539
|
+
[
|
|
540
|
+
key,
|
|
541
|
+
value.toString()
|
|
542
|
+
]
|
|
543
|
+
];
|
|
544
|
+
});
|
|
545
|
+
}
|
|
546
|
+
__name(convertHeaders, "convertHeaders");
|
|
547
|
+
function convertBody(body, contentType) {
|
|
548
|
+
if (!body || !contentType) {
|
|
549
|
+
return;
|
|
550
|
+
}
|
|
551
|
+
try {
|
|
552
|
+
if (contentType.startsWith("application/json")) {
|
|
553
|
+
if (isValidJsonString(body)) {
|
|
554
|
+
return import_buffer2.Buffer.from(body);
|
|
555
|
+
} else {
|
|
556
|
+
return import_buffer2.Buffer.from(JSON.stringify(body));
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
if (contentType.startsWith("text/") && typeof body === "string") {
|
|
560
|
+
return import_buffer2.Buffer.from(body);
|
|
561
|
+
}
|
|
562
|
+
} catch (error) {
|
|
563
|
+
return;
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
__name(convertBody, "convertBody");
|
|
567
|
+
function isValidJsonString(body) {
|
|
568
|
+
if (typeof body !== "string") {
|
|
569
|
+
return false;
|
|
570
|
+
}
|
|
571
|
+
try {
|
|
572
|
+
JSON.parse(body);
|
|
573
|
+
return true;
|
|
574
|
+
} catch {
|
|
575
|
+
return false;
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
__name(isValidJsonString, "isValidJsonString");
|
|
579
|
+
function matchPatterns(value, patterns) {
|
|
580
|
+
return patterns.some((pattern) => {
|
|
581
|
+
return pattern.test(value);
|
|
582
|
+
});
|
|
583
|
+
}
|
|
584
|
+
__name(matchPatterns, "matchPatterns");
|
|
585
|
+
function skipEmptyValues(data) {
|
|
586
|
+
return Object.fromEntries(Object.entries(data).filter(([_, v]) => {
|
|
587
|
+
if (v == null || Number.isNaN(v)) return false;
|
|
588
|
+
if (Array.isArray(v) || import_buffer2.Buffer.isBuffer(v) || typeof v === "string") {
|
|
589
|
+
return v.length > 0;
|
|
590
|
+
}
|
|
591
|
+
return true;
|
|
592
|
+
}));
|
|
593
|
+
}
|
|
594
|
+
__name(skipEmptyValues, "skipEmptyValues");
|
|
595
|
+
function checkWritableFs() {
|
|
596
|
+
try {
|
|
597
|
+
const testPath = (0, import_path2.join)((0, import_os2.tmpdir)(), `apitally-${(0, import_crypto2.randomUUID)()}`);
|
|
598
|
+
(0, import_fs2.writeFileSync)(testPath, "test");
|
|
599
|
+
(0, import_fs2.unlinkSync)(testPath);
|
|
600
|
+
return true;
|
|
601
|
+
} catch (error) {
|
|
602
|
+
return false;
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
__name(checkWritableFs, "checkWritableFs");
|
|
606
|
+
|
|
607
|
+
// src/common/serverErrorCounter.ts
|
|
608
|
+
var import_crypto3 = require("crypto");
|
|
212
609
|
var MAX_MSG_LENGTH = 2048;
|
|
213
610
|
var MAX_STACKTRACE_LENGTH = 65536;
|
|
214
611
|
var _ServerErrorCounter = class _ServerErrorCounter {
|
|
@@ -260,7 +657,7 @@ var _ServerErrorCounter = class _ServerErrorCounter {
|
|
|
260
657
|
serverError.msg.trim(),
|
|
261
658
|
serverError.traceback.trim()
|
|
262
659
|
].join("|");
|
|
263
|
-
return (0,
|
|
660
|
+
return (0, import_crypto3.createHash)("md5").update(hashInput).digest("hex");
|
|
264
661
|
}
|
|
265
662
|
getTruncatedMessage(msg) {
|
|
266
663
|
msg = msg.trim();
|
|
@@ -306,7 +703,7 @@ __name(_ServerErrorCounter, "ServerErrorCounter");
|
|
|
306
703
|
var ServerErrorCounter = _ServerErrorCounter;
|
|
307
704
|
|
|
308
705
|
// src/common/validationErrorCounter.ts
|
|
309
|
-
var
|
|
706
|
+
var import_crypto4 = require("crypto");
|
|
310
707
|
var _ValidationErrorCounter = class _ValidationErrorCounter {
|
|
311
708
|
errorCounts;
|
|
312
709
|
errorDetails;
|
|
@@ -350,7 +747,7 @@ var _ValidationErrorCounter = class _ValidationErrorCounter {
|
|
|
350
747
|
validationError.msg.trim(),
|
|
351
748
|
validationError.type
|
|
352
749
|
].join("|");
|
|
353
|
-
return (0,
|
|
750
|
+
return (0, import_crypto4.createHash)("md5").update(hashInput).digest("hex");
|
|
354
751
|
}
|
|
355
752
|
};
|
|
356
753
|
__name(_ValidationErrorCounter, "ValidationErrorCounter");
|
|
@@ -379,11 +776,12 @@ var _ApitallyClient = class _ApitallyClient {
|
|
|
379
776
|
startupData;
|
|
380
777
|
startupDataSent = false;
|
|
381
778
|
requestCounter;
|
|
779
|
+
requestLogger;
|
|
382
780
|
validationErrorCounter;
|
|
383
781
|
serverErrorCounter;
|
|
384
782
|
consumerRegistry;
|
|
385
783
|
logger;
|
|
386
|
-
constructor({ clientId, env = "dev", logger }) {
|
|
784
|
+
constructor({ clientId, env = "dev", requestLoggingConfig, logger }) {
|
|
387
785
|
if (_ApitallyClient.instance) {
|
|
388
786
|
throw new Error("Apitally client is already initialized");
|
|
389
787
|
}
|
|
@@ -396,9 +794,10 @@ var _ApitallyClient = class _ApitallyClient {
|
|
|
396
794
|
_ApitallyClient.instance = this;
|
|
397
795
|
this.clientId = clientId;
|
|
398
796
|
this.env = env;
|
|
399
|
-
this.instanceUuid = (0,
|
|
797
|
+
this.instanceUuid = (0, import_crypto5.randomUUID)();
|
|
400
798
|
this.syncDataQueue = [];
|
|
401
799
|
this.requestCounter = new RequestCounter();
|
|
800
|
+
this.requestLogger = new RequestLogger(requestLoggingConfig);
|
|
402
801
|
this.validationErrorCounter = new ValidationErrorCounter();
|
|
403
802
|
this.serverErrorCounter = new ServerErrorCounter();
|
|
404
803
|
this.consumerRegistry = new ConsumerRegistry();
|
|
@@ -420,6 +819,8 @@ var _ApitallyClient = class _ApitallyClient {
|
|
|
420
819
|
async handleShutdown() {
|
|
421
820
|
this.stopSync();
|
|
422
821
|
await this.sendSyncData();
|
|
822
|
+
await this.sendLogData();
|
|
823
|
+
await this.requestLogger.close();
|
|
423
824
|
_ApitallyClient.instance = void 0;
|
|
424
825
|
}
|
|
425
826
|
getHubUrlPrefix() {
|
|
@@ -466,7 +867,8 @@ var _ApitallyClient = class _ApitallyClient {
|
|
|
466
867
|
async sync() {
|
|
467
868
|
try {
|
|
468
869
|
const promises = [
|
|
469
|
-
this.sendSyncData()
|
|
870
|
+
this.sendSyncData(),
|
|
871
|
+
this.sendLogData()
|
|
470
872
|
];
|
|
471
873
|
if (!this.startupDataSent) {
|
|
472
874
|
promises.push(this.sendStartupData());
|
|
@@ -494,7 +896,7 @@ var _ApitallyClient = class _ApitallyClient {
|
|
|
494
896
|
this.logger.debug("Sending startup data to Apitally Hub");
|
|
495
897
|
const payload = {
|
|
496
898
|
instance_uuid: this.instanceUuid,
|
|
497
|
-
message_uuid: (0,
|
|
899
|
+
message_uuid: (0, import_crypto5.randomUUID)(),
|
|
498
900
|
...this.startupData
|
|
499
901
|
};
|
|
500
902
|
try {
|
|
@@ -516,7 +918,7 @@ var _ApitallyClient = class _ApitallyClient {
|
|
|
516
918
|
const newPayload = {
|
|
517
919
|
time_offset: 0,
|
|
518
920
|
instance_uuid: this.instanceUuid,
|
|
519
|
-
message_uuid: (0,
|
|
921
|
+
message_uuid: (0, import_crypto5.randomUUID)(),
|
|
520
922
|
requests: this.requestCounter.getAndResetRequests(),
|
|
521
923
|
validation_errors: this.validationErrorCounter.getAndResetValidationErrors(),
|
|
522
924
|
server_errors: this.serverErrorCounter.getAndResetServerErrors(),
|
|
@@ -535,8 +937,7 @@ var _ApitallyClient = class _ApitallyClient {
|
|
|
535
937
|
const timeOffset = Date.now() - time;
|
|
536
938
|
if (timeOffset <= MAX_QUEUE_TIME) {
|
|
537
939
|
if (i > 0) {
|
|
538
|
-
|
|
539
|
-
await new Promise((resolve) => setTimeout(resolve, waitMs));
|
|
940
|
+
await this.randomDelay();
|
|
540
941
|
}
|
|
541
942
|
payload.time_offset = timeOffset / 1e3;
|
|
542
943
|
await this.sendData("sync", payload);
|
|
@@ -555,6 +956,52 @@ var _ApitallyClient = class _ApitallyClient {
|
|
|
555
956
|
}
|
|
556
957
|
}
|
|
557
958
|
}
|
|
959
|
+
async sendLogData() {
|
|
960
|
+
this.logger.debug("Sending request log data to Apitally Hub");
|
|
961
|
+
await this.requestLogger.rotateFile();
|
|
962
|
+
const fetchWithRetry = (0, import_fetch_retry.default)(fetch, {
|
|
963
|
+
retries: 3,
|
|
964
|
+
retryDelay: 1e3,
|
|
965
|
+
retryOn: [
|
|
966
|
+
408,
|
|
967
|
+
429,
|
|
968
|
+
500,
|
|
969
|
+
502,
|
|
970
|
+
503,
|
|
971
|
+
504
|
|
972
|
+
]
|
|
973
|
+
});
|
|
974
|
+
let i = 0;
|
|
975
|
+
let logFile;
|
|
976
|
+
while (logFile = this.requestLogger.getFile()) {
|
|
977
|
+
if (i > 0) {
|
|
978
|
+
await this.randomDelay();
|
|
979
|
+
}
|
|
980
|
+
try {
|
|
981
|
+
const response = await fetchWithRetry(`${this.getHubUrlPrefix()}log?uuid=${logFile.uuid}`, {
|
|
982
|
+
method: "POST",
|
|
983
|
+
body: await logFile.getContent()
|
|
984
|
+
});
|
|
985
|
+
if (response.status === 402 && response.headers.has("Retry-After")) {
|
|
986
|
+
const retryAfter = parseInt(response.headers.get("Retry-After") ?? "0");
|
|
987
|
+
if (retryAfter > 0) {
|
|
988
|
+
this.requestLogger.suspendUntil = Date.now() + retryAfter * 1e3;
|
|
989
|
+
this.requestLogger.clear();
|
|
990
|
+
return;
|
|
991
|
+
}
|
|
992
|
+
}
|
|
993
|
+
if (!response.ok) {
|
|
994
|
+
throw new HTTPError(response);
|
|
995
|
+
}
|
|
996
|
+
logFile.delete();
|
|
997
|
+
} catch (error) {
|
|
998
|
+
this.requestLogger.retryFileLater(logFile);
|
|
999
|
+
break;
|
|
1000
|
+
}
|
|
1001
|
+
i++;
|
|
1002
|
+
if (i >= 10) break;
|
|
1003
|
+
}
|
|
1004
|
+
}
|
|
558
1005
|
handleHubError(error) {
|
|
559
1006
|
if (error instanceof HTTPError) {
|
|
560
1007
|
if (error.response.status === 404) {
|
|
@@ -569,6 +1016,10 @@ var _ApitallyClient = class _ApitallyClient {
|
|
|
569
1016
|
}
|
|
570
1017
|
return false;
|
|
571
1018
|
}
|
|
1019
|
+
async randomDelay() {
|
|
1020
|
+
const delay = 100 + Math.random() * 200;
|
|
1021
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
1022
|
+
}
|
|
572
1023
|
};
|
|
573
1024
|
__name(_ApitallyClient, "ApitallyClient");
|
|
574
1025
|
__publicField(_ApitallyClient, "instance");
|
|
@@ -624,19 +1075,20 @@ var getMiddleware = /* @__PURE__ */ __name((client) => {
|
|
|
624
1075
|
}
|
|
625
1076
|
throw error;
|
|
626
1077
|
} finally {
|
|
1078
|
+
const responseTime = performance.now() - startTime;
|
|
1079
|
+
const consumer = getConsumer(ctx);
|
|
1080
|
+
client.consumerRegistry.addOrUpdateConsumer(consumer);
|
|
627
1081
|
if (!path) {
|
|
628
1082
|
path = getPath(ctx);
|
|
629
1083
|
}
|
|
630
1084
|
if (path) {
|
|
631
1085
|
try {
|
|
632
|
-
const consumer = getConsumer(ctx);
|
|
633
|
-
client.consumerRegistry.addOrUpdateConsumer(consumer);
|
|
634
1086
|
client.requestCounter.addRequest({
|
|
635
1087
|
consumer: consumer == null ? void 0 : consumer.identifier,
|
|
636
1088
|
method: ctx.request.method,
|
|
637
1089
|
path,
|
|
638
1090
|
statusCode: statusCode || ctx.response.status,
|
|
639
|
-
responseTime
|
|
1091
|
+
responseTime,
|
|
640
1092
|
requestSize: ctx.request.length,
|
|
641
1093
|
responseSize: ctx.response.length
|
|
642
1094
|
});
|
|
@@ -647,6 +1099,24 @@ var getMiddleware = /* @__PURE__ */ __name((client) => {
|
|
|
647
1099
|
});
|
|
648
1100
|
}
|
|
649
1101
|
}
|
|
1102
|
+
if (client.requestLogger.enabled) {
|
|
1103
|
+
client.requestLogger.logRequest({
|
|
1104
|
+
timestamp: Date.now() / 1e3,
|
|
1105
|
+
method: ctx.request.method,
|
|
1106
|
+
path,
|
|
1107
|
+
url: ctx.request.href,
|
|
1108
|
+
headers: convertHeaders(ctx.request.headers),
|
|
1109
|
+
size: Number(ctx.request.headers["content-length"]),
|
|
1110
|
+
consumer: consumer == null ? void 0 : consumer.identifier,
|
|
1111
|
+
body: convertBody(ctx.request.body, ctx.request.get("content-type"))
|
|
1112
|
+
}, {
|
|
1113
|
+
statusCode: statusCode || ctx.response.status,
|
|
1114
|
+
responseTime: responseTime / 1e3,
|
|
1115
|
+
headers: convertHeaders(ctx.response.headers),
|
|
1116
|
+
size: Number(ctx.response.headers["content-length"]),
|
|
1117
|
+
body: convertBody(ctx.response.body, ctx.response.get("content-type"))
|
|
1118
|
+
});
|
|
1119
|
+
}
|
|
650
1120
|
}
|
|
651
1121
|
};
|
|
652
1122
|
}, "getMiddleware");
|