apitally 0.25.6 → 0.25.8
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/dist/common/client.cjs +12 -12
- package/dist/common/client.cjs.map +1 -1
- package/dist/common/client.d.cts +6 -6
- package/dist/common/client.d.ts +6 -6
- package/dist/common/client.js +6 -6
- package/dist/common/client.js.map +1 -1
- package/dist/common/consumerRegistry.cjs +2 -0
- package/dist/common/consumerRegistry.cjs.map +1 -1
- package/dist/common/consumerRegistry.d.cts +1 -1
- package/dist/common/consumerRegistry.d.ts +1 -1
- package/dist/common/consumerRegistry.js +1 -0
- package/dist/common/consumerRegistry.js.map +1 -1
- package/dist/common/requestCounter.cjs +5 -0
- package/dist/common/requestCounter.cjs.map +1 -1
- package/dist/common/requestCounter.d.cts +1 -1
- package/dist/common/requestCounter.d.ts +1 -1
- package/dist/common/requestCounter.js +1 -0
- package/dist/common/requestCounter.js.map +1 -1
- package/dist/common/requestLogger.cjs +53 -37
- package/dist/common/requestLogger.cjs.map +1 -1
- package/dist/common/requestLogger.d.cts +3 -2
- package/dist/common/requestLogger.d.ts +3 -2
- package/dist/common/requestLogger.js +51 -36
- package/dist/common/requestLogger.js.map +1 -1
- package/dist/common/serverErrorCounter.cjs +2 -0
- package/dist/common/serverErrorCounter.cjs.map +1 -1
- package/dist/common/serverErrorCounter.d.cts +1 -1
- package/dist/common/serverErrorCounter.d.ts +1 -1
- package/dist/common/serverErrorCounter.js +1 -0
- package/dist/common/serverErrorCounter.js.map +1 -1
- package/dist/common/spanCollector.cjs +3 -1
- package/dist/common/spanCollector.cjs.map +1 -1
- package/dist/common/spanCollector.d.cts +1 -1
- package/dist/common/spanCollector.d.ts +1 -1
- package/dist/common/spanCollector.js +1 -0
- package/dist/common/spanCollector.js.map +1 -1
- package/dist/common/tempGzipFile.cjs +11 -5
- package/dist/common/tempGzipFile.cjs.map +1 -1
- package/dist/common/tempGzipFile.d.cts +2 -2
- package/dist/common/tempGzipFile.d.ts +2 -2
- package/dist/common/tempGzipFile.js +10 -5
- package/dist/common/tempGzipFile.js.map +1 -1
- package/dist/common/validationErrorCounter.cjs +5 -0
- package/dist/common/validationErrorCounter.cjs.map +1 -1
- package/dist/common/validationErrorCounter.d.cts +1 -1
- package/dist/common/validationErrorCounter.d.ts +1 -1
- package/dist/common/validationErrorCounter.js +1 -0
- package/dist/common/validationErrorCounter.js.map +1 -1
- package/dist/elysia/index.cjs +2 -12
- package/dist/elysia/index.cjs.map +1 -1
- package/dist/elysia/index.d.cts +1 -1
- package/dist/elysia/index.d.ts +1 -1
- package/dist/elysia/index.js +2 -2
- package/dist/elysia/index.js.map +1 -1
- package/dist/elysia/plugin.cjs +5 -0
- package/dist/elysia/plugin.cjs.map +1 -1
- package/dist/elysia/plugin.d.cts +1 -1
- package/dist/elysia/plugin.d.ts +1 -1
- package/dist/elysia/plugin.js +1 -0
- package/dist/elysia/plugin.js.map +1 -1
- package/dist/fastify/index.cjs +2 -12
- package/dist/fastify/index.cjs.map +1 -1
- package/dist/fastify/index.d.cts +1 -1
- package/dist/fastify/index.d.ts +1 -1
- package/dist/fastify/index.js +2 -2
- package/dist/fastify/index.js.map +1 -1
- package/dist/fastify/plugin.cjs +4 -1
- package/dist/fastify/plugin.cjs.map +1 -1
- package/dist/fastify/plugin.d.cts +2 -2
- package/dist/fastify/plugin.d.ts +2 -2
- package/dist/fastify/plugin.js +3 -1
- package/dist/fastify/plugin.js.map +1 -1
- package/dist/hapi/index.cjs +2 -12
- package/dist/hapi/index.cjs.map +1 -1
- package/dist/hapi/index.d.cts +1 -1
- package/dist/hapi/index.d.ts +1 -1
- package/dist/hapi/index.js +2 -2
- package/dist/hapi/index.js.map +1 -1
- package/dist/hapi/plugin.cjs +2 -0
- package/dist/hapi/plugin.cjs.map +1 -1
- package/dist/hapi/plugin.d.cts +1 -1
- package/dist/hapi/plugin.d.ts +1 -1
- package/dist/hapi/plugin.js +1 -0
- package/dist/hapi/plugin.js.map +1 -1
- package/package.json +9 -3
|
@@ -29,6 +29,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
29
29
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
30
30
|
var requestLogger_exports = {};
|
|
31
31
|
__export(requestLogger_exports, {
|
|
32
|
+
RequestLogger: () => RequestLogger,
|
|
32
33
|
convertBody: () => convertBody,
|
|
33
34
|
convertHeaders: () => convertHeaders,
|
|
34
35
|
default: () => RequestLogger
|
|
@@ -39,7 +40,7 @@ var import_node_buffer = require("node:buffer");
|
|
|
39
40
|
var import_node_crypto = require("node:crypto");
|
|
40
41
|
var import_sentry = require('./sentry.cjs');
|
|
41
42
|
var import_serverErrorCounter = require('./serverErrorCounter.cjs');
|
|
42
|
-
var import_tempGzipFile =
|
|
43
|
+
var import_tempGzipFile = require('./tempGzipFile.cjs');
|
|
43
44
|
const MAX_BODY_SIZE = 5e4;
|
|
44
45
|
const MAX_FILE_SIZE = 1e6;
|
|
45
46
|
const MAX_FILES = 50;
|
|
@@ -128,6 +129,7 @@ const _RequestLogger = class _RequestLogger {
|
|
|
128
129
|
currentFile = null;
|
|
129
130
|
files = [];
|
|
130
131
|
maintainIntervalId;
|
|
132
|
+
maintainInProgress = false;
|
|
131
133
|
lock = new import_async_lock.default();
|
|
132
134
|
constructor(config) {
|
|
133
135
|
this.config = {
|
|
@@ -137,7 +139,8 @@ const _RequestLogger = class _RequestLogger {
|
|
|
137
139
|
this.enabled = this.config.enabled && (0, import_tempGzipFile.checkWritableFs)();
|
|
138
140
|
if (this.enabled) {
|
|
139
141
|
this.maintainIntervalId = setInterval(() => {
|
|
140
|
-
this.maintain()
|
|
142
|
+
this.maintain().catch(() => {
|
|
143
|
+
});
|
|
141
144
|
}, 1e3);
|
|
142
145
|
}
|
|
143
146
|
}
|
|
@@ -360,35 +363,39 @@ const _RequestLogger = class _RequestLogger {
|
|
|
360
363
|
return;
|
|
361
364
|
}
|
|
362
365
|
return this.lock.acquire("file", async () => {
|
|
366
|
+
const items = this.pendingWrites;
|
|
367
|
+
this.pendingWrites = [];
|
|
368
|
+
if (items.length === 0) {
|
|
369
|
+
return;
|
|
370
|
+
}
|
|
363
371
|
if (!this.currentFile) {
|
|
364
|
-
this.currentFile = new import_tempGzipFile.
|
|
372
|
+
this.currentFile = new import_tempGzipFile.TempGzipFile("request_logs");
|
|
365
373
|
}
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
await this.currentFile.writeLine(import_node_buffer.Buffer.from(JSON.stringify(finalItem)));
|
|
390
|
-
}
|
|
374
|
+
const lines = [];
|
|
375
|
+
for (let item of items) {
|
|
376
|
+
item = this.applyMasking(item);
|
|
377
|
+
const finalItem = {
|
|
378
|
+
uuid: item.uuid,
|
|
379
|
+
request: skipEmptyValues(item.request),
|
|
380
|
+
response: skipEmptyValues(item.response),
|
|
381
|
+
exception: item.exception,
|
|
382
|
+
logs: item.logs,
|
|
383
|
+
spans: item.spans,
|
|
384
|
+
traceId: item.traceId
|
|
385
|
+
};
|
|
386
|
+
[
|
|
387
|
+
finalItem.request.body,
|
|
388
|
+
finalItem.response.body
|
|
389
|
+
].forEach((body) => {
|
|
390
|
+
if (body) {
|
|
391
|
+
body.toJSON = function() {
|
|
392
|
+
return this.toString("base64");
|
|
393
|
+
};
|
|
394
|
+
}
|
|
395
|
+
});
|
|
396
|
+
lines.push(import_node_buffer.Buffer.from(JSON.stringify(finalItem)));
|
|
391
397
|
}
|
|
398
|
+
await this.currentFile.writeLines(lines);
|
|
392
399
|
});
|
|
393
400
|
}
|
|
394
401
|
getFile() {
|
|
@@ -407,16 +414,24 @@ const _RequestLogger = class _RequestLogger {
|
|
|
407
414
|
});
|
|
408
415
|
}
|
|
409
416
|
async maintain() {
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
await this.rotateFile();
|
|
413
|
-
}
|
|
414
|
-
while (this.files.length > MAX_FILES) {
|
|
415
|
-
const file = this.files.shift();
|
|
416
|
-
file == null ? void 0 : file.delete();
|
|
417
|
+
if (this.maintainInProgress) {
|
|
418
|
+
return;
|
|
417
419
|
}
|
|
418
|
-
|
|
419
|
-
|
|
420
|
+
this.maintainInProgress = true;
|
|
421
|
+
try {
|
|
422
|
+
await this.writeToFile();
|
|
423
|
+
if (this.currentFile && this.currentFile.size > MAX_FILE_SIZE) {
|
|
424
|
+
await this.rotateFile();
|
|
425
|
+
}
|
|
426
|
+
while (this.files.length > MAX_FILES) {
|
|
427
|
+
const file = this.files.shift();
|
|
428
|
+
file == null ? void 0 : file.delete();
|
|
429
|
+
}
|
|
430
|
+
if (this.suspendUntil !== null && this.suspendUntil < Date.now()) {
|
|
431
|
+
this.suspendUntil = null;
|
|
432
|
+
}
|
|
433
|
+
} finally {
|
|
434
|
+
this.maintainInProgress = false;
|
|
420
435
|
}
|
|
421
436
|
}
|
|
422
437
|
async clear() {
|
|
@@ -521,6 +536,7 @@ function truncateLogMessage(msg) {
|
|
|
521
536
|
__name(truncateLogMessage, "truncateLogMessage");
|
|
522
537
|
// Annotate the CommonJS export names for ESM import in node:
|
|
523
538
|
0 && (module.exports = {
|
|
539
|
+
RequestLogger,
|
|
524
540
|
convertBody,
|
|
525
541
|
convertHeaders
|
|
526
542
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/common/requestLogger.ts"],"sourcesContent":["import AsyncLock from \"async-lock\";\nimport { Buffer } from \"node:buffer\";\nimport { randomUUID } from \"node:crypto\";\nimport type { IncomingHttpHeaders, OutgoingHttpHeaders } from \"node:http\";\n\nimport { getSentryEventId } from \"./sentry.js\";\nimport {\n truncateExceptionMessage,\n truncateExceptionStackTrace,\n} from \"./serverErrorCounter.js\";\nimport type { SpanData } from \"./spanCollector.js\";\nimport TempGzipFile, { checkWritableFs } from \"./tempGzipFile.js\";\n\nconst MAX_BODY_SIZE = 50_000; // 50 KB (uncompressed)\nconst MAX_FILE_SIZE = 1_000_000; // 1 MB (compressed)\nconst MAX_FILES = 50;\nconst MAX_PENDING_WRITES = 100;\nconst MAX_LOG_MSG_LENGTH = 2048;\nconst BODY_TOO_LARGE = Buffer.from(\"<body too large>\");\nconst BODY_MASKED = Buffer.from(\"<masked>\");\nconst MASKED = \"******\";\nconst ALLOWED_CONTENT_TYPES = [\n \"application/json\",\n \"application/ld+json\",\n \"application/problem+json\",\n \"application/vnd.api+json\",\n \"application/x-ndjson\",\n \"text/plain\",\n \"text/html\",\n];\nconst EXCLUDE_PATH_PATTERNS = [\n /\\/_?healthz?$/i,\n /\\/_?health[_-]?checks?$/i,\n /\\/_?heart[_-]?beats?$/i,\n /\\/ping$/i,\n /\\/ready$/i,\n /\\/live$/i,\n /\\/favicon(?:-[\\w-]+)?\\.(ico|png|svg)$/,\n /\\/apple-touch-icon(?:-[\\w-]+)?\\.png$/,\n /\\/robots\\.txt$/,\n /\\/sitemap\\.xml$/,\n /\\/manifest\\.json$/,\n /\\/site\\.webmanifest$/,\n /\\/service-worker\\.js$/,\n /\\/sw\\.js$/,\n /\\/\\.well-known\\//,\n];\nconst EXCLUDE_USER_AGENT_PATTERNS = [\n /health[-_ ]?check/i,\n /microsoft-azure-application-lb/i,\n /googlehc/i,\n /kube-probe/i,\n];\nconst MASK_QUERY_PARAM_PATTERNS = [\n /auth/i,\n /api-?key/i,\n /secret/i,\n /token/i,\n /password/i,\n /pwd/i,\n];\nconst MASK_HEADER_PATTERNS = [\n /auth/i,\n /api-?key/i,\n /secret/i,\n /token/i,\n /cookie/i,\n];\nconst MASK_BODY_FIELD_PATTERNS = [\n /password/i,\n /pwd/i,\n /token/i,\n /secret/i,\n /auth/i,\n /card[-_ ]?number/i,\n /ccv/i,\n /ssn/i,\n];\n\nexport type Request = {\n timestamp: number;\n method: string;\n path?: string;\n url: string;\n headers: [string, string][];\n size?: number;\n consumer?: string;\n body?: Buffer;\n};\n\nexport type Response = {\n statusCode: number;\n responseTime: number;\n headers: [string, string][];\n size?: number;\n body?: Buffer;\n};\n\nexport type LogRecord = {\n timestamp: number;\n logger?: string;\n level: string;\n message: string;\n};\n\nexport type RequestLoggingConfig = {\n enabled: boolean;\n logQueryParams: boolean;\n logRequestHeaders: boolean;\n logRequestBody: boolean;\n logResponseHeaders: boolean;\n logResponseBody: boolean;\n logException: boolean;\n captureLogs: boolean;\n captureTraces: boolean;\n maskQueryParams: RegExp[];\n maskHeaders: RegExp[];\n maskBodyFields: RegExp[];\n maskRequestBodyCallback?: (request: Request) => Buffer | null | undefined;\n maskResponseBodyCallback?: (\n request: Request,\n response: Response,\n ) => Buffer | null | undefined;\n excludePaths: RegExp[];\n excludeCallback?: (request: Request, response: Response) => boolean;\n};\n\nconst DEFAULT_CONFIG: RequestLoggingConfig = {\n enabled: false,\n logQueryParams: true,\n logRequestHeaders: false,\n logRequestBody: false,\n logResponseHeaders: true,\n logResponseBody: false,\n logException: true,\n captureLogs: false,\n captureTraces: false,\n maskQueryParams: [],\n maskHeaders: [],\n maskBodyFields: [],\n excludePaths: [],\n};\n\ntype RequestLogItem = {\n uuid: string;\n request: Request;\n response: Response;\n exception?: {\n type: string;\n message: string;\n stacktrace: string;\n sentryEventId?: string;\n };\n logs?: LogRecord[];\n spans?: SpanData[];\n traceId?: string;\n};\n\nexport default class RequestLogger {\n public config: RequestLoggingConfig;\n public enabled: boolean;\n public suspendUntil: number | null = null;\n private pendingWrites: RequestLogItem[] = [];\n private currentFile: TempGzipFile | null = null;\n private files: TempGzipFile[] = [];\n private maintainIntervalId?: NodeJS.Timeout;\n private lock = new AsyncLock();\n\n constructor(config?: Partial<RequestLoggingConfig>) {\n this.config = { ...DEFAULT_CONFIG, ...config };\n this.enabled = this.config.enabled && checkWritableFs();\n\n if (this.enabled) {\n this.maintainIntervalId = setInterval(() => {\n this.maintain();\n }, 1000);\n }\n }\n\n get maxBodySize() {\n return MAX_BODY_SIZE;\n }\n\n private shouldExcludePath(urlPath: string) {\n const patterns = [...this.config.excludePaths, ...EXCLUDE_PATH_PATTERNS];\n return matchPatterns(urlPath, patterns);\n }\n\n private shouldExcludeUserAgent(userAgent?: string) {\n return userAgent\n ? matchPatterns(userAgent, EXCLUDE_USER_AGENT_PATTERNS)\n : false;\n }\n\n private shouldMaskQueryParam(name: string) {\n const patterns = [\n ...this.config.maskQueryParams,\n ...MASK_QUERY_PARAM_PATTERNS,\n ];\n return matchPatterns(name, patterns);\n }\n\n private shouldMaskHeader(name: string) {\n const patterns = [...this.config.maskHeaders, ...MASK_HEADER_PATTERNS];\n return matchPatterns(name, patterns);\n }\n\n private shouldMaskBodyField(name: string) {\n const patterns = [\n ...this.config.maskBodyFields,\n ...MASK_BODY_FIELD_PATTERNS,\n ];\n return matchPatterns(name, patterns);\n }\n\n private getContentType(headers: [string, string][]) {\n return headers.find(([k]) => k.toLowerCase() === \"content-type\")?.[1];\n }\n\n private hasSupportedContentType(headers: [string, string][]) {\n const contentType = this.getContentType(headers);\n return this.isSupportedContentType(contentType);\n }\n\n public isSupportedContentType(contentType?: string | string[] | null) {\n if (Array.isArray(contentType) && contentType.length > 0) {\n contentType = contentType[0];\n }\n return (\n typeof contentType === \"string\" &&\n ALLOWED_CONTENT_TYPES.some((t) => contentType.startsWith(t))\n );\n }\n\n private static isHttps(headers: [string, string][]): boolean {\n return headers.some(([k, v]) => {\n switch (k.toLowerCase()) {\n case \"x-forwarded-proto\":\n case \"x-forwarded-protocol\":\n case \"x-forwarded-scheme\":\n case \"x-url-scheme\":\n case \"x-scheme\":\n return v.split(\",\")[0].trim().toLowerCase() === \"https\";\n case \"front-end-https\":\n case \"x-forwarded-ssl\":\n return v.toLowerCase() === \"on\";\n case \"forwarded\":\n return v.split(\",\")[0].trim().toLowerCase().includes(\"proto=https\");\n default:\n return false;\n }\n });\n }\n\n private maskQueryParams(search: string) {\n const params = new URLSearchParams(search);\n for (const [key] of params) {\n if (this.shouldMaskQueryParam(key)) {\n params.set(key, MASKED);\n }\n }\n return params.toString();\n }\n\n private maskHeaders(headers: [string, string][]): [string, string][] {\n return headers.map(([k, v]) => [k, this.shouldMaskHeader(k) ? MASKED : v]);\n }\n\n private maskBody(data: any): any {\n if (typeof data === \"object\" && data !== null && !Array.isArray(data)) {\n const result: any = {};\n for (const [key, value] of Object.entries(data)) {\n if (typeof value === \"string\" && this.shouldMaskBodyField(key)) {\n result[key] = MASKED;\n } else {\n result[key] = this.maskBody(value);\n }\n }\n return result;\n }\n if (Array.isArray(data)) {\n return data.map((item) => this.maskBody(item));\n }\n return data;\n }\n\n private applyMasking(item: RequestLogItem) {\n // Apply user-provided maskRequestBodyCallback function\n if (\n this.config.maskRequestBodyCallback &&\n item.request.body &&\n item.request.body !== BODY_TOO_LARGE\n ) {\n try {\n const maskedBody = this.config.maskRequestBodyCallback(item.request);\n item.request.body = maskedBody ?? BODY_MASKED;\n } catch {\n item.request.body = undefined;\n }\n }\n\n // Apply user-provided maskResponseBodyCallback function\n if (\n this.config.maskResponseBodyCallback &&\n item.response.body &&\n item.response.body !== BODY_TOO_LARGE\n ) {\n try {\n const maskedBody = this.config.maskResponseBodyCallback(\n item.request,\n item.response,\n );\n item.response.body = maskedBody ?? BODY_MASKED;\n } catch {\n item.response.body = undefined;\n }\n }\n\n // Check request and response body sizes\n if (item.request.body && item.request.body.length > MAX_BODY_SIZE) {\n item.request.body = BODY_TOO_LARGE;\n }\n if (item.response.body && item.response.body.length > MAX_BODY_SIZE) {\n item.response.body = BODY_TOO_LARGE;\n }\n\n // Mask request and response body fields\n for (const key of [\"request\", \"response\"] as const) {\n const bodyData = item[key].body;\n if (\n !bodyData ||\n bodyData === BODY_TOO_LARGE ||\n bodyData === BODY_MASKED\n ) {\n continue;\n }\n\n try {\n const contentType = this.getContentType(item[key].headers);\n if (!contentType || /\\bjson\\b/i.test(contentType)) {\n const parsedBody = JSON.parse(bodyData.toString());\n const maskedBody = this.maskBody(parsedBody);\n item[key].body = Buffer.from(JSON.stringify(maskedBody));\n } else if (/\\bndjson\\b/i.test(contentType)) {\n const lines = bodyData\n .toString()\n .split(\"\\n\")\n .filter((line) => line.trim());\n const maskedLines = lines.map((line) => {\n try {\n const parsed = JSON.parse(line);\n const masked = this.maskBody(parsed);\n return JSON.stringify(masked);\n } catch {\n return line; // Keep unparseable lines as is\n }\n });\n item[key].body = Buffer.from(maskedLines.join(\"\\n\"));\n }\n } catch {\n // If parsing fails, leave body as is\n }\n }\n\n // Mask request and response headers\n item.request.headers = this.config.logRequestHeaders\n ? this.maskHeaders(item.request.headers)\n : [];\n item.response.headers = this.config.logResponseHeaders\n ? this.maskHeaders(item.response.headers)\n : [];\n\n // Mask query params\n const url = new URL(item.request.url);\n url.search = this.config.logQueryParams\n ? this.maskQueryParams(url.search)\n : \"\";\n item.request.url = url.toString();\n\n return item;\n }\n\n logRequest(\n request: Request,\n response: Response,\n error?: Error,\n logs?: LogRecord[],\n spans?: SpanData[],\n traceId?: string,\n ) {\n if (!this.enabled || this.suspendUntil !== null) return;\n\n const url = new URL(request.url);\n const path = request.path ?? url.pathname;\n const userAgent = request.headers.find(\n ([k]) => k.toLowerCase() === \"user-agent\",\n )?.[1];\n\n if (url.protocol === \"http:\" && RequestLogger.isHttps(request.headers)) {\n url.protocol = \"https:\";\n request.url = url.toString();\n }\n\n if (\n this.shouldExcludePath(path) ||\n this.shouldExcludeUserAgent(userAgent) ||\n (this.config.excludeCallback?.(request, response) ?? false)\n ) {\n return;\n }\n\n if (\n !this.config.logRequestBody ||\n !this.hasSupportedContentType(request.headers)\n ) {\n request.body = undefined;\n }\n if (\n !this.config.logResponseBody ||\n !this.hasSupportedContentType(response.headers)\n ) {\n response.body = undefined;\n }\n\n if (request.size !== undefined && request.size < 0) {\n request.size = undefined;\n }\n if (response.size !== undefined && response.size < 0) {\n response.size = undefined;\n }\n\n const item: RequestLogItem = {\n uuid: randomUUID(),\n request: request,\n response: response,\n exception:\n error && this.config.logException\n ? {\n type: error.name,\n message: truncateExceptionMessage(error.message),\n stacktrace: truncateExceptionStackTrace(error.stack || \"\"),\n sentryEventId: getSentryEventId(),\n }\n : undefined,\n };\n\n if (logs && logs.length > 0) {\n item.logs = logs.map((log) => ({\n timestamp: log.timestamp,\n logger: log.logger,\n level: log.level,\n message: truncateLogMessage(log.message),\n }));\n }\n if (spans && spans.length > 0) {\n item.spans = spans;\n }\n if (traceId) {\n item.traceId = traceId;\n }\n this.pendingWrites.push(item);\n\n if (this.pendingWrites.length > MAX_PENDING_WRITES) {\n this.pendingWrites.shift();\n }\n }\n\n async writeToFile() {\n if (!this.enabled || this.pendingWrites.length === 0) {\n return;\n }\n return this.lock.acquire(\"file\", async () => {\n if (!this.currentFile) {\n this.currentFile = new TempGzipFile(\"request_logs\");\n }\n while (this.pendingWrites.length > 0) {\n let item = this.pendingWrites.shift();\n if (item) {\n item = this.applyMasking(item);\n\n const finalItem = {\n uuid: item.uuid,\n request: skipEmptyValues(item.request),\n response: skipEmptyValues(item.response),\n exception: item.exception,\n logs: item.logs,\n spans: item.spans,\n traceId: item.traceId,\n };\n\n // Set up body serialization for JSON\n [finalItem.request.body, finalItem.response.body].forEach((body) => {\n if (body) {\n // @ts-expect-error Override Buffer's default JSON serialization\n body.toJSON = function () {\n return this.toString(\"base64\");\n };\n }\n });\n\n await this.currentFile.writeLine(\n Buffer.from(JSON.stringify(finalItem)),\n );\n }\n }\n });\n }\n\n getFile() {\n return this.files.shift();\n }\n\n retryFileLater(file: TempGzipFile) {\n this.files.unshift(file);\n }\n\n async rotateFile() {\n return this.lock.acquire(\"file\", async () => {\n if (this.currentFile) {\n await this.currentFile.close();\n this.files.push(this.currentFile);\n this.currentFile = null;\n }\n });\n }\n\n async maintain() {\n await this.writeToFile();\n if (this.currentFile && this.currentFile.size > MAX_FILE_SIZE) {\n await this.rotateFile();\n }\n while (this.files.length > MAX_FILES) {\n const file = this.files.shift();\n file?.delete();\n }\n if (this.suspendUntil !== null && this.suspendUntil < Date.now()) {\n this.suspendUntil = null;\n }\n }\n\n async clear() {\n this.pendingWrites = [];\n await this.rotateFile();\n this.files.forEach((file) => {\n file.delete();\n });\n this.files = [];\n }\n\n async close() {\n this.enabled = false;\n await this.clear();\n if (this.maintainIntervalId) {\n clearInterval(this.maintainIntervalId);\n }\n }\n}\n\nexport function convertHeaders(\n headers:\n | Headers\n | IncomingHttpHeaders\n | OutgoingHttpHeaders\n | Record<string, string | string[] | number | undefined>\n | undefined,\n) {\n if (!headers) {\n return [];\n }\n if (headers instanceof Headers) {\n return Array.from(headers.entries());\n }\n return Object.entries(headers).flatMap(([key, value]) => {\n if (value === undefined) {\n return [];\n }\n if (Array.isArray(value)) {\n return value.map((v) => [key, v]);\n }\n return [[key, value.toString()]];\n }) as [string, string][];\n}\n\nexport function convertBody(body: any, contentType?: string | null) {\n if (!body || !contentType) {\n return;\n }\n try {\n if (contentType.startsWith(\"application/json\")) {\n if (isValidJsonString(body)) {\n return Buffer.from(body);\n } else {\n return Buffer.from(JSON.stringify(body));\n }\n }\n if (contentType.startsWith(\"text/\") && typeof body === \"string\") {\n return Buffer.from(body);\n }\n } catch (error) {\n return;\n }\n}\n\nfunction isValidJsonString(body: any) {\n if (typeof body !== \"string\") {\n return false;\n }\n try {\n JSON.parse(body);\n return true;\n } catch {\n return false;\n }\n}\n\nfunction matchPatterns(value: string, patterns: RegExp[]) {\n return patterns.some((pattern) => {\n return pattern.test(value);\n });\n}\n\nfunction skipEmptyValues<T extends Record<string, any>>(data: T) {\n return Object.fromEntries(\n Object.entries(data).filter(([_, v]) => {\n if (v == null || Number.isNaN(v)) return false;\n if (Array.isArray(v) || Buffer.isBuffer(v) || typeof v === \"string\") {\n return v.length > 0;\n }\n return true;\n }),\n ) as Partial<T>;\n}\n\nfunction truncateLogMessage(msg: string) {\n if (msg.length > MAX_LOG_MSG_LENGTH) {\n const suffix = \"... (truncated)\";\n return msg.slice(0, MAX_LOG_MSG_LENGTH - suffix.length) + suffix;\n }\n return msg;\n}\n"],"mappings":"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","names":["MAX_BODY_SIZE","MAX_FILE_SIZE","MAX_FILES","MAX_PENDING_WRITES","MAX_LOG_MSG_LENGTH","BODY_TOO_LARGE","Buffer","from","BODY_MASKED","MASKED","ALLOWED_CONTENT_TYPES","EXCLUDE_PATH_PATTERNS","EXCLUDE_USER_AGENT_PATTERNS","MASK_QUERY_PARAM_PATTERNS","MASK_HEADER_PATTERNS","MASK_BODY_FIELD_PATTERNS","DEFAULT_CONFIG","enabled","logQueryParams","logRequestHeaders","logRequestBody","logResponseHeaders","logResponseBody","logException","captureLogs","captureTraces","maskQueryParams","maskHeaders","maskBodyFields","excludePaths","RequestLogger","config","suspendUntil","pendingWrites","currentFile","files","maintainIntervalId","lock","AsyncLock","checkWritableFs","setInterval","maintain","maxBodySize","shouldExcludePath","urlPath","patterns","matchPatterns","shouldExcludeUserAgent","userAgent","shouldMaskQueryParam","name","shouldMaskHeader","shouldMaskBodyField","getContentType","headers","find","k","toLowerCase","hasSupportedContentType","contentType","isSupportedContentType","Array","isArray","length","some","t","startsWith","isHttps","v","split","trim","includes","search","params","URLSearchParams","key","set","toString","map","maskBody","data","result","value","Object","entries","item","applyMasking","maskRequestBodyCallback","request","body","maskedBody","undefined","maskResponseBodyCallback","response","bodyData","test","parsedBody","JSON","parse","stringify","lines","filter","line","maskedLines","parsed","masked","join","url","URL","logRequest","error","logs","spans","traceId","path","pathname","protocol","excludeCallback","size","uuid","randomUUID","exception","type","message","truncateExceptionMessage","stacktrace","truncateExceptionStackTrace","stack","sentryEventId","getSentryEventId","log","timestamp","logger","level","truncateLogMessage","push","shift","writeToFile","acquire","TempGzipFile","finalItem","skipEmptyValues","forEach","toJSON","writeLine","getFile","retryFileLater","file","unshift","rotateFile","close","delete","Date","now","clear","clearInterval","convertHeaders","Headers","flatMap","convertBody","isValidJsonString","pattern","fromEntries","_","Number","isNaN","isBuffer","msg","suffix","slice"]}
|
|
1
|
+
{"version":3,"sources":["../../src/common/requestLogger.ts"],"sourcesContent":["import AsyncLock from \"async-lock\";\nimport { Buffer } from \"node:buffer\";\nimport { randomUUID } from \"node:crypto\";\nimport type { IncomingHttpHeaders, OutgoingHttpHeaders } from \"node:http\";\n\nimport { getSentryEventId } from \"./sentry.js\";\nimport {\n truncateExceptionMessage,\n truncateExceptionStackTrace,\n} from \"./serverErrorCounter.js\";\nimport type { SpanData } from \"./spanCollector.js\";\nimport { checkWritableFs, TempGzipFile } from \"./tempGzipFile.js\";\n\nconst MAX_BODY_SIZE = 50_000; // 50 KB (uncompressed)\nconst MAX_FILE_SIZE = 1_000_000; // 1 MB (compressed)\nconst MAX_FILES = 50;\nconst MAX_PENDING_WRITES = 100;\nconst MAX_LOG_MSG_LENGTH = 2048;\nconst BODY_TOO_LARGE = Buffer.from(\"<body too large>\");\nconst BODY_MASKED = Buffer.from(\"<masked>\");\nconst MASKED = \"******\";\nconst ALLOWED_CONTENT_TYPES = [\n \"application/json\",\n \"application/ld+json\",\n \"application/problem+json\",\n \"application/vnd.api+json\",\n \"application/x-ndjson\",\n \"text/plain\",\n \"text/html\",\n];\nconst EXCLUDE_PATH_PATTERNS = [\n /\\/_?healthz?$/i,\n /\\/_?health[_-]?checks?$/i,\n /\\/_?heart[_-]?beats?$/i,\n /\\/ping$/i,\n /\\/ready$/i,\n /\\/live$/i,\n /\\/favicon(?:-[\\w-]+)?\\.(ico|png|svg)$/,\n /\\/apple-touch-icon(?:-[\\w-]+)?\\.png$/,\n /\\/robots\\.txt$/,\n /\\/sitemap\\.xml$/,\n /\\/manifest\\.json$/,\n /\\/site\\.webmanifest$/,\n /\\/service-worker\\.js$/,\n /\\/sw\\.js$/,\n /\\/\\.well-known\\//,\n];\nconst EXCLUDE_USER_AGENT_PATTERNS = [\n /health[-_ ]?check/i,\n /microsoft-azure-application-lb/i,\n /googlehc/i,\n /kube-probe/i,\n];\nconst MASK_QUERY_PARAM_PATTERNS = [\n /auth/i,\n /api-?key/i,\n /secret/i,\n /token/i,\n /password/i,\n /pwd/i,\n];\nconst MASK_HEADER_PATTERNS = [\n /auth/i,\n /api-?key/i,\n /secret/i,\n /token/i,\n /cookie/i,\n];\nconst MASK_BODY_FIELD_PATTERNS = [\n /password/i,\n /pwd/i,\n /token/i,\n /secret/i,\n /auth/i,\n /card[-_ ]?number/i,\n /ccv/i,\n /ssn/i,\n];\n\nexport type Request = {\n timestamp: number;\n method: string;\n path?: string;\n url: string;\n headers: [string, string][];\n size?: number;\n consumer?: string;\n body?: Buffer;\n};\n\nexport type Response = {\n statusCode: number;\n responseTime: number;\n headers: [string, string][];\n size?: number;\n body?: Buffer;\n};\n\nexport type LogRecord = {\n timestamp: number;\n logger?: string;\n level: string;\n message: string;\n};\n\nexport type RequestLoggingConfig = {\n enabled: boolean;\n logQueryParams: boolean;\n logRequestHeaders: boolean;\n logRequestBody: boolean;\n logResponseHeaders: boolean;\n logResponseBody: boolean;\n logException: boolean;\n captureLogs: boolean;\n captureTraces: boolean;\n maskQueryParams: RegExp[];\n maskHeaders: RegExp[];\n maskBodyFields: RegExp[];\n maskRequestBodyCallback?: (request: Request) => Buffer | null | undefined;\n maskResponseBodyCallback?: (\n request: Request,\n response: Response,\n ) => Buffer | null | undefined;\n excludePaths: RegExp[];\n excludeCallback?: (request: Request, response: Response) => boolean;\n};\n\nconst DEFAULT_CONFIG: RequestLoggingConfig = {\n enabled: false,\n logQueryParams: true,\n logRequestHeaders: false,\n logRequestBody: false,\n logResponseHeaders: true,\n logResponseBody: false,\n logException: true,\n captureLogs: false,\n captureTraces: false,\n maskQueryParams: [],\n maskHeaders: [],\n maskBodyFields: [],\n excludePaths: [],\n};\n\ntype RequestLogItem = {\n uuid: string;\n request: Request;\n response: Response;\n exception?: {\n type: string;\n message: string;\n stacktrace: string;\n sentryEventId?: string;\n };\n logs?: LogRecord[];\n spans?: SpanData[];\n traceId?: string;\n};\n\nexport default class RequestLogger {\n public config: RequestLoggingConfig;\n public enabled: boolean;\n public suspendUntil: number | null = null;\n private pendingWrites: RequestLogItem[] = [];\n private currentFile: TempGzipFile | null = null;\n private files: TempGzipFile[] = [];\n private maintainIntervalId?: NodeJS.Timeout;\n private maintainInProgress = false;\n private lock = new AsyncLock();\n\n constructor(config?: Partial<RequestLoggingConfig>) {\n this.config = { ...DEFAULT_CONFIG, ...config };\n this.enabled = this.config.enabled && checkWritableFs();\n\n if (this.enabled) {\n this.maintainIntervalId = setInterval(() => {\n this.maintain().catch(() => {});\n }, 1000);\n }\n }\n\n get maxBodySize() {\n return MAX_BODY_SIZE;\n }\n\n private shouldExcludePath(urlPath: string) {\n const patterns = [...this.config.excludePaths, ...EXCLUDE_PATH_PATTERNS];\n return matchPatterns(urlPath, patterns);\n }\n\n private shouldExcludeUserAgent(userAgent?: string) {\n return userAgent\n ? matchPatterns(userAgent, EXCLUDE_USER_AGENT_PATTERNS)\n : false;\n }\n\n private shouldMaskQueryParam(name: string) {\n const patterns = [\n ...this.config.maskQueryParams,\n ...MASK_QUERY_PARAM_PATTERNS,\n ];\n return matchPatterns(name, patterns);\n }\n\n private shouldMaskHeader(name: string) {\n const patterns = [...this.config.maskHeaders, ...MASK_HEADER_PATTERNS];\n return matchPatterns(name, patterns);\n }\n\n private shouldMaskBodyField(name: string) {\n const patterns = [\n ...this.config.maskBodyFields,\n ...MASK_BODY_FIELD_PATTERNS,\n ];\n return matchPatterns(name, patterns);\n }\n\n private getContentType(headers: [string, string][]) {\n return headers.find(([k]) => k.toLowerCase() === \"content-type\")?.[1];\n }\n\n private hasSupportedContentType(headers: [string, string][]) {\n const contentType = this.getContentType(headers);\n return this.isSupportedContentType(contentType);\n }\n\n public isSupportedContentType(contentType?: string | string[] | null) {\n if (Array.isArray(contentType) && contentType.length > 0) {\n contentType = contentType[0];\n }\n return (\n typeof contentType === \"string\" &&\n ALLOWED_CONTENT_TYPES.some((t) => contentType.startsWith(t))\n );\n }\n\n private static isHttps(headers: [string, string][]): boolean {\n return headers.some(([k, v]) => {\n switch (k.toLowerCase()) {\n case \"x-forwarded-proto\":\n case \"x-forwarded-protocol\":\n case \"x-forwarded-scheme\":\n case \"x-url-scheme\":\n case \"x-scheme\":\n return v.split(\",\")[0].trim().toLowerCase() === \"https\";\n case \"front-end-https\":\n case \"x-forwarded-ssl\":\n return v.toLowerCase() === \"on\";\n case \"forwarded\":\n return v.split(\",\")[0].trim().toLowerCase().includes(\"proto=https\");\n default:\n return false;\n }\n });\n }\n\n private maskQueryParams(search: string) {\n const params = new URLSearchParams(search);\n for (const [key] of params) {\n if (this.shouldMaskQueryParam(key)) {\n params.set(key, MASKED);\n }\n }\n return params.toString();\n }\n\n private maskHeaders(headers: [string, string][]): [string, string][] {\n return headers.map(([k, v]) => [k, this.shouldMaskHeader(k) ? MASKED : v]);\n }\n\n private maskBody(data: any): any {\n if (typeof data === \"object\" && data !== null && !Array.isArray(data)) {\n const result: any = {};\n for (const [key, value] of Object.entries(data)) {\n if (typeof value === \"string\" && this.shouldMaskBodyField(key)) {\n result[key] = MASKED;\n } else {\n result[key] = this.maskBody(value);\n }\n }\n return result;\n }\n if (Array.isArray(data)) {\n return data.map((item) => this.maskBody(item));\n }\n return data;\n }\n\n private applyMasking(item: RequestLogItem) {\n // Apply user-provided maskRequestBodyCallback function\n if (\n this.config.maskRequestBodyCallback &&\n item.request.body &&\n item.request.body !== BODY_TOO_LARGE\n ) {\n try {\n const maskedBody = this.config.maskRequestBodyCallback(item.request);\n item.request.body = maskedBody ?? BODY_MASKED;\n } catch {\n item.request.body = undefined;\n }\n }\n\n // Apply user-provided maskResponseBodyCallback function\n if (\n this.config.maskResponseBodyCallback &&\n item.response.body &&\n item.response.body !== BODY_TOO_LARGE\n ) {\n try {\n const maskedBody = this.config.maskResponseBodyCallback(\n item.request,\n item.response,\n );\n item.response.body = maskedBody ?? BODY_MASKED;\n } catch {\n item.response.body = undefined;\n }\n }\n\n // Check request and response body sizes\n if (item.request.body && item.request.body.length > MAX_BODY_SIZE) {\n item.request.body = BODY_TOO_LARGE;\n }\n if (item.response.body && item.response.body.length > MAX_BODY_SIZE) {\n item.response.body = BODY_TOO_LARGE;\n }\n\n // Mask request and response body fields\n for (const key of [\"request\", \"response\"] as const) {\n const bodyData = item[key].body;\n if (\n !bodyData ||\n bodyData === BODY_TOO_LARGE ||\n bodyData === BODY_MASKED\n ) {\n continue;\n }\n\n try {\n const contentType = this.getContentType(item[key].headers);\n if (!contentType || /\\bjson\\b/i.test(contentType)) {\n const parsedBody = JSON.parse(bodyData.toString());\n const maskedBody = this.maskBody(parsedBody);\n item[key].body = Buffer.from(JSON.stringify(maskedBody));\n } else if (/\\bndjson\\b/i.test(contentType)) {\n const lines = bodyData\n .toString()\n .split(\"\\n\")\n .filter((line) => line.trim());\n const maskedLines = lines.map((line) => {\n try {\n const parsed = JSON.parse(line);\n const masked = this.maskBody(parsed);\n return JSON.stringify(masked);\n } catch {\n return line; // Keep unparseable lines as is\n }\n });\n item[key].body = Buffer.from(maskedLines.join(\"\\n\"));\n }\n } catch {\n // If parsing fails, leave body as is\n }\n }\n\n // Mask request and response headers\n item.request.headers = this.config.logRequestHeaders\n ? this.maskHeaders(item.request.headers)\n : [];\n item.response.headers = this.config.logResponseHeaders\n ? this.maskHeaders(item.response.headers)\n : [];\n\n // Mask query params\n const url = new URL(item.request.url);\n url.search = this.config.logQueryParams\n ? this.maskQueryParams(url.search)\n : \"\";\n item.request.url = url.toString();\n\n return item;\n }\n\n logRequest(\n request: Request,\n response: Response,\n error?: Error,\n logs?: LogRecord[],\n spans?: SpanData[],\n traceId?: string,\n ) {\n if (!this.enabled || this.suspendUntil !== null) return;\n\n const url = new URL(request.url);\n const path = request.path ?? url.pathname;\n const userAgent = request.headers.find(\n ([k]) => k.toLowerCase() === \"user-agent\",\n )?.[1];\n\n if (url.protocol === \"http:\" && RequestLogger.isHttps(request.headers)) {\n url.protocol = \"https:\";\n request.url = url.toString();\n }\n\n if (\n this.shouldExcludePath(path) ||\n this.shouldExcludeUserAgent(userAgent) ||\n (this.config.excludeCallback?.(request, response) ?? false)\n ) {\n return;\n }\n\n if (\n !this.config.logRequestBody ||\n !this.hasSupportedContentType(request.headers)\n ) {\n request.body = undefined;\n }\n if (\n !this.config.logResponseBody ||\n !this.hasSupportedContentType(response.headers)\n ) {\n response.body = undefined;\n }\n\n if (request.size !== undefined && request.size < 0) {\n request.size = undefined;\n }\n if (response.size !== undefined && response.size < 0) {\n response.size = undefined;\n }\n\n const item: RequestLogItem = {\n uuid: randomUUID(),\n request: request,\n response: response,\n exception:\n error && this.config.logException\n ? {\n type: error.name,\n message: truncateExceptionMessage(error.message),\n stacktrace: truncateExceptionStackTrace(error.stack || \"\"),\n sentryEventId: getSentryEventId(),\n }\n : undefined,\n };\n\n if (logs && logs.length > 0) {\n item.logs = logs.map((log) => ({\n timestamp: log.timestamp,\n logger: log.logger,\n level: log.level,\n message: truncateLogMessage(log.message),\n }));\n }\n if (spans && spans.length > 0) {\n item.spans = spans;\n }\n if (traceId) {\n item.traceId = traceId;\n }\n this.pendingWrites.push(item);\n\n if (this.pendingWrites.length > MAX_PENDING_WRITES) {\n this.pendingWrites.shift();\n }\n }\n\n async writeToFile() {\n if (!this.enabled || this.pendingWrites.length === 0) {\n return;\n }\n return this.lock.acquire(\"file\", async () => {\n // Snapshot the queue so the drain is bounded by what was pending when\n // the lock was acquired. Items pushed during this drain land in a fresh\n // array and are picked up on the next tick. Without this snapshot,\n // under sustained traffic the while-loop could hold the lock for far\n // longer than the maintain() interval, stacking up subsequent acquires.\n const items = this.pendingWrites;\n this.pendingWrites = [];\n if (items.length === 0) {\n return;\n }\n if (!this.currentFile) {\n this.currentFile = new TempGzipFile(\"request_logs\");\n }\n\n const lines: Buffer[] = [];\n for (let item of items) {\n item = this.applyMasking(item);\n\n const finalItem = {\n uuid: item.uuid,\n request: skipEmptyValues(item.request),\n response: skipEmptyValues(item.response),\n exception: item.exception,\n logs: item.logs,\n spans: item.spans,\n traceId: item.traceId,\n };\n\n // Set up body serialization for JSON\n [finalItem.request.body, finalItem.response.body].forEach((body) => {\n if (body) {\n // @ts-expect-error Override Buffer's default JSON serialization\n body.toJSON = function () {\n return this.toString(\"base64\");\n };\n }\n });\n\n lines.push(Buffer.from(JSON.stringify(finalItem)));\n }\n\n await this.currentFile.writeLines(lines);\n });\n }\n\n getFile() {\n return this.files.shift();\n }\n\n retryFileLater(file: TempGzipFile) {\n this.files.unshift(file);\n }\n\n async rotateFile() {\n return this.lock.acquire(\"file\", async () => {\n if (this.currentFile) {\n await this.currentFile.close();\n this.files.push(this.currentFile);\n this.currentFile = null;\n }\n });\n }\n\n async maintain() {\n // Non-reentrant: if a previous maintain() is still running, drop this\n // tick. Without this, every interval tick that arrives while the\n // current drain is in progress would push another acquire onto the\n // \"file\" lock queue, eventually exceeding async-lock's maxPending.\n if (this.maintainInProgress) {\n return;\n }\n this.maintainInProgress = true;\n try {\n await this.writeToFile();\n if (this.currentFile && this.currentFile.size > MAX_FILE_SIZE) {\n await this.rotateFile();\n }\n while (this.files.length > MAX_FILES) {\n const file = this.files.shift();\n file?.delete();\n }\n if (this.suspendUntil !== null && this.suspendUntil < Date.now()) {\n this.suspendUntil = null;\n }\n } finally {\n this.maintainInProgress = false;\n }\n }\n\n async clear() {\n this.pendingWrites = [];\n await this.rotateFile();\n this.files.forEach((file) => {\n file.delete();\n });\n this.files = [];\n }\n\n async close() {\n this.enabled = false;\n await this.clear();\n if (this.maintainIntervalId) {\n clearInterval(this.maintainIntervalId);\n }\n }\n}\n\nexport function convertHeaders(\n headers:\n | Headers\n | IncomingHttpHeaders\n | OutgoingHttpHeaders\n | Record<string, string | string[] | number | undefined>\n | undefined,\n) {\n if (!headers) {\n return [];\n }\n if (headers instanceof Headers) {\n return Array.from(headers.entries());\n }\n return Object.entries(headers).flatMap(([key, value]) => {\n if (value === undefined) {\n return [];\n }\n if (Array.isArray(value)) {\n return value.map((v) => [key, v]);\n }\n return [[key, value.toString()]];\n }) as [string, string][];\n}\n\nexport function convertBody(body: any, contentType?: string | null) {\n if (!body || !contentType) {\n return;\n }\n try {\n if (contentType.startsWith(\"application/json\")) {\n if (isValidJsonString(body)) {\n return Buffer.from(body);\n } else {\n return Buffer.from(JSON.stringify(body));\n }\n }\n if (contentType.startsWith(\"text/\") && typeof body === \"string\") {\n return Buffer.from(body);\n }\n } catch (error) {\n return;\n }\n}\n\nfunction isValidJsonString(body: any) {\n if (typeof body !== \"string\") {\n return false;\n }\n try {\n JSON.parse(body);\n return true;\n } catch {\n return false;\n }\n}\n\nfunction matchPatterns(value: string, patterns: RegExp[]) {\n return patterns.some((pattern) => {\n return pattern.test(value);\n });\n}\n\nfunction skipEmptyValues<T extends Record<string, any>>(data: T) {\n return Object.fromEntries(\n Object.entries(data).filter(([_, v]) => {\n if (v == null || Number.isNaN(v)) return false;\n if (Array.isArray(v) || Buffer.isBuffer(v) || typeof v === \"string\") {\n return v.length > 0;\n }\n return true;\n }),\n ) as Partial<T>;\n}\n\nfunction truncateLogMessage(msg: string) {\n if (msg.length > MAX_LOG_MSG_LENGTH) {\n const suffix = \"... (truncated)\";\n return msg.slice(0, MAX_LOG_MSG_LENGTH - suffix.length) + suffix;\n }\n return msg;\n}\n\nexport { RequestLogger };\n"],"mappings":"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","names":["MAX_BODY_SIZE","MAX_FILE_SIZE","MAX_FILES","MAX_PENDING_WRITES","MAX_LOG_MSG_LENGTH","BODY_TOO_LARGE","Buffer","from","BODY_MASKED","MASKED","ALLOWED_CONTENT_TYPES","EXCLUDE_PATH_PATTERNS","EXCLUDE_USER_AGENT_PATTERNS","MASK_QUERY_PARAM_PATTERNS","MASK_HEADER_PATTERNS","MASK_BODY_FIELD_PATTERNS","DEFAULT_CONFIG","enabled","logQueryParams","logRequestHeaders","logRequestBody","logResponseHeaders","logResponseBody","logException","captureLogs","captureTraces","maskQueryParams","maskHeaders","maskBodyFields","excludePaths","RequestLogger","config","suspendUntil","pendingWrites","currentFile","files","maintainIntervalId","maintainInProgress","lock","AsyncLock","checkWritableFs","setInterval","maintain","catch","maxBodySize","shouldExcludePath","urlPath","patterns","matchPatterns","shouldExcludeUserAgent","userAgent","shouldMaskQueryParam","name","shouldMaskHeader","shouldMaskBodyField","getContentType","headers","find","k","toLowerCase","hasSupportedContentType","contentType","isSupportedContentType","Array","isArray","length","some","t","startsWith","isHttps","v","split","trim","includes","search","params","URLSearchParams","key","set","toString","map","maskBody","data","result","value","Object","entries","item","applyMasking","maskRequestBodyCallback","request","body","maskedBody","undefined","maskResponseBodyCallback","response","bodyData","test","parsedBody","JSON","parse","stringify","lines","filter","line","maskedLines","parsed","masked","join","url","URL","logRequest","error","logs","spans","traceId","path","pathname","protocol","excludeCallback","size","uuid","randomUUID","exception","type","message","truncateExceptionMessage","stacktrace","truncateExceptionStackTrace","stack","sentryEventId","getSentryEventId","log","timestamp","logger","level","truncateLogMessage","push","shift","writeToFile","acquire","items","TempGzipFile","finalItem","skipEmptyValues","forEach","toJSON","writeLines","getFile","retryFileLater","file","unshift","rotateFile","close","delete","Date","now","clear","clearInterval","convertHeaders","Headers","flatMap","convertBody","isValidJsonString","pattern","fromEntries","_","Number","isNaN","isBuffer","msg","suffix","slice"]}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Buffer } from 'node:buffer';
|
|
2
2
|
import { IncomingHttpHeaders, OutgoingHttpHeaders } from 'node:http';
|
|
3
3
|
import { SpanData } from './spanCollector.cjs';
|
|
4
|
-
import TempGzipFile from './tempGzipFile.cjs';
|
|
4
|
+
import { TempGzipFile } from './tempGzipFile.cjs';
|
|
5
5
|
import '@opentelemetry/sdk-trace-base';
|
|
6
6
|
|
|
7
7
|
type Request = {
|
|
@@ -53,6 +53,7 @@ declare class RequestLogger {
|
|
|
53
53
|
private currentFile;
|
|
54
54
|
private files;
|
|
55
55
|
private maintainIntervalId?;
|
|
56
|
+
private maintainInProgress;
|
|
56
57
|
private lock;
|
|
57
58
|
constructor(config?: Partial<RequestLoggingConfig>);
|
|
58
59
|
get maxBodySize(): number;
|
|
@@ -81,4 +82,4 @@ declare class RequestLogger {
|
|
|
81
82
|
declare function convertHeaders(headers: Headers | IncomingHttpHeaders | OutgoingHttpHeaders | Record<string, string | string[] | number | undefined> | undefined): [string, string][];
|
|
82
83
|
declare function convertBody(body: any, contentType?: string | null): Buffer<any> | undefined;
|
|
83
84
|
|
|
84
|
-
export { type LogRecord, type Request, type RequestLoggingConfig, type Response, convertBody, convertHeaders, RequestLogger as default };
|
|
85
|
+
export { type LogRecord, type Request, RequestLogger, type RequestLoggingConfig, type Response, convertBody, convertHeaders, RequestLogger as default };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Buffer } from 'node:buffer';
|
|
2
2
|
import { IncomingHttpHeaders, OutgoingHttpHeaders } from 'node:http';
|
|
3
3
|
import { SpanData } from './spanCollector.js';
|
|
4
|
-
import TempGzipFile from './tempGzipFile.js';
|
|
4
|
+
import { TempGzipFile } from './tempGzipFile.js';
|
|
5
5
|
import '@opentelemetry/sdk-trace-base';
|
|
6
6
|
|
|
7
7
|
type Request = {
|
|
@@ -53,6 +53,7 @@ declare class RequestLogger {
|
|
|
53
53
|
private currentFile;
|
|
54
54
|
private files;
|
|
55
55
|
private maintainIntervalId?;
|
|
56
|
+
private maintainInProgress;
|
|
56
57
|
private lock;
|
|
57
58
|
constructor(config?: Partial<RequestLoggingConfig>);
|
|
58
59
|
get maxBodySize(): number;
|
|
@@ -81,4 +82,4 @@ declare class RequestLogger {
|
|
|
81
82
|
declare function convertHeaders(headers: Headers | IncomingHttpHeaders | OutgoingHttpHeaders | Record<string, string | string[] | number | undefined> | undefined): [string, string][];
|
|
82
83
|
declare function convertBody(body: any, contentType?: string | null): Buffer<any> | undefined;
|
|
83
84
|
|
|
84
|
-
export { type LogRecord, type Request, type RequestLoggingConfig, type Response, convertBody, convertHeaders, RequestLogger as default };
|
|
85
|
+
export { type LogRecord, type Request, RequestLogger, type RequestLoggingConfig, type Response, convertBody, convertHeaders, RequestLogger as default };
|
|
@@ -5,7 +5,7 @@ import { Buffer as Buffer2 } from "node:buffer";
|
|
|
5
5
|
import { randomUUID } from "node:crypto";
|
|
6
6
|
import { getSentryEventId } from "./sentry.js";
|
|
7
7
|
import { truncateExceptionMessage, truncateExceptionStackTrace } from "./serverErrorCounter.js";
|
|
8
|
-
import
|
|
8
|
+
import { checkWritableFs, TempGzipFile } from "./tempGzipFile.js";
|
|
9
9
|
const MAX_BODY_SIZE = 5e4;
|
|
10
10
|
const MAX_FILE_SIZE = 1e6;
|
|
11
11
|
const MAX_FILES = 50;
|
|
@@ -94,6 +94,7 @@ const _RequestLogger = class _RequestLogger {
|
|
|
94
94
|
currentFile = null;
|
|
95
95
|
files = [];
|
|
96
96
|
maintainIntervalId;
|
|
97
|
+
maintainInProgress = false;
|
|
97
98
|
lock = new AsyncLock();
|
|
98
99
|
constructor(config) {
|
|
99
100
|
this.config = {
|
|
@@ -103,7 +104,8 @@ const _RequestLogger = class _RequestLogger {
|
|
|
103
104
|
this.enabled = this.config.enabled && checkWritableFs();
|
|
104
105
|
if (this.enabled) {
|
|
105
106
|
this.maintainIntervalId = setInterval(() => {
|
|
106
|
-
this.maintain()
|
|
107
|
+
this.maintain().catch(() => {
|
|
108
|
+
});
|
|
107
109
|
}, 1e3);
|
|
108
110
|
}
|
|
109
111
|
}
|
|
@@ -326,35 +328,39 @@ const _RequestLogger = class _RequestLogger {
|
|
|
326
328
|
return;
|
|
327
329
|
}
|
|
328
330
|
return this.lock.acquire("file", async () => {
|
|
331
|
+
const items = this.pendingWrites;
|
|
332
|
+
this.pendingWrites = [];
|
|
333
|
+
if (items.length === 0) {
|
|
334
|
+
return;
|
|
335
|
+
}
|
|
329
336
|
if (!this.currentFile) {
|
|
330
337
|
this.currentFile = new TempGzipFile("request_logs");
|
|
331
338
|
}
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
await this.currentFile.writeLine(Buffer2.from(JSON.stringify(finalItem)));
|
|
356
|
-
}
|
|
339
|
+
const lines = [];
|
|
340
|
+
for (let item of items) {
|
|
341
|
+
item = this.applyMasking(item);
|
|
342
|
+
const finalItem = {
|
|
343
|
+
uuid: item.uuid,
|
|
344
|
+
request: skipEmptyValues(item.request),
|
|
345
|
+
response: skipEmptyValues(item.response),
|
|
346
|
+
exception: item.exception,
|
|
347
|
+
logs: item.logs,
|
|
348
|
+
spans: item.spans,
|
|
349
|
+
traceId: item.traceId
|
|
350
|
+
};
|
|
351
|
+
[
|
|
352
|
+
finalItem.request.body,
|
|
353
|
+
finalItem.response.body
|
|
354
|
+
].forEach((body) => {
|
|
355
|
+
if (body) {
|
|
356
|
+
body.toJSON = function() {
|
|
357
|
+
return this.toString("base64");
|
|
358
|
+
};
|
|
359
|
+
}
|
|
360
|
+
});
|
|
361
|
+
lines.push(Buffer2.from(JSON.stringify(finalItem)));
|
|
357
362
|
}
|
|
363
|
+
await this.currentFile.writeLines(lines);
|
|
358
364
|
});
|
|
359
365
|
}
|
|
360
366
|
getFile() {
|
|
@@ -373,16 +379,24 @@ const _RequestLogger = class _RequestLogger {
|
|
|
373
379
|
});
|
|
374
380
|
}
|
|
375
381
|
async maintain() {
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
await this.rotateFile();
|
|
379
|
-
}
|
|
380
|
-
while (this.files.length > MAX_FILES) {
|
|
381
|
-
const file = this.files.shift();
|
|
382
|
-
file == null ? void 0 : file.delete();
|
|
382
|
+
if (this.maintainInProgress) {
|
|
383
|
+
return;
|
|
383
384
|
}
|
|
384
|
-
|
|
385
|
-
|
|
385
|
+
this.maintainInProgress = true;
|
|
386
|
+
try {
|
|
387
|
+
await this.writeToFile();
|
|
388
|
+
if (this.currentFile && this.currentFile.size > MAX_FILE_SIZE) {
|
|
389
|
+
await this.rotateFile();
|
|
390
|
+
}
|
|
391
|
+
while (this.files.length > MAX_FILES) {
|
|
392
|
+
const file = this.files.shift();
|
|
393
|
+
file == null ? void 0 : file.delete();
|
|
394
|
+
}
|
|
395
|
+
if (this.suspendUntil !== null && this.suspendUntil < Date.now()) {
|
|
396
|
+
this.suspendUntil = null;
|
|
397
|
+
}
|
|
398
|
+
} finally {
|
|
399
|
+
this.maintainInProgress = false;
|
|
386
400
|
}
|
|
387
401
|
}
|
|
388
402
|
async clear() {
|
|
@@ -486,6 +500,7 @@ function truncateLogMessage(msg) {
|
|
|
486
500
|
}
|
|
487
501
|
__name(truncateLogMessage, "truncateLogMessage");
|
|
488
502
|
export {
|
|
503
|
+
RequestLogger,
|
|
489
504
|
convertBody,
|
|
490
505
|
convertHeaders,
|
|
491
506
|
RequestLogger as default
|