apitally 0.24.0 → 0.25.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adonisjs/index.cjs +3 -0
- package/dist/adonisjs/index.cjs.map +1 -1
- package/dist/adonisjs/index.d.cts +2 -0
- package/dist/adonisjs/index.d.ts +2 -0
- package/dist/adonisjs/index.js +2 -0
- package/dist/adonisjs/index.js.map +1 -1
- package/dist/adonisjs/middleware.cjs +6 -2
- package/dist/adonisjs/middleware.cjs.map +1 -1
- package/dist/adonisjs/middleware.d.cts +2 -0
- package/dist/adonisjs/middleware.d.ts +2 -0
- package/dist/adonisjs/middleware.js +6 -2
- package/dist/adonisjs/middleware.js.map +1 -1
- package/dist/adonisjs/provider.d.cts +2 -0
- package/dist/adonisjs/provider.d.ts +2 -0
- package/dist/adonisjs/stubs/config/apitally.stub +1 -0
- package/dist/common/client.cjs +4 -0
- package/dist/common/client.cjs.map +1 -1
- package/dist/common/client.d.cts +3 -0
- package/dist/common/client.d.ts +3 -0
- package/dist/common/client.js +4 -0
- package/dist/common/client.js.map +1 -1
- package/dist/common/consumerRegistry.d.cts +2 -0
- package/dist/common/consumerRegistry.d.ts +2 -0
- package/dist/common/requestCounter.d.cts +2 -0
- package/dist/common/requestCounter.d.ts +2 -0
- package/dist/common/requestLogger.cjs +11 -2
- package/dist/common/requestLogger.cjs.map +1 -1
- package/dist/common/requestLogger.d.cts +6 -1
- package/dist/common/requestLogger.d.ts +6 -1
- package/dist/common/requestLogger.js +11 -2
- package/dist/common/requestLogger.js.map +1 -1
- package/dist/common/serverErrorCounter.d.cts +2 -0
- package/dist/common/serverErrorCounter.d.ts +2 -0
- package/dist/common/spanCollector.cjs +217 -0
- package/dist/common/spanCollector.cjs.map +1 -0
- package/dist/common/spanCollector.d.cts +50 -0
- package/dist/common/spanCollector.d.ts +50 -0
- package/dist/common/spanCollector.js +194 -0
- package/dist/common/spanCollector.js.map +1 -0
- package/dist/common/types.d.cts +2 -0
- package/dist/common/types.d.ts +2 -0
- package/dist/common/validationErrorCounter.d.cts +2 -0
- package/dist/common/validationErrorCounter.d.ts +2 -0
- package/dist/elysia/index.cjs +3 -0
- package/dist/elysia/index.cjs.map +1 -1
- package/dist/elysia/index.d.cts +2 -0
- package/dist/elysia/index.d.ts +2 -0
- package/dist/elysia/index.js +2 -0
- package/dist/elysia/index.js.map +1 -1
- package/dist/elysia/plugin.cjs +9 -1
- package/dist/elysia/plugin.cjs.map +1 -1
- package/dist/elysia/plugin.d.cts +4 -0
- package/dist/elysia/plugin.d.ts +4 -0
- package/dist/elysia/plugin.js +9 -1
- package/dist/elysia/plugin.js.map +1 -1
- package/dist/elysia/utils.d.cts +2 -0
- package/dist/elysia/utils.d.ts +2 -0
- package/dist/express/index.cjs +3 -0
- package/dist/express/index.cjs.map +1 -1
- package/dist/express/index.d.cts +2 -0
- package/dist/express/index.d.ts +2 -0
- package/dist/express/index.js +2 -0
- package/dist/express/index.js.map +1 -1
- package/dist/express/middleware.cjs +85 -77
- package/dist/express/middleware.cjs.map +1 -1
- package/dist/express/middleware.d.cts +2 -0
- package/dist/express/middleware.d.ts +2 -0
- package/dist/express/middleware.js +85 -77
- package/dist/express/middleware.js.map +1 -1
- package/dist/fastify/index.cjs +3 -0
- package/dist/fastify/index.cjs.map +1 -1
- package/dist/fastify/index.d.cts +2 -0
- package/dist/fastify/index.d.ts +2 -0
- package/dist/fastify/index.js +2 -0
- package/dist/fastify/index.js.map +1 -1
- package/dist/fastify/plugin.cjs +14 -5
- 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 +14 -5
- package/dist/fastify/plugin.js.map +1 -1
- package/dist/h3/index.cjs +3 -0
- package/dist/h3/index.cjs.map +1 -1
- package/dist/h3/index.d.cts +2 -0
- package/dist/h3/index.d.ts +2 -0
- package/dist/h3/index.js +2 -0
- package/dist/h3/index.js.map +1 -1
- package/dist/h3/plugin.cjs +13 -5
- package/dist/h3/plugin.cjs.map +1 -1
- package/dist/h3/plugin.d.cts +4 -0
- package/dist/h3/plugin.d.ts +4 -0
- package/dist/h3/plugin.js +13 -5
- package/dist/h3/plugin.js.map +1 -1
- package/dist/hapi/index.cjs +3 -0
- package/dist/hapi/index.cjs.map +1 -1
- package/dist/hapi/index.d.cts +2 -0
- package/dist/hapi/index.d.ts +2 -0
- package/dist/hapi/index.js +2 -0
- package/dist/hapi/index.js.map +1 -1
- package/dist/hapi/plugin.cjs +10 -3
- package/dist/hapi/plugin.cjs.map +1 -1
- package/dist/hapi/plugin.d.cts +4 -0
- package/dist/hapi/plugin.d.ts +4 -0
- package/dist/hapi/plugin.js +10 -3
- package/dist/hapi/plugin.js.map +1 -1
- package/dist/hapi/utils.d.cts +2 -0
- package/dist/hapi/utils.d.ts +2 -0
- package/dist/hono/index.cjs +3 -0
- package/dist/hono/index.cjs.map +1 -1
- package/dist/hono/index.d.cts +2 -0
- package/dist/hono/index.d.ts +2 -0
- package/dist/hono/index.js +2 -0
- package/dist/hono/index.js.map +1 -1
- package/dist/hono/middleware.cjs +6 -2
- package/dist/hono/middleware.cjs.map +1 -1
- package/dist/hono/middleware.d.cts +2 -0
- package/dist/hono/middleware.d.ts +2 -0
- package/dist/hono/middleware.js +6 -2
- package/dist/hono/middleware.js.map +1 -1
- package/dist/hono/utils.d.cts +2 -0
- package/dist/hono/utils.d.ts +2 -0
- package/dist/koa/index.cjs +3 -0
- package/dist/koa/index.cjs.map +1 -1
- package/dist/koa/index.d.cts +2 -0
- package/dist/koa/index.d.ts +2 -0
- package/dist/koa/index.js +2 -0
- package/dist/koa/index.js.map +1 -1
- package/dist/koa/middleware.cjs +8 -4
- package/dist/koa/middleware.cjs.map +1 -1
- package/dist/koa/middleware.d.cts +2 -0
- package/dist/koa/middleware.d.ts +2 -0
- package/dist/koa/middleware.js +8 -4
- package/dist/koa/middleware.js.map +1 -1
- package/dist/loggers/console.d.cts +4 -0
- package/dist/loggers/console.d.ts +4 -0
- package/dist/loggers/hapi.d.cts +4 -0
- package/dist/loggers/hapi.d.ts +4 -0
- package/dist/loggers/index.d.cts +4 -0
- package/dist/loggers/index.d.ts +4 -0
- package/dist/loggers/nestjs.d.cts +4 -0
- package/dist/loggers/nestjs.d.ts +4 -0
- package/dist/loggers/pino.d.cts +4 -0
- package/dist/loggers/pino.d.ts +4 -0
- package/dist/loggers/winston.d.cts +4 -0
- package/dist/loggers/winston.d.ts +4 -0
- package/dist/nestjs/index.cjs +3 -0
- package/dist/nestjs/index.cjs.map +1 -1
- package/dist/nestjs/index.d.cts +2 -0
- package/dist/nestjs/index.d.ts +2 -0
- package/dist/nestjs/index.js +2 -0
- package/dist/nestjs/index.js.map +1 -1
- package/package.json +5 -2
|
@@ -4,6 +4,8 @@ import 'winston';
|
|
|
4
4
|
import './requestLogger.js';
|
|
5
5
|
import 'node:buffer';
|
|
6
6
|
import 'node:http';
|
|
7
|
+
import './spanCollector.js';
|
|
8
|
+
import '@opentelemetry/sdk-trace-base';
|
|
7
9
|
import './tempGzipFile.js';
|
|
8
10
|
|
|
9
11
|
declare const consumerFromStringOrObject: (consumer: ApitallyConsumer | string) => ApitallyConsumer | null;
|
|
@@ -114,6 +114,7 @@ const DEFAULT_CONFIG = {
|
|
|
114
114
|
logResponseBody: false,
|
|
115
115
|
logException: true,
|
|
116
116
|
captureLogs: false,
|
|
117
|
+
captureTraces: false,
|
|
117
118
|
maskQueryParams: [],
|
|
118
119
|
maskHeaders: [],
|
|
119
120
|
maskBodyFields: [],
|
|
@@ -277,7 +278,7 @@ const _RequestLogger = class _RequestLogger {
|
|
|
277
278
|
item.request.url = url.toString();
|
|
278
279
|
return item;
|
|
279
280
|
}
|
|
280
|
-
logRequest(request, response, error, logs) {
|
|
281
|
+
logRequest(request, response, error, logs, spans, traceId) {
|
|
281
282
|
var _a, _b, _c;
|
|
282
283
|
if (!this.enabled || this.suspendUntil !== null) return;
|
|
283
284
|
const url = new URL(request.url);
|
|
@@ -317,6 +318,12 @@ const _RequestLogger = class _RequestLogger {
|
|
|
317
318
|
message: truncateLogMessage(log.message)
|
|
318
319
|
}));
|
|
319
320
|
}
|
|
321
|
+
if (spans && spans.length > 0) {
|
|
322
|
+
item.spans = spans;
|
|
323
|
+
}
|
|
324
|
+
if (traceId) {
|
|
325
|
+
item.traceId = traceId;
|
|
326
|
+
}
|
|
320
327
|
this.pendingWrites.push(item);
|
|
321
328
|
if (this.pendingWrites.length > MAX_PENDING_WRITES) {
|
|
322
329
|
this.pendingWrites.shift();
|
|
@@ -339,7 +346,9 @@ const _RequestLogger = class _RequestLogger {
|
|
|
339
346
|
request: skipEmptyValues(item.request),
|
|
340
347
|
response: skipEmptyValues(item.response),
|
|
341
348
|
exception: item.exception,
|
|
342
|
-
logs: item.logs
|
|
349
|
+
logs: item.logs,
|
|
350
|
+
spans: item.spans,
|
|
351
|
+
traceId: item.traceId
|
|
343
352
|
};
|
|
344
353
|
[
|
|
345
354
|
finalItem.request.body,
|
|
@@ -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 { IncomingHttpHeaders, OutgoingHttpHeaders } from \"node:http\";\n\nimport { getSentryEventId } from \"./sentry.js\";\nimport {\n truncateExceptionMessage,\n truncateExceptionStackTrace,\n} from \"./serverErrorCounter.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 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 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};\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 | null) {\n return (\n typeof contentType === \"string\" &&\n ALLOWED_CONTENT_TYPES.some((t) => contentType.startsWith(t))\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 ) {\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 (\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 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 };\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","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","some","t","startsWith","search","params","URLSearchParams","key","set","toString","map","v","maskBody","data","Array","isArray","result","value","Object","entries","item","applyMasking","maskRequestBodyCallback","request","body","maskedBody","undefined","maskResponseBodyCallback","response","length","bodyData","test","parsedBody","JSON","parse","stringify","lines","split","filter","line","trim","maskedLines","parsed","masked","join","url","URL","logRequest","error","logs","path","pathname","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 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 | null) {\n return (\n typeof contentType === \"string\" &&\n ALLOWED_CONTENT_TYPES.some((t) => contentType.startsWith(t))\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 (\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":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;;;;;;;AAAA,wBAAsB;AACtB,yBAAuB;AACvB,yBAA2B;AAG3B,oBAAiC;AACjC,gCAGO;AAEP,0BAA8C;AAE9C,MAAMA,gBAAgB;AACtB,MAAMC,gBAAgB;AACtB,MAAMC,YAAY;AAClB,MAAMC,qBAAqB;AAC3B,MAAMC,qBAAqB;AAC3B,MAAMC,iBAAiBC,0BAAOC,KAAK,kBAAA;AACnC,MAAMC,cAAcF,0BAAOC,KAAK,UAAA;AAChC,MAAME,SAAS;AACf,MAAMC,wBAAwB;EAC5B;EACA;EACA;EACA;EACA;EACA;EACA;;AAEF,MAAMC,wBAAwB;EAC5B;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEF,MAAMC,8BAA8B;EAClC;EACA;EACA;EACA;;AAEF,MAAMC,4BAA4B;EAChC;EACA;EACA;EACA;EACA;EACA;;AAEF,MAAMC,uBAAuB;EAC3B;EACA;EACA;EACA;EACA;;AAEF,MAAMC,2BAA2B;EAC/B;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAmDF,MAAMC,iBAAuC;EAC3CC,SAAS;EACTC,gBAAgB;EAChBC,mBAAmB;EACnBC,gBAAgB;EAChBC,oBAAoB;EACpBC,iBAAiB;EACjBC,cAAc;EACdC,aAAa;EACbC,eAAe;EACfC,iBAAiB,CAAA;EACjBC,aAAa,CAAA;EACbC,gBAAgB,CAAA;EAChBC,cAAc,CAAA;AAChB;AAiBA,MAAqBC,iBAArB,MAAqBA,eAAAA;EACZC;EACAd;EACAe,eAA8B;EAC7BC,gBAAkC,CAAA;EAClCC,cAAmC;EACnCC,QAAwB,CAAA;EACxBC;EACAC,OAAO,IAAIC,kBAAAA,QAAAA;EAEnB,YAAYP,QAAwC;AAClD,SAAKA,SAAS;MAAE,GAAGf;MAAgB,GAAGe;IAAO;AAC7C,SAAKd,UAAU,KAAKc,OAAOd,eAAWsB,qCAAAA;AAEtC,QAAI,KAAKtB,SAAS;AAChB,WAAKmB,qBAAqBI,YAAY,MAAA;AACpC,aAAKC,SAAQ;MACf,GAAG,GAAA;IACL;EACF;EAEA,IAAIC,cAAc;AAChB,WAAO1C;EACT;EAEQ2C,kBAAkBC,SAAiB;AACzC,UAAMC,WAAW;SAAI,KAAKd,OAAOF;SAAiBlB;;AAClD,WAAOmC,cAAcF,SAASC,QAAAA;EAChC;EAEQE,uBAAuBC,WAAoB;AACjD,WAAOA,YACHF,cAAcE,WAAWpC,2BAAAA,IACzB;EACN;EAEQqC,qBAAqBC,MAAc;AACzC,UAAML,WAAW;SACZ,KAAKd,OAAOL;SACZb;;AAEL,WAAOiC,cAAcI,MAAML,QAAAA;EAC7B;EAEQM,iBAAiBD,MAAc;AACrC,UAAML,WAAW;SAAI,KAAKd,OAAOJ;SAAgBb;;AACjD,WAAOgC,cAAcI,MAAML,QAAAA;EAC7B;EAEQO,oBAAoBF,MAAc;AACxC,UAAML,WAAW;SACZ,KAAKd,OAAOH;SACZb;;AAEL,WAAO+B,cAAcI,MAAML,QAAAA;EAC7B;EAEQQ,eAAeC,SAA6B;AAvNtD;AAwNI,YAAOA,aAAQC,KAAK,CAAC,CAACC,CAAAA,MAAOA,EAAEC,YAAW,MAAO,cAAA,MAA1CH,mBAA4D;EACrE;EAEQI,wBAAwBJ,SAA6B;AAC3D,UAAMK,cAAc,KAAKN,eAAeC,OAAAA;AACxC,WAAO,KAAKM,uBAAuBD,WAAAA;EACrC;EAEOC,uBAAuBD,aAA6B;AACzD,WACE,OAAOA,gBAAgB,YACvBjD,sBAAsBmD,KAAK,CAACC,MAAMH,YAAYI,WAAWD,CAAAA,CAAAA;EAE7D;EAEQpC,gBAAgBsC,QAAgB;AACtC,UAAMC,SAAS,IAAIC,gBAAgBF,MAAAA;AACnC,eAAW,CAACG,GAAAA,KAAQF,QAAQ;AAC1B,UAAI,KAAKhB,qBAAqBkB,GAAAA,GAAM;AAClCF,eAAOG,IAAID,KAAK1D,MAAAA;MAClB;IACF;AACA,WAAOwD,OAAOI,SAAQ;EACxB;EAEQ1C,YAAY2B,SAAiD;AACnE,WAAOA,QAAQgB,IAAI,CAAC,CAACd,GAAGe,CAAAA,MAAO;MAACf;MAAG,KAAKL,iBAAiBK,CAAAA,IAAK/C,SAAS8D;KAAE;EAC3E;EAEQC,SAASC,MAAgB;AAC/B,QAAI,OAAOA,SAAS,YAAYA,SAAS,QAAQ,CAACC,MAAMC,QAAQF,IAAAA,GAAO;AACrE,YAAMG,SAAc,CAAC;AACrB,iBAAW,CAACT,KAAKU,KAAAA,KAAUC,OAAOC,QAAQN,IAAAA,GAAO;AAC/C,YAAI,OAAOI,UAAU,YAAY,KAAKzB,oBAAoBe,GAAAA,GAAM;AAC9DS,iBAAOT,GAAAA,IAAO1D;QAChB,OAAO;AACLmE,iBAAOT,GAAAA,IAAO,KAAKK,SAASK,KAAAA;QAC9B;MACF;AACA,aAAOD;IACT;AACA,QAAIF,MAAMC,QAAQF,IAAAA,GAAO;AACvB,aAAOA,KAAKH,IAAI,CAACU,SAAS,KAAKR,SAASQ,IAAAA,CAAAA;IAC1C;AACA,WAAOP;EACT;EAEQQ,aAAaD,MAAsB;AAEzC,QACE,KAAKjD,OAAOmD,2BACZF,KAAKG,QAAQC,QACbJ,KAAKG,QAAQC,SAAS/E,gBACtB;AACA,UAAI;AACF,cAAMgF,aAAa,KAAKtD,OAAOmD,wBAAwBF,KAAKG,OAAO;AACnEH,aAAKG,QAAQC,OAAOC,cAAc7E;MACpC,QAAQ;AACNwE,aAAKG,QAAQC,OAAOE;MACtB;IACF;AAGA,QACE,KAAKvD,OAAOwD,4BACZP,KAAKQ,SAASJ,QACdJ,KAAKQ,SAASJ,SAAS/E,gBACvB;AACA,UAAI;AACF,cAAMgF,aAAa,KAAKtD,OAAOwD,yBAC7BP,KAAKG,SACLH,KAAKQ,QAAQ;AAEfR,aAAKQ,SAASJ,OAAOC,cAAc7E;MACrC,QAAQ;AACNwE,aAAKQ,SAASJ,OAAOE;MACvB;IACF;AAGA,QAAIN,KAAKG,QAAQC,QAAQJ,KAAKG,QAAQC,KAAKK,SAASzF,eAAe;AACjEgF,WAAKG,QAAQC,OAAO/E;IACtB;AACA,QAAI2E,KAAKQ,SAASJ,QAAQJ,KAAKQ,SAASJ,KAAKK,SAASzF,eAAe;AACnEgF,WAAKQ,SAASJ,OAAO/E;IACvB;AAGA,eAAW8D,OAAO;MAAC;MAAW;OAAsB;AAClD,YAAMuB,WAAWV,KAAKb,GAAAA,EAAKiB;AAC3B,UACE,CAACM,YACDA,aAAarF,kBACbqF,aAAalF,aACb;AACA;MACF;AAEA,UAAI;AACF,cAAMmD,cAAc,KAAKN,eAAe2B,KAAKb,GAAAA,EAAKb,OAAO;AACzD,YAAI,CAACK,eAAe,YAAYgC,KAAKhC,WAAAA,GAAc;AACjD,gBAAMiC,aAAaC,KAAKC,MAAMJ,SAASrB,SAAQ,CAAA;AAC/C,gBAAMgB,aAAa,KAAKb,SAASoB,UAAAA;AACjCZ,eAAKb,GAAAA,EAAKiB,OAAO9E,0BAAOC,KAAKsF,KAAKE,UAAUV,UAAAA,CAAAA;QAC9C,WAAW,cAAcM,KAAKhC,WAAAA,GAAc;AAC1C,gBAAMqC,QAAQN,SACXrB,SAAQ,EACR4B,MAAM,IAAA,EACNC,OAAO,CAACC,SAASA,KAAKC,KAAI,CAAA;AAC7B,gBAAMC,cAAcL,MAAM1B,IAAI,CAAC6B,SAAAA;AAC7B,gBAAI;AACF,oBAAMG,SAAST,KAAKC,MAAMK,IAAAA;AAC1B,oBAAMI,SAAS,KAAK/B,SAAS8B,MAAAA;AAC7B,qBAAOT,KAAKE,UAAUQ,MAAAA;YACxB,QAAQ;AACN,qBAAOJ;YACT;UACF,CAAA;AACAnB,eAAKb,GAAAA,EAAKiB,OAAO9E,0BAAOC,KAAK8F,YAAYG,KAAK,IAAA,CAAA;QAChD;MACF,QAAQ;MAER;IACF;AAGAxB,SAAKG,QAAQ7B,UAAU,KAAKvB,OAAOZ,oBAC/B,KAAKQ,YAAYqD,KAAKG,QAAQ7B,OAAO,IACrC,CAAA;AACJ0B,SAAKQ,SAASlC,UAAU,KAAKvB,OAAOV,qBAChC,KAAKM,YAAYqD,KAAKQ,SAASlC,OAAO,IACtC,CAAA;AAGJ,UAAMmD,MAAM,IAAIC,IAAI1B,KAAKG,QAAQsB,GAAG;AACpCA,QAAIzC,SAAS,KAAKjC,OAAOb,iBACrB,KAAKQ,gBAAgB+E,IAAIzC,MAAM,IAC/B;AACJgB,SAAKG,QAAQsB,MAAMA,IAAIpC,SAAQ;AAE/B,WAAOW;EACT;EAEA2B,WACExB,SACAK,UACAoB,OACAC,MACAC,OACAC,SACA;AA9WJ;AA+WI,QAAI,CAAC,KAAK9F,WAAW,KAAKe,iBAAiB,KAAM;AAEjD,UAAMyE,MAAM,IAAIC,IAAIvB,QAAQsB,GAAG;AAC/B,UAAMO,OAAO7B,QAAQ6B,QAAQP,IAAIQ;AACjC,UAAMjE,aAAYmC,aAAQ7B,QAAQC,KAChC,CAAC,CAACC,CAAAA,MAAOA,EAAEC,YAAW,MAAO,YAAA,MADb0B,mBAEd;AAEJ,QACE,KAAKxC,kBAAkBqE,IAAAA,KACvB,KAAKjE,uBAAuBC,SAAAA,QAC3B,gBAAKjB,QAAOmF,oBAAZ,4BAA8B/B,SAASK,cAAa,QACrD;AACA;IACF;AAEA,QACE,CAAC,KAAKzD,OAAOX,kBACb,CAAC,KAAKsC,wBAAwByB,QAAQ7B,OAAO,GAC7C;AACA6B,cAAQC,OAAOE;IACjB;AACA,QACE,CAAC,KAAKvD,OAAOT,mBACb,CAAC,KAAKoC,wBAAwB8B,SAASlC,OAAO,GAC9C;AACAkC,eAASJ,OAAOE;IAClB;AAEA,QAAIH,QAAQgC,SAAS7B,UAAaH,QAAQgC,OAAO,GAAG;AAClDhC,cAAQgC,OAAO7B;IACjB;AACA,QAAIE,SAAS2B,SAAS7B,UAAaE,SAAS2B,OAAO,GAAG;AACpD3B,eAAS2B,OAAO7B;IAClB;AAEA,UAAMN,OAAuB;MAC3BoC,UAAMC,+BAAAA;MACNlC;MACAK;MACA8B,WACEV,SAAS,KAAK7E,OAAOR,eACjB;QACEgG,MAAMX,MAAM1D;QACZsE,aAASC,oDAAyBb,MAAMY,OAAO;QAC/CE,gBAAYC,uDAA4Bf,MAAMgB,SAAS,EAAA;QACvDC,mBAAeC,gCAAAA;MACjB,IACAxC;IACR;AAEA,QAAIuB,QAAQA,KAAKpB,SAAS,GAAG;AAC3BT,WAAK6B,OAAOA,KAAKvC,IAAI,CAACyD,SAAS;QAC7BC,WAAWD,IAAIC;QACfC,QAAQF,IAAIE;QACZC,OAAOH,IAAIG;QACXV,SAASW,mBAAmBJ,IAAIP,OAAO;MACzC,EAAA;IACF;AACA,QAAIV,SAASA,MAAMrB,SAAS,GAAG;AAC7BT,WAAK8B,QAAQA;IACf;AACA,QAAIC,SAAS;AACX/B,WAAK+B,UAAUA;IACjB;AACA,SAAK9E,cAAcmG,KAAKpD,IAAAA;AAExB,QAAI,KAAK/C,cAAcwD,SAAStF,oBAAoB;AAClD,WAAK8B,cAAcoG,MAAK;IAC1B;EACF;EAEA,MAAMC,cAAc;AAClB,QAAI,CAAC,KAAKrH,WAAW,KAAKgB,cAAcwD,WAAW,GAAG;AACpD;IACF;AACA,WAAO,KAAKpD,KAAKkG,QAAQ,QAAQ,YAAA;AAC/B,UAAI,CAAC,KAAKrG,aAAa;AACrB,aAAKA,cAAc,IAAIsG,oBAAAA,QAAa,cAAA;MACtC;AACA,aAAO,KAAKvG,cAAcwD,SAAS,GAAG;AACpC,YAAIT,OAAO,KAAK/C,cAAcoG,MAAK;AACnC,YAAIrD,MAAM;AACRA,iBAAO,KAAKC,aAAaD,IAAAA;AAEzB,gBAAMyD,YAAY;YAChBrB,MAAMpC,KAAKoC;YACXjC,SAASuD,gBAAgB1D,KAAKG,OAAO;YACrCK,UAAUkD,gBAAgB1D,KAAKQ,QAAQ;YACvC8B,WAAWtC,KAAKsC;YAChBT,MAAM7B,KAAK6B;YACXC,OAAO9B,KAAK8B;YACZC,SAAS/B,KAAK+B;UAChB;AAGA;YAAC0B,UAAUtD,QAAQC;YAAMqD,UAAUjD,SAASJ;YAAMuD,QAAQ,CAACvD,SAAAA;AACzD,gBAAIA,MAAM;AAERA,mBAAKwD,SAAS,WAAA;AACZ,uBAAO,KAAKvE,SAAS,QAAA;cACvB;YACF;UACF,CAAA;AAEA,gBAAM,KAAKnC,YAAY2G,UACrBvI,0BAAOC,KAAKsF,KAAKE,UAAU0C,SAAAA,CAAAA,CAAAA;QAE/B;MACF;IACF,CAAA;EACF;EAEAK,UAAU;AACR,WAAO,KAAK3G,MAAMkG,MAAK;EACzB;EAEAU,eAAeC,MAAoB;AACjC,SAAK7G,MAAM8G,QAAQD,IAAAA;EACrB;EAEA,MAAME,aAAa;AACjB,WAAO,KAAK7G,KAAKkG,QAAQ,QAAQ,YAAA;AAC/B,UAAI,KAAKrG,aAAa;AACpB,cAAM,KAAKA,YAAYiH,MAAK;AAC5B,aAAKhH,MAAMiG,KAAK,KAAKlG,WAAW;AAChC,aAAKA,cAAc;MACrB;IACF,CAAA;EACF;EAEA,MAAMO,WAAW;AACf,UAAM,KAAK6F,YAAW;AACtB,QAAI,KAAKpG,eAAe,KAAKA,YAAYiF,OAAOlH,eAAe;AAC7D,YAAM,KAAKiJ,WAAU;IACvB;AACA,WAAO,KAAK/G,MAAMsD,SAASvF,WAAW;AACpC,YAAM8I,OAAO,KAAK7G,MAAMkG,MAAK;AAC7BW,mCAAMI;IACR;AACA,QAAI,KAAKpH,iBAAiB,QAAQ,KAAKA,eAAeqH,KAAKC,IAAG,GAAI;AAChE,WAAKtH,eAAe;IACtB;EACF;EAEA,MAAMuH,QAAQ;AACZ,SAAKtH,gBAAgB,CAAA;AACrB,UAAM,KAAKiH,WAAU;AACrB,SAAK/G,MAAMwG,QAAQ,CAACK,SAAAA;AAClBA,WAAKI,OAAM;IACb,CAAA;AACA,SAAKjH,QAAQ,CAAA;EACf;EAEA,MAAMgH,QAAQ;AACZ,SAAKlI,UAAU;AACf,UAAM,KAAKsI,MAAK;AAChB,QAAI,KAAKnH,oBAAoB;AAC3BoH,oBAAc,KAAKpH,kBAAkB;IACvC;EACF;AACF;AAlXqBN;AAArB,IAAqBA,gBAArB;AAoXO,SAAS2H,eACdnG,SAKa;AAEb,MAAI,CAACA,SAAS;AACZ,WAAO,CAAA;EACT;AACA,MAAIA,mBAAmBoG,SAAS;AAC9B,WAAOhF,MAAMnE,KAAK+C,QAAQyB,QAAO,CAAA;EACnC;AACA,SAAOD,OAAOC,QAAQzB,OAAAA,EAASqG,QAAQ,CAAC,CAACxF,KAAKU,KAAAA,MAAM;AAClD,QAAIA,UAAUS,QAAW;AACvB,aAAO,CAAA;IACT;AACA,QAAIZ,MAAMC,QAAQE,KAAAA,GAAQ;AACxB,aAAOA,MAAMP,IAAI,CAACC,MAAM;QAACJ;QAAKI;OAAE;IAClC;AACA,WAAO;MAAC;QAACJ;QAAKU,MAAMR,SAAQ;;;EAC9B,CAAA;AACF;AAvBgBoF;AAyBT,SAASG,YAAYxE,MAAWzB,aAA2B;AAChE,MAAI,CAACyB,QAAQ,CAACzB,aAAa;AACzB;EACF;AACA,MAAI;AACF,QAAIA,YAAYI,WAAW,kBAAA,GAAqB;AAC9C,UAAI8F,kBAAkBzE,IAAAA,GAAO;AAC3B,eAAO9E,0BAAOC,KAAK6E,IAAAA;MACrB,OAAO;AACL,eAAO9E,0BAAOC,KAAKsF,KAAKE,UAAUX,IAAAA,CAAAA;MACpC;IACF;AACA,QAAIzB,YAAYI,WAAW,OAAA,KAAY,OAAOqB,SAAS,UAAU;AAC/D,aAAO9E,0BAAOC,KAAK6E,IAAAA;IACrB;EACF,SAASwB,OAAO;AACd;EACF;AACF;AAlBgBgD;AAoBhB,SAASC,kBAAkBzE,MAAS;AAClC,MAAI,OAAOA,SAAS,UAAU;AAC5B,WAAO;EACT;AACA,MAAI;AACFS,SAAKC,MAAMV,IAAAA;AACX,WAAO;EACT,QAAQ;AACN,WAAO;EACT;AACF;AAVSyE;AAYT,SAAS/G,cAAc+B,OAAehC,UAAkB;AACtD,SAAOA,SAASgB,KAAK,CAACiG,YAAAA;AACpB,WAAOA,QAAQnE,KAAKd,KAAAA;EACtB,CAAA;AACF;AAJS/B;AAMT,SAAS4F,gBAA+CjE,MAAO;AAC7D,SAAOK,OAAOiF,YACZjF,OAAOC,QAAQN,IAAAA,EAAMyB,OAAO,CAAC,CAAC8D,GAAGzF,CAAAA,MAAE;AACjC,QAAIA,KAAK,QAAQ0F,OAAOC,MAAM3F,CAAAA,EAAI,QAAO;AACzC,QAAIG,MAAMC,QAAQJ,CAAAA,KAAMjE,0BAAO6J,SAAS5F,CAAAA,KAAM,OAAOA,MAAM,UAAU;AACnE,aAAOA,EAAEkB,SAAS;IACpB;AACA,WAAO;EACT,CAAA,CAAA;AAEJ;AAVSiD;AAYT,SAASP,mBAAmBiC,KAAW;AACrC,MAAIA,IAAI3E,SAASrF,oBAAoB;AACnC,UAAMiK,SAAS;AACf,WAAOD,IAAIE,MAAM,GAAGlK,qBAAqBiK,OAAO5E,MAAM,IAAI4E;EAC5D;AACA,SAAOD;AACT;AANSjC;","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","some","t","startsWith","search","params","URLSearchParams","key","set","toString","map","v","maskBody","data","Array","isArray","result","value","Object","entries","item","applyMasking","maskRequestBodyCallback","request","body","maskedBody","undefined","maskResponseBodyCallback","response","length","bodyData","test","parsedBody","JSON","parse","stringify","lines","split","filter","line","trim","maskedLines","parsed","masked","join","url","URL","logRequest","error","logs","spans","traceId","path","pathname","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,6 +1,10 @@
|
|
|
1
1
|
import { Buffer } from 'node:buffer';
|
|
2
2
|
import { IncomingHttpHeaders, OutgoingHttpHeaders } from 'node:http';
|
|
3
|
+
import { SpanData } from './spanCollector.cjs';
|
|
3
4
|
import TempGzipFile from './tempGzipFile.cjs';
|
|
5
|
+
import '@opentelemetry/sdk-trace-base';
|
|
6
|
+
import './logging.cjs';
|
|
7
|
+
import 'winston';
|
|
4
8
|
|
|
5
9
|
type Request = {
|
|
6
10
|
timestamp: number;
|
|
@@ -34,6 +38,7 @@ type RequestLoggingConfig = {
|
|
|
34
38
|
logResponseBody: boolean;
|
|
35
39
|
logException: boolean;
|
|
36
40
|
captureLogs: boolean;
|
|
41
|
+
captureTraces: boolean;
|
|
37
42
|
maskQueryParams: RegExp[];
|
|
38
43
|
maskHeaders: RegExp[];
|
|
39
44
|
maskBodyFields: RegExp[];
|
|
@@ -65,7 +70,7 @@ declare class RequestLogger {
|
|
|
65
70
|
private maskHeaders;
|
|
66
71
|
private maskBody;
|
|
67
72
|
private applyMasking;
|
|
68
|
-
logRequest(request: Request, response: Response, error?: Error, logs?: LogRecord[]): void;
|
|
73
|
+
logRequest(request: Request, response: Response, error?: Error, logs?: LogRecord[], spans?: SpanData[], traceId?: string): void;
|
|
69
74
|
writeToFile(): Promise<void>;
|
|
70
75
|
getFile(): TempGzipFile | undefined;
|
|
71
76
|
retryFileLater(file: TempGzipFile): void;
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { Buffer } from 'node:buffer';
|
|
2
2
|
import { IncomingHttpHeaders, OutgoingHttpHeaders } from 'node:http';
|
|
3
|
+
import { SpanData } from './spanCollector.js';
|
|
3
4
|
import TempGzipFile from './tempGzipFile.js';
|
|
5
|
+
import '@opentelemetry/sdk-trace-base';
|
|
6
|
+
import './logging.js';
|
|
7
|
+
import 'winston';
|
|
4
8
|
|
|
5
9
|
type Request = {
|
|
6
10
|
timestamp: number;
|
|
@@ -34,6 +38,7 @@ type RequestLoggingConfig = {
|
|
|
34
38
|
logResponseBody: boolean;
|
|
35
39
|
logException: boolean;
|
|
36
40
|
captureLogs: boolean;
|
|
41
|
+
captureTraces: boolean;
|
|
37
42
|
maskQueryParams: RegExp[];
|
|
38
43
|
maskHeaders: RegExp[];
|
|
39
44
|
maskBodyFields: RegExp[];
|
|
@@ -65,7 +70,7 @@ declare class RequestLogger {
|
|
|
65
70
|
private maskHeaders;
|
|
66
71
|
private maskBody;
|
|
67
72
|
private applyMasking;
|
|
68
|
-
logRequest(request: Request, response: Response, error?: Error, logs?: LogRecord[]): void;
|
|
73
|
+
logRequest(request: Request, response: Response, error?: Error, logs?: LogRecord[], spans?: SpanData[], traceId?: string): void;
|
|
69
74
|
writeToFile(): Promise<void>;
|
|
70
75
|
getFile(): TempGzipFile | undefined;
|
|
71
76
|
retryFileLater(file: TempGzipFile): void;
|
|
@@ -80,6 +80,7 @@ const DEFAULT_CONFIG = {
|
|
|
80
80
|
logResponseBody: false,
|
|
81
81
|
logException: true,
|
|
82
82
|
captureLogs: false,
|
|
83
|
+
captureTraces: false,
|
|
83
84
|
maskQueryParams: [],
|
|
84
85
|
maskHeaders: [],
|
|
85
86
|
maskBodyFields: [],
|
|
@@ -243,7 +244,7 @@ const _RequestLogger = class _RequestLogger {
|
|
|
243
244
|
item.request.url = url.toString();
|
|
244
245
|
return item;
|
|
245
246
|
}
|
|
246
|
-
logRequest(request, response, error, logs) {
|
|
247
|
+
logRequest(request, response, error, logs, spans, traceId) {
|
|
247
248
|
var _a, _b, _c;
|
|
248
249
|
if (!this.enabled || this.suspendUntil !== null) return;
|
|
249
250
|
const url = new URL(request.url);
|
|
@@ -283,6 +284,12 @@ const _RequestLogger = class _RequestLogger {
|
|
|
283
284
|
message: truncateLogMessage(log.message)
|
|
284
285
|
}));
|
|
285
286
|
}
|
|
287
|
+
if (spans && spans.length > 0) {
|
|
288
|
+
item.spans = spans;
|
|
289
|
+
}
|
|
290
|
+
if (traceId) {
|
|
291
|
+
item.traceId = traceId;
|
|
292
|
+
}
|
|
286
293
|
this.pendingWrites.push(item);
|
|
287
294
|
if (this.pendingWrites.length > MAX_PENDING_WRITES) {
|
|
288
295
|
this.pendingWrites.shift();
|
|
@@ -305,7 +312,9 @@ const _RequestLogger = class _RequestLogger {
|
|
|
305
312
|
request: skipEmptyValues(item.request),
|
|
306
313
|
response: skipEmptyValues(item.response),
|
|
307
314
|
exception: item.exception,
|
|
308
|
-
logs: item.logs
|
|
315
|
+
logs: item.logs,
|
|
316
|
+
spans: item.spans,
|
|
317
|
+
traceId: item.traceId
|
|
309
318
|
};
|
|
310
319
|
[
|
|
311
320
|
finalItem.request.body,
|
|
@@ -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 { IncomingHttpHeaders, OutgoingHttpHeaders } from \"node:http\";\n\nimport { getSentryEventId } from \"./sentry.js\";\nimport {\n truncateExceptionMessage,\n truncateExceptionStackTrace,\n} from \"./serverErrorCounter.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 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 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};\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 | null) {\n return (\n typeof contentType === \"string\" &&\n ALLOWED_CONTENT_TYPES.some((t) => contentType.startsWith(t))\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 ) {\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 (\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 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 };\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":["AsyncLock","Buffer","randomUUID","getSentryEventId","truncateExceptionMessage","truncateExceptionStackTrace","TempGzipFile","checkWritableFs","MAX_BODY_SIZE","MAX_FILE_SIZE","MAX_FILES","MAX_PENDING_WRITES","MAX_LOG_MSG_LENGTH","BODY_TOO_LARGE","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","maskQueryParams","maskHeaders","maskBodyFields","excludePaths","RequestLogger","config","suspendUntil","pendingWrites","currentFile","files","maintainIntervalId","lock","setInterval","maintain","maxBodySize","shouldExcludePath","urlPath","patterns","matchPatterns","shouldExcludeUserAgent","userAgent","shouldMaskQueryParam","name","shouldMaskHeader","shouldMaskBodyField","getContentType","headers","find","k","toLowerCase","hasSupportedContentType","contentType","isSupportedContentType","some","t","startsWith","search","params","URLSearchParams","key","set","toString","map","v","maskBody","data","Array","isArray","result","value","Object","entries","item","applyMasking","maskRequestBodyCallback","request","body","maskedBody","undefined","maskResponseBodyCallback","response","length","bodyData","test","parsedBody","JSON","parse","stringify","lines","split","filter","line","trim","maskedLines","parsed","masked","join","url","URL","logRequest","error","logs","path","pathname","excludeCallback","size","uuid","exception","type","message","stacktrace","stack","sentryEventId","log","timestamp","logger","level","truncateLogMessage","push","shift","writeToFile","acquire","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 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 | null) {\n return (\n typeof contentType === \"string\" &&\n ALLOWED_CONTENT_TYPES.some((t) => contentType.startsWith(t))\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 (\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":["AsyncLock","Buffer","randomUUID","getSentryEventId","truncateExceptionMessage","truncateExceptionStackTrace","TempGzipFile","checkWritableFs","MAX_BODY_SIZE","MAX_FILE_SIZE","MAX_FILES","MAX_PENDING_WRITES","MAX_LOG_MSG_LENGTH","BODY_TOO_LARGE","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","setInterval","maintain","maxBodySize","shouldExcludePath","urlPath","patterns","matchPatterns","shouldExcludeUserAgent","userAgent","shouldMaskQueryParam","name","shouldMaskHeader","shouldMaskBodyField","getContentType","headers","find","k","toLowerCase","hasSupportedContentType","contentType","isSupportedContentType","some","t","startsWith","search","params","URLSearchParams","key","set","toString","map","v","maskBody","data","Array","isArray","result","value","Object","entries","item","applyMasking","maskRequestBodyCallback","request","body","maskedBody","undefined","maskResponseBodyCallback","response","length","bodyData","test","parsedBody","JSON","parse","stringify","lines","split","filter","line","trim","maskedLines","parsed","masked","join","url","URL","logRequest","error","logs","spans","traceId","path","pathname","excludeCallback","size","uuid","exception","type","message","stacktrace","stack","sentryEventId","log","timestamp","logger","level","truncateLogMessage","push","shift","writeToFile","acquire","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"]}
|