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/middleware.js
CHANGED
|
@@ -10,7 +10,7 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
|
|
|
10
10
|
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
11
11
|
|
|
12
12
|
// src/common/client.ts
|
|
13
|
-
import { randomUUID } from "crypto";
|
|
13
|
+
import { randomUUID as randomUUID3 } from "crypto";
|
|
14
14
|
import fetchRetry from "fetch-retry";
|
|
15
15
|
|
|
16
16
|
// src/common/consumerRegistry.ts
|
|
@@ -179,6 +179,403 @@ var _RequestCounter = class _RequestCounter {
|
|
|
179
179
|
__name(_RequestCounter, "RequestCounter");
|
|
180
180
|
var RequestCounter = _RequestCounter;
|
|
181
181
|
|
|
182
|
+
// src/common/requestLogger.ts
|
|
183
|
+
import { Buffer as Buffer3 } from "buffer";
|
|
184
|
+
import { randomUUID as randomUUID2 } from "crypto";
|
|
185
|
+
import { unlinkSync as unlinkSync2, writeFileSync } from "fs";
|
|
186
|
+
import { tmpdir as tmpdir2 } from "os";
|
|
187
|
+
import { join as join2 } from "path";
|
|
188
|
+
|
|
189
|
+
// src/common/tempGzipFile.ts
|
|
190
|
+
import { Buffer as Buffer2 } from "buffer";
|
|
191
|
+
import { randomUUID } from "crypto";
|
|
192
|
+
import { createWriteStream, readFile, unlinkSync } from "fs";
|
|
193
|
+
import { tmpdir } from "os";
|
|
194
|
+
import { join } from "path";
|
|
195
|
+
import { createGzip } from "zlib";
|
|
196
|
+
var _TempGzipFile = class _TempGzipFile {
|
|
197
|
+
uuid;
|
|
198
|
+
filePath;
|
|
199
|
+
gzip;
|
|
200
|
+
writeStream;
|
|
201
|
+
readyPromise;
|
|
202
|
+
constructor() {
|
|
203
|
+
this.uuid = randomUUID();
|
|
204
|
+
this.filePath = join(tmpdir(), `apitally-${this.uuid}.gz`);
|
|
205
|
+
this.writeStream = createWriteStream(this.filePath);
|
|
206
|
+
this.readyPromise = new Promise((resolve, reject) => {
|
|
207
|
+
this.writeStream.once("ready", resolve);
|
|
208
|
+
this.writeStream.once("error", reject);
|
|
209
|
+
});
|
|
210
|
+
this.gzip = createGzip();
|
|
211
|
+
this.gzip.pipe(this.writeStream);
|
|
212
|
+
}
|
|
213
|
+
get size() {
|
|
214
|
+
return this.writeStream.bytesWritten;
|
|
215
|
+
}
|
|
216
|
+
async writeLine(data) {
|
|
217
|
+
await this.readyPromise;
|
|
218
|
+
return new Promise((resolve, reject) => {
|
|
219
|
+
this.gzip.write(Buffer2.concat([
|
|
220
|
+
data,
|
|
221
|
+
Buffer2.from("\n")
|
|
222
|
+
]), (error) => {
|
|
223
|
+
if (error) {
|
|
224
|
+
reject(error);
|
|
225
|
+
} else {
|
|
226
|
+
resolve();
|
|
227
|
+
}
|
|
228
|
+
});
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
async getContent() {
|
|
232
|
+
return new Promise((resolve, reject) => {
|
|
233
|
+
readFile(this.filePath, (error, data) => {
|
|
234
|
+
if (error) {
|
|
235
|
+
reject(error);
|
|
236
|
+
} else {
|
|
237
|
+
resolve(data);
|
|
238
|
+
}
|
|
239
|
+
});
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
async close() {
|
|
243
|
+
return new Promise((resolve) => {
|
|
244
|
+
this.gzip.end(() => {
|
|
245
|
+
resolve();
|
|
246
|
+
});
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
async delete() {
|
|
250
|
+
await this.close();
|
|
251
|
+
unlinkSync(this.filePath);
|
|
252
|
+
}
|
|
253
|
+
};
|
|
254
|
+
__name(_TempGzipFile, "TempGzipFile");
|
|
255
|
+
var TempGzipFile = _TempGzipFile;
|
|
256
|
+
|
|
257
|
+
// src/common/requestLogger.ts
|
|
258
|
+
var MAX_BODY_SIZE = 5e4;
|
|
259
|
+
var MAX_FILE_SIZE = 1e6;
|
|
260
|
+
var MAX_FILES = 50;
|
|
261
|
+
var MAX_PENDING_WRITES = 100;
|
|
262
|
+
var BODY_TOO_LARGE = Buffer3.from("<body too large>");
|
|
263
|
+
var BODY_MASKED = Buffer3.from("<masked>");
|
|
264
|
+
var MASKED = "******";
|
|
265
|
+
var ALLOWED_CONTENT_TYPES = [
|
|
266
|
+
"application/json",
|
|
267
|
+
"text/plain"
|
|
268
|
+
];
|
|
269
|
+
var EXCLUDE_PATH_PATTERNS = [
|
|
270
|
+
/\/_?healthz?$/i,
|
|
271
|
+
/\/_?health[_-]?checks?$/i,
|
|
272
|
+
/\/_?heart[_-]?beats?$/i,
|
|
273
|
+
/\/ping$/i,
|
|
274
|
+
/\/ready$/i,
|
|
275
|
+
/\/live$/i
|
|
276
|
+
];
|
|
277
|
+
var EXCLUDE_USER_AGENT_PATTERNS = [
|
|
278
|
+
/health[-_ ]?check/i,
|
|
279
|
+
/microsoft-azure-application-lb/i,
|
|
280
|
+
/googlehc/i,
|
|
281
|
+
/kube-probe/i
|
|
282
|
+
];
|
|
283
|
+
var MASK_QUERY_PARAM_PATTERNS = [
|
|
284
|
+
/auth/i,
|
|
285
|
+
/api-?key/i,
|
|
286
|
+
/secret/i,
|
|
287
|
+
/token/i,
|
|
288
|
+
/password/i,
|
|
289
|
+
/pwd/i
|
|
290
|
+
];
|
|
291
|
+
var MASK_HEADER_PATTERNS = [
|
|
292
|
+
/auth/i,
|
|
293
|
+
/api-?key/i,
|
|
294
|
+
/secret/i,
|
|
295
|
+
/token/i,
|
|
296
|
+
/cookie/i
|
|
297
|
+
];
|
|
298
|
+
var DEFAULT_CONFIG = {
|
|
299
|
+
enabled: false,
|
|
300
|
+
logQueryParams: true,
|
|
301
|
+
logRequestHeaders: false,
|
|
302
|
+
logRequestBody: false,
|
|
303
|
+
logResponseHeaders: true,
|
|
304
|
+
logResponseBody: false,
|
|
305
|
+
maskQueryParams: [],
|
|
306
|
+
maskHeaders: [],
|
|
307
|
+
excludePaths: []
|
|
308
|
+
};
|
|
309
|
+
var _RequestLogger = class _RequestLogger {
|
|
310
|
+
config;
|
|
311
|
+
enabled;
|
|
312
|
+
suspendUntil = null;
|
|
313
|
+
pendingWrites = [];
|
|
314
|
+
currentFile = null;
|
|
315
|
+
files = [];
|
|
316
|
+
maintainIntervalId;
|
|
317
|
+
constructor(config) {
|
|
318
|
+
this.config = {
|
|
319
|
+
...DEFAULT_CONFIG,
|
|
320
|
+
...config
|
|
321
|
+
};
|
|
322
|
+
this.enabled = this.config.enabled && checkWritableFs();
|
|
323
|
+
if (this.enabled) {
|
|
324
|
+
this.maintainIntervalId = setInterval(() => {
|
|
325
|
+
this.maintain();
|
|
326
|
+
}, 1e3);
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
shouldExcludePath(urlPath) {
|
|
330
|
+
const patterns = [
|
|
331
|
+
...this.config.excludePaths,
|
|
332
|
+
...EXCLUDE_PATH_PATTERNS
|
|
333
|
+
];
|
|
334
|
+
return matchPatterns(urlPath, patterns);
|
|
335
|
+
}
|
|
336
|
+
shouldExcludeUserAgent(userAgent) {
|
|
337
|
+
return userAgent ? matchPatterns(userAgent, EXCLUDE_USER_AGENT_PATTERNS) : false;
|
|
338
|
+
}
|
|
339
|
+
shouldMaskQueryParam(name) {
|
|
340
|
+
const patterns = [
|
|
341
|
+
...this.config.maskQueryParams,
|
|
342
|
+
...MASK_QUERY_PARAM_PATTERNS
|
|
343
|
+
];
|
|
344
|
+
return matchPatterns(name, patterns);
|
|
345
|
+
}
|
|
346
|
+
shouldMaskHeader(name) {
|
|
347
|
+
const patterns = [
|
|
348
|
+
...this.config.maskHeaders,
|
|
349
|
+
...MASK_HEADER_PATTERNS
|
|
350
|
+
];
|
|
351
|
+
return matchPatterns(name, patterns);
|
|
352
|
+
}
|
|
353
|
+
hasSupportedContentType(headers) {
|
|
354
|
+
var _a2;
|
|
355
|
+
const contentType = (_a2 = headers.find(([k]) => k.toLowerCase() === "content-type")) == null ? void 0 : _a2[1];
|
|
356
|
+
return contentType !== void 0 && ALLOWED_CONTENT_TYPES.some((t) => contentType.startsWith(t));
|
|
357
|
+
}
|
|
358
|
+
maskQueryParams(search) {
|
|
359
|
+
const params = new URLSearchParams(search);
|
|
360
|
+
for (const [key] of params) {
|
|
361
|
+
if (this.shouldMaskQueryParam(key)) {
|
|
362
|
+
params.set(key, MASKED);
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
return params.toString();
|
|
366
|
+
}
|
|
367
|
+
maskHeaders(headers) {
|
|
368
|
+
return headers.map(([k, v]) => [
|
|
369
|
+
k,
|
|
370
|
+
this.shouldMaskHeader(k) ? MASKED : v
|
|
371
|
+
]);
|
|
372
|
+
}
|
|
373
|
+
logRequest(request, response) {
|
|
374
|
+
var _a2, _b, _c;
|
|
375
|
+
if (!this.enabled || this.suspendUntil !== null) return;
|
|
376
|
+
const url = new URL(request.url);
|
|
377
|
+
const path = request.path ?? url.pathname;
|
|
378
|
+
const userAgent = (_a2 = request.headers.find(([k]) => k.toLowerCase() === "user-agent")) == null ? void 0 : _a2[1];
|
|
379
|
+
if (this.shouldExcludePath(path) || this.shouldExcludeUserAgent(userAgent) || (((_c = (_b = this.config).excludeCallback) == null ? void 0 : _c.call(_b, request, response)) ?? false)) {
|
|
380
|
+
return;
|
|
381
|
+
}
|
|
382
|
+
url.search = this.config.logQueryParams ? this.maskQueryParams(url.search) : "";
|
|
383
|
+
request.url = url.toString();
|
|
384
|
+
request.headers = this.config.logRequestHeaders ? this.maskHeaders(request.headers) : [];
|
|
385
|
+
response.headers = this.config.logResponseHeaders ? this.maskHeaders(response.headers) : [];
|
|
386
|
+
if (!this.config.logRequestBody || !this.hasSupportedContentType(request.headers)) {
|
|
387
|
+
request.body = void 0;
|
|
388
|
+
} else if (request.body) {
|
|
389
|
+
if (request.body.length > MAX_BODY_SIZE) {
|
|
390
|
+
request.body = BODY_TOO_LARGE;
|
|
391
|
+
} else if (this.config.maskRequestBodyCallback) {
|
|
392
|
+
try {
|
|
393
|
+
request.body = this.config.maskRequestBodyCallback(request) ?? BODY_MASKED;
|
|
394
|
+
if (request.body.length > MAX_BODY_SIZE) {
|
|
395
|
+
request.body = BODY_TOO_LARGE;
|
|
396
|
+
}
|
|
397
|
+
} catch {
|
|
398
|
+
request.body = void 0;
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
if (!this.config.logResponseBody || !this.hasSupportedContentType(response.headers)) {
|
|
403
|
+
response.body = void 0;
|
|
404
|
+
} else if (response.body) {
|
|
405
|
+
if (response.body.length > MAX_BODY_SIZE) {
|
|
406
|
+
response.body = BODY_TOO_LARGE;
|
|
407
|
+
} else if (this.config.maskResponseBodyCallback) {
|
|
408
|
+
try {
|
|
409
|
+
response.body = this.config.maskResponseBodyCallback(request, response) ?? BODY_MASKED;
|
|
410
|
+
if (response.body.length > MAX_BODY_SIZE) {
|
|
411
|
+
response.body = BODY_TOO_LARGE;
|
|
412
|
+
}
|
|
413
|
+
} catch {
|
|
414
|
+
response.body = void 0;
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
const item = {
|
|
419
|
+
uuid: randomUUID2(),
|
|
420
|
+
request: skipEmptyValues(request),
|
|
421
|
+
response: skipEmptyValues(response)
|
|
422
|
+
};
|
|
423
|
+
[
|
|
424
|
+
item.request.body,
|
|
425
|
+
item.response.body
|
|
426
|
+
].forEach((body) => {
|
|
427
|
+
if (body) {
|
|
428
|
+
body.toJSON = function() {
|
|
429
|
+
return this.toString("base64");
|
|
430
|
+
};
|
|
431
|
+
}
|
|
432
|
+
});
|
|
433
|
+
this.pendingWrites.push(JSON.stringify(item));
|
|
434
|
+
if (this.pendingWrites.length > MAX_PENDING_WRITES) {
|
|
435
|
+
this.pendingWrites.shift();
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
async writeToFile() {
|
|
439
|
+
if (!this.enabled || this.pendingWrites.length === 0) {
|
|
440
|
+
return;
|
|
441
|
+
}
|
|
442
|
+
if (!this.currentFile) {
|
|
443
|
+
this.currentFile = new TempGzipFile();
|
|
444
|
+
}
|
|
445
|
+
while (this.pendingWrites.length > 0) {
|
|
446
|
+
const item = this.pendingWrites.shift();
|
|
447
|
+
if (item) {
|
|
448
|
+
await this.currentFile.writeLine(Buffer3.from(item));
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
getFile() {
|
|
453
|
+
return this.files.shift();
|
|
454
|
+
}
|
|
455
|
+
retryFileLater(file) {
|
|
456
|
+
this.files.unshift(file);
|
|
457
|
+
}
|
|
458
|
+
async rotateFile() {
|
|
459
|
+
if (this.currentFile) {
|
|
460
|
+
await this.currentFile.close();
|
|
461
|
+
this.files.push(this.currentFile);
|
|
462
|
+
this.currentFile = null;
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
async maintain() {
|
|
466
|
+
await this.writeToFile();
|
|
467
|
+
if (this.currentFile && this.currentFile.size > MAX_FILE_SIZE) {
|
|
468
|
+
await this.rotateFile();
|
|
469
|
+
}
|
|
470
|
+
while (this.files.length > MAX_FILES) {
|
|
471
|
+
const file = this.files.shift();
|
|
472
|
+
file == null ? void 0 : file.delete();
|
|
473
|
+
}
|
|
474
|
+
if (this.suspendUntil !== null && this.suspendUntil < Date.now()) {
|
|
475
|
+
this.suspendUntil = null;
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
async clear() {
|
|
479
|
+
this.pendingWrites = [];
|
|
480
|
+
await this.rotateFile();
|
|
481
|
+
this.files.forEach((file) => {
|
|
482
|
+
file.delete();
|
|
483
|
+
});
|
|
484
|
+
this.files = [];
|
|
485
|
+
}
|
|
486
|
+
async close() {
|
|
487
|
+
this.enabled = false;
|
|
488
|
+
await this.clear();
|
|
489
|
+
if (this.maintainIntervalId) {
|
|
490
|
+
clearInterval(this.maintainIntervalId);
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
};
|
|
494
|
+
__name(_RequestLogger, "RequestLogger");
|
|
495
|
+
var RequestLogger = _RequestLogger;
|
|
496
|
+
function convertHeaders(headers) {
|
|
497
|
+
if (headers instanceof Headers) {
|
|
498
|
+
return Array.from(headers.entries());
|
|
499
|
+
}
|
|
500
|
+
return Object.entries(headers).flatMap(([key, value]) => {
|
|
501
|
+
if (value === void 0) {
|
|
502
|
+
return [];
|
|
503
|
+
}
|
|
504
|
+
if (Array.isArray(value)) {
|
|
505
|
+
return value.map((v) => [
|
|
506
|
+
key,
|
|
507
|
+
v
|
|
508
|
+
]);
|
|
509
|
+
}
|
|
510
|
+
return [
|
|
511
|
+
[
|
|
512
|
+
key,
|
|
513
|
+
value.toString()
|
|
514
|
+
]
|
|
515
|
+
];
|
|
516
|
+
});
|
|
517
|
+
}
|
|
518
|
+
__name(convertHeaders, "convertHeaders");
|
|
519
|
+
function convertBody(body, contentType) {
|
|
520
|
+
if (!body || !contentType) {
|
|
521
|
+
return;
|
|
522
|
+
}
|
|
523
|
+
try {
|
|
524
|
+
if (contentType.startsWith("application/json")) {
|
|
525
|
+
if (isValidJsonString(body)) {
|
|
526
|
+
return Buffer3.from(body);
|
|
527
|
+
} else {
|
|
528
|
+
return Buffer3.from(JSON.stringify(body));
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
if (contentType.startsWith("text/") && typeof body === "string") {
|
|
532
|
+
return Buffer3.from(body);
|
|
533
|
+
}
|
|
534
|
+
} catch (error) {
|
|
535
|
+
return;
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
__name(convertBody, "convertBody");
|
|
539
|
+
function isValidJsonString(body) {
|
|
540
|
+
if (typeof body !== "string") {
|
|
541
|
+
return false;
|
|
542
|
+
}
|
|
543
|
+
try {
|
|
544
|
+
JSON.parse(body);
|
|
545
|
+
return true;
|
|
546
|
+
} catch {
|
|
547
|
+
return false;
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
__name(isValidJsonString, "isValidJsonString");
|
|
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) || Buffer3.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 = join2(tmpdir2(), `apitally-${randomUUID2()}`);
|
|
570
|
+
writeFileSync(testPath, "test");
|
|
571
|
+
unlinkSync2(testPath);
|
|
572
|
+
return true;
|
|
573
|
+
} catch (error) {
|
|
574
|
+
return false;
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
__name(checkWritableFs, "checkWritableFs");
|
|
578
|
+
|
|
182
579
|
// src/common/serverErrorCounter.ts
|
|
183
580
|
import { createHash } from "crypto";
|
|
184
581
|
var MAX_MSG_LENGTH = 2048;
|
|
@@ -351,11 +748,12 @@ var _ApitallyClient = class _ApitallyClient {
|
|
|
351
748
|
startupData;
|
|
352
749
|
startupDataSent = false;
|
|
353
750
|
requestCounter;
|
|
751
|
+
requestLogger;
|
|
354
752
|
validationErrorCounter;
|
|
355
753
|
serverErrorCounter;
|
|
356
754
|
consumerRegistry;
|
|
357
755
|
logger;
|
|
358
|
-
constructor({ clientId, env = "dev", logger }) {
|
|
756
|
+
constructor({ clientId, env = "dev", requestLoggingConfig, logger }) {
|
|
359
757
|
if (_ApitallyClient.instance) {
|
|
360
758
|
throw new Error("Apitally client is already initialized");
|
|
361
759
|
}
|
|
@@ -368,9 +766,10 @@ var _ApitallyClient = class _ApitallyClient {
|
|
|
368
766
|
_ApitallyClient.instance = this;
|
|
369
767
|
this.clientId = clientId;
|
|
370
768
|
this.env = env;
|
|
371
|
-
this.instanceUuid =
|
|
769
|
+
this.instanceUuid = randomUUID3();
|
|
372
770
|
this.syncDataQueue = [];
|
|
373
771
|
this.requestCounter = new RequestCounter();
|
|
772
|
+
this.requestLogger = new RequestLogger(requestLoggingConfig);
|
|
374
773
|
this.validationErrorCounter = new ValidationErrorCounter();
|
|
375
774
|
this.serverErrorCounter = new ServerErrorCounter();
|
|
376
775
|
this.consumerRegistry = new ConsumerRegistry();
|
|
@@ -392,6 +791,8 @@ var _ApitallyClient = class _ApitallyClient {
|
|
|
392
791
|
async handleShutdown() {
|
|
393
792
|
this.stopSync();
|
|
394
793
|
await this.sendSyncData();
|
|
794
|
+
await this.sendLogData();
|
|
795
|
+
await this.requestLogger.close();
|
|
395
796
|
_ApitallyClient.instance = void 0;
|
|
396
797
|
}
|
|
397
798
|
getHubUrlPrefix() {
|
|
@@ -438,7 +839,8 @@ var _ApitallyClient = class _ApitallyClient {
|
|
|
438
839
|
async sync() {
|
|
439
840
|
try {
|
|
440
841
|
const promises = [
|
|
441
|
-
this.sendSyncData()
|
|
842
|
+
this.sendSyncData(),
|
|
843
|
+
this.sendLogData()
|
|
442
844
|
];
|
|
443
845
|
if (!this.startupDataSent) {
|
|
444
846
|
promises.push(this.sendStartupData());
|
|
@@ -466,7 +868,7 @@ var _ApitallyClient = class _ApitallyClient {
|
|
|
466
868
|
this.logger.debug("Sending startup data to Apitally Hub");
|
|
467
869
|
const payload = {
|
|
468
870
|
instance_uuid: this.instanceUuid,
|
|
469
|
-
message_uuid:
|
|
871
|
+
message_uuid: randomUUID3(),
|
|
470
872
|
...this.startupData
|
|
471
873
|
};
|
|
472
874
|
try {
|
|
@@ -488,7 +890,7 @@ var _ApitallyClient = class _ApitallyClient {
|
|
|
488
890
|
const newPayload = {
|
|
489
891
|
time_offset: 0,
|
|
490
892
|
instance_uuid: this.instanceUuid,
|
|
491
|
-
message_uuid:
|
|
893
|
+
message_uuid: randomUUID3(),
|
|
492
894
|
requests: this.requestCounter.getAndResetRequests(),
|
|
493
895
|
validation_errors: this.validationErrorCounter.getAndResetValidationErrors(),
|
|
494
896
|
server_errors: this.serverErrorCounter.getAndResetServerErrors(),
|
|
@@ -507,8 +909,7 @@ var _ApitallyClient = class _ApitallyClient {
|
|
|
507
909
|
const timeOffset = Date.now() - time;
|
|
508
910
|
if (timeOffset <= MAX_QUEUE_TIME) {
|
|
509
911
|
if (i > 0) {
|
|
510
|
-
|
|
511
|
-
await new Promise((resolve) => setTimeout(resolve, waitMs));
|
|
912
|
+
await this.randomDelay();
|
|
512
913
|
}
|
|
513
914
|
payload.time_offset = timeOffset / 1e3;
|
|
514
915
|
await this.sendData("sync", payload);
|
|
@@ -527,6 +928,52 @@ var _ApitallyClient = class _ApitallyClient {
|
|
|
527
928
|
}
|
|
528
929
|
}
|
|
529
930
|
}
|
|
931
|
+
async sendLogData() {
|
|
932
|
+
this.logger.debug("Sending request log data to Apitally Hub");
|
|
933
|
+
await this.requestLogger.rotateFile();
|
|
934
|
+
const fetchWithRetry = fetchRetry(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
|
+
}
|
|
530
977
|
handleHubError(error) {
|
|
531
978
|
if (error instanceof HTTPError) {
|
|
532
979
|
if (error.response.status === 404) {
|
|
@@ -541,6 +988,10 @@ var _ApitallyClient = class _ApitallyClient {
|
|
|
541
988
|
}
|
|
542
989
|
return false;
|
|
543
990
|
}
|
|
991
|
+
async randomDelay() {
|
|
992
|
+
const delay = 100 + Math.random() * 200;
|
|
993
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
994
|
+
}
|
|
544
995
|
};
|
|
545
996
|
__name(_ApitallyClient, "ApitallyClient");
|
|
546
997
|
__publicField(_ApitallyClient, "instance");
|
|
@@ -595,19 +1046,20 @@ var getMiddleware = /* @__PURE__ */ __name((client) => {
|
|
|
595
1046
|
}
|
|
596
1047
|
throw error;
|
|
597
1048
|
} finally {
|
|
1049
|
+
const responseTime = performance.now() - startTime;
|
|
1050
|
+
const consumer = getConsumer(ctx);
|
|
1051
|
+
client.consumerRegistry.addOrUpdateConsumer(consumer);
|
|
598
1052
|
if (!path) {
|
|
599
1053
|
path = getPath(ctx);
|
|
600
1054
|
}
|
|
601
1055
|
if (path) {
|
|
602
1056
|
try {
|
|
603
|
-
const consumer = getConsumer(ctx);
|
|
604
|
-
client.consumerRegistry.addOrUpdateConsumer(consumer);
|
|
605
1057
|
client.requestCounter.addRequest({
|
|
606
1058
|
consumer: consumer == null ? void 0 : consumer.identifier,
|
|
607
1059
|
method: ctx.request.method,
|
|
608
1060
|
path,
|
|
609
1061
|
statusCode: statusCode || ctx.response.status,
|
|
610
|
-
responseTime
|
|
1062
|
+
responseTime,
|
|
611
1063
|
requestSize: ctx.request.length,
|
|
612
1064
|
responseSize: ctx.response.length
|
|
613
1065
|
});
|
|
@@ -618,6 +1070,24 @@ var getMiddleware = /* @__PURE__ */ __name((client) => {
|
|
|
618
1070
|
});
|
|
619
1071
|
}
|
|
620
1072
|
}
|
|
1073
|
+
if (client.requestLogger.enabled) {
|
|
1074
|
+
client.requestLogger.logRequest({
|
|
1075
|
+
timestamp: Date.now() / 1e3,
|
|
1076
|
+
method: ctx.request.method,
|
|
1077
|
+
path,
|
|
1078
|
+
url: ctx.request.href,
|
|
1079
|
+
headers: convertHeaders(ctx.request.headers),
|
|
1080
|
+
size: Number(ctx.request.headers["content-length"]),
|
|
1081
|
+
consumer: consumer == null ? void 0 : consumer.identifier,
|
|
1082
|
+
body: convertBody(ctx.request.body, ctx.request.get("content-type"))
|
|
1083
|
+
}, {
|
|
1084
|
+
statusCode: statusCode || ctx.response.status,
|
|
1085
|
+
responseTime: responseTime / 1e3,
|
|
1086
|
+
headers: convertHeaders(ctx.response.headers),
|
|
1087
|
+
size: Number(ctx.response.headers["content-length"]),
|
|
1088
|
+
body: convertBody(ctx.response.body, ctx.response.get("content-type"))
|
|
1089
|
+
});
|
|
1090
|
+
}
|
|
621
1091
|
}
|
|
622
1092
|
};
|
|
623
1093
|
}, "getMiddleware");
|