apitally 0.25.7 → 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 +4 -2
- package/dist/common/requestLogger.cjs.map +1 -1
- package/dist/common/requestLogger.d.cts +2 -2
- package/dist/common/requestLogger.d.ts +2 -2
- package/dist/common/requestLogger.js +2 -1
- 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 +2 -0
- package/dist/common/tempGzipFile.cjs.map +1 -1
- package/dist/common/tempGzipFile.d.cts +1 -1
- package/dist/common/tempGzipFile.d.ts +1 -1
- package/dist/common/tempGzipFile.js +1 -0
- 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 +3 -3
|
@@ -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 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"],"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,qBAAqB;EACrBC,OAAO,IAAIC,kBAAAA,QAAAA;EAEnB,YAAYR,QAAwC;AAClD,SAAKA,SAAS;MAAE,GAAGf;MAAgB,GAAGe;IAAO;AAC7C,SAAKd,UAAU,KAAKc,OAAOd,eAAWuB,qCAAAA;AAEtC,QAAI,KAAKvB,SAAS;AAChB,WAAKmB,qBAAqBK,YAAY,MAAA;AACpC,aAAKC,SAAQ,EAAGC,MAAM,MAAA;QAAO,CAAA;MAC/B,GAAG,GAAA;IACL;EACF;EAEA,IAAIC,cAAc;AAChB,WAAO5C;EACT;EAEQ6C,kBAAkBC,SAAiB;AACzC,UAAMC,WAAW;SAAI,KAAKhB,OAAOF;SAAiBlB;;AAClD,WAAOqC,cAAcF,SAASC,QAAAA;EAChC;EAEQE,uBAAuBC,WAAoB;AACjD,WAAOA,YACHF,cAAcE,WAAWtC,2BAAAA,IACzB;EACN;EAEQuC,qBAAqBC,MAAc;AACzC,UAAML,WAAW;SACZ,KAAKhB,OAAOL;SACZb;;AAEL,WAAOmC,cAAcI,MAAML,QAAAA;EAC7B;EAEQM,iBAAiBD,MAAc;AACrC,UAAML,WAAW;SAAI,KAAKhB,OAAOJ;SAAgBb;;AACjD,WAAOkC,cAAcI,MAAML,QAAAA;EAC7B;EAEQO,oBAAoBF,MAAc;AACxC,UAAML,WAAW;SACZ,KAAKhB,OAAOH;SACZb;;AAEL,WAAOiC,cAAcI,MAAML,QAAAA;EAC7B;EAEQQ,eAAeC,SAA6B;AAxNtD;AAyNI,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,aAAwC;AACpE,QAAIE,MAAMC,QAAQH,WAAAA,KAAgBA,YAAYI,SAAS,GAAG;AACxDJ,oBAAcA,YAAY,CAAA;IAC5B;AACA,WACE,OAAOA,gBAAgB,YACvBnD,sBAAsBwD,KAAK,CAACC,MAAMN,YAAYO,WAAWD,CAAAA,CAAAA;EAE7D;EAEA,OAAeE,QAAQb,SAAsC;AAC3D,WAAOA,QAAQU,KAAK,CAAC,CAACR,GAAGY,CAAAA,MAAE;AACzB,cAAQZ,EAAEC,YAAW,GAAA;QACnB,KAAK;QACL,KAAK;QACL,KAAK;QACL,KAAK;QACL,KAAK;AACH,iBAAOW,EAAEC,MAAM,GAAA,EAAK,CAAA,EAAGC,KAAI,EAAGb,YAAW,MAAO;QAClD,KAAK;QACL,KAAK;AACH,iBAAOW,EAAEX,YAAW,MAAO;QAC7B,KAAK;AACH,iBAAOW,EAAEC,MAAM,GAAA,EAAK,CAAA,EAAGC,KAAI,EAAGb,YAAW,EAAGc,SAAS,aAAA;QACvD;AACE,iBAAO;MACX;IACF,CAAA;EACF;EAEQ/C,gBAAgBgD,QAAgB;AACtC,UAAMC,SAAS,IAAIC,gBAAgBF,MAAAA;AACnC,eAAW,CAACG,GAAAA,KAAQF,QAAQ;AAC1B,UAAI,KAAKxB,qBAAqB0B,GAAAA,GAAM;AAClCF,eAAOG,IAAID,KAAKpE,MAAAA;MAClB;IACF;AACA,WAAOkE,OAAOI,SAAQ;EACxB;EAEQpD,YAAY6B,SAAiD;AACnE,WAAOA,QAAQwB,IAAI,CAAC,CAACtB,GAAGY,CAAAA,MAAO;MAACZ;MAAG,KAAKL,iBAAiBK,CAAAA,IAAKjD,SAAS6D;KAAE;EAC3E;EAEQW,SAASC,MAAgB;AAC/B,QAAI,OAAOA,SAAS,YAAYA,SAAS,QAAQ,CAACnB,MAAMC,QAAQkB,IAAAA,GAAO;AACrE,YAAMC,SAAc,CAAC;AACrB,iBAAW,CAACN,KAAKO,KAAAA,KAAUC,OAAOC,QAAQJ,IAAAA,GAAO;AAC/C,YAAI,OAAOE,UAAU,YAAY,KAAK9B,oBAAoBuB,GAAAA,GAAM;AAC9DM,iBAAON,GAAAA,IAAOpE;QAChB,OAAO;AACL0E,iBAAON,GAAAA,IAAO,KAAKI,SAASG,KAAAA;QAC9B;MACF;AACA,aAAOD;IACT;AACA,QAAIpB,MAAMC,QAAQkB,IAAAA,GAAO;AACvB,aAAOA,KAAKF,IAAI,CAACO,SAAS,KAAKN,SAASM,IAAAA,CAAAA;IAC1C;AACA,WAAOL;EACT;EAEQM,aAAaD,MAAsB;AAEzC,QACE,KAAKxD,OAAO0D,2BACZF,KAAKG,QAAQC,QACbJ,KAAKG,QAAQC,SAAStF,gBACtB;AACA,UAAI;AACF,cAAMuF,aAAa,KAAK7D,OAAO0D,wBAAwBF,KAAKG,OAAO;AACnEH,aAAKG,QAAQC,OAAOC,cAAcpF;MACpC,QAAQ;AACN+E,aAAKG,QAAQC,OAAOE;MACtB;IACF;AAGA,QACE,KAAK9D,OAAO+D,4BACZP,KAAKQ,SAASJ,QACdJ,KAAKQ,SAASJ,SAAStF,gBACvB;AACA,UAAI;AACF,cAAMuF,aAAa,KAAK7D,OAAO+D,yBAC7BP,KAAKG,SACLH,KAAKQ,QAAQ;AAEfR,aAAKQ,SAASJ,OAAOC,cAAcpF;MACrC,QAAQ;AACN+E,aAAKQ,SAASJ,OAAOE;MACvB;IACF;AAGA,QAAIN,KAAKG,QAAQC,QAAQJ,KAAKG,QAAQC,KAAK1B,SAASjE,eAAe;AACjEuF,WAAKG,QAAQC,OAAOtF;IACtB;AACA,QAAIkF,KAAKQ,SAASJ,QAAQJ,KAAKQ,SAASJ,KAAK1B,SAASjE,eAAe;AACnEuF,WAAKQ,SAASJ,OAAOtF;IACvB;AAGA,eAAWwE,OAAO;MAAC;MAAW;OAAsB;AAClD,YAAMmB,WAAWT,KAAKV,GAAAA,EAAKc;AAC3B,UACE,CAACK,YACDA,aAAa3F,kBACb2F,aAAaxF,aACb;AACA;MACF;AAEA,UAAI;AACF,cAAMqD,cAAc,KAAKN,eAAegC,KAAKV,GAAAA,EAAKrB,OAAO;AACzD,YAAI,CAACK,eAAe,YAAYoC,KAAKpC,WAAAA,GAAc;AACjD,gBAAMqC,aAAaC,KAAKC,MAAMJ,SAASjB,SAAQ,CAAA;AAC/C,gBAAMa,aAAa,KAAKX,SAASiB,UAAAA;AACjCX,eAAKV,GAAAA,EAAKc,OAAOrF,0BAAOC,KAAK4F,KAAKE,UAAUT,UAAAA,CAAAA;QAC9C,WAAW,cAAcK,KAAKpC,WAAAA,GAAc;AAC1C,gBAAMyC,QAAQN,SACXjB,SAAQ,EACRR,MAAM,IAAA,EACNgC,OAAO,CAACC,SAASA,KAAKhC,KAAI,CAAA;AAC7B,gBAAMiC,cAAcH,MAAMtB,IAAI,CAACwB,SAAAA;AAC7B,gBAAI;AACF,oBAAME,SAASP,KAAKC,MAAMI,IAAAA;AAC1B,oBAAMG,SAAS,KAAK1B,SAASyB,MAAAA;AAC7B,qBAAOP,KAAKE,UAAUM,MAAAA;YACxB,QAAQ;AACN,qBAAOH;YACT;UACF,CAAA;AACAjB,eAAKV,GAAAA,EAAKc,OAAOrF,0BAAOC,KAAKkG,YAAYG,KAAK,IAAA,CAAA;QAChD;MACF,QAAQ;MAER;IACF;AAGArB,SAAKG,QAAQlC,UAAU,KAAKzB,OAAOZ,oBAC/B,KAAKQ,YAAY4D,KAAKG,QAAQlC,OAAO,IACrC,CAAA;AACJ+B,SAAKQ,SAASvC,UAAU,KAAKzB,OAAOV,qBAChC,KAAKM,YAAY4D,KAAKQ,SAASvC,OAAO,IACtC,CAAA;AAGJ,UAAMqD,MAAM,IAAIC,IAAIvB,KAAKG,QAAQmB,GAAG;AACpCA,QAAInC,SAAS,KAAK3C,OAAOb,iBACrB,KAAKQ,gBAAgBmF,IAAInC,MAAM,IAC/B;AACJa,SAAKG,QAAQmB,MAAMA,IAAI9B,SAAQ;AAE/B,WAAOQ;EACT;EAEAwB,WACErB,SACAK,UACAiB,OACAC,MACAC,OACAC,SACA;AAtYJ;AAuYI,QAAI,CAAC,KAAKlG,WAAW,KAAKe,iBAAiB,KAAM;AAEjD,UAAM6E,MAAM,IAAIC,IAAIpB,QAAQmB,GAAG;AAC/B,UAAMO,OAAO1B,QAAQ0B,QAAQP,IAAIQ;AACjC,UAAMnE,aAAYwC,aAAQlC,QAAQC,KAChC,CAAC,CAACC,CAAAA,MAAOA,EAAEC,YAAW,MAAO,YAAA,MADb+B,mBAEd;AAEJ,QAAImB,IAAIS,aAAa,WAAWxF,eAAcuC,QAAQqB,QAAQlC,OAAO,GAAG;AACtEqD,UAAIS,WAAW;AACf5B,cAAQmB,MAAMA,IAAI9B,SAAQ;IAC5B;AAEA,QACE,KAAKlC,kBAAkBuE,IAAAA,KACvB,KAAKnE,uBAAuBC,SAAAA,QAC3B,gBAAKnB,QAAOwF,oBAAZ,4BAA8B7B,SAASK,cAAa,QACrD;AACA;IACF;AAEA,QACE,CAAC,KAAKhE,OAAOX,kBACb,CAAC,KAAKwC,wBAAwB8B,QAAQlC,OAAO,GAC7C;AACAkC,cAAQC,OAAOE;IACjB;AACA,QACE,CAAC,KAAK9D,OAAOT,mBACb,CAAC,KAAKsC,wBAAwBmC,SAASvC,OAAO,GAC9C;AACAuC,eAASJ,OAAOE;IAClB;AAEA,QAAIH,QAAQ8B,SAAS3B,UAAaH,QAAQ8B,OAAO,GAAG;AAClD9B,cAAQ8B,OAAO3B;IACjB;AACA,QAAIE,SAASyB,SAAS3B,UAAaE,SAASyB,OAAO,GAAG;AACpDzB,eAASyB,OAAO3B;IAClB;AAEA,UAAMN,OAAuB;MAC3BkC,UAAMC,+BAAAA;MACNhC;MACAK;MACA4B,WACEX,SAAS,KAAKjF,OAAOR,eACjB;QACEqG,MAAMZ,MAAM5D;QACZyE,aAASC,oDAAyBd,MAAMa,OAAO;QAC/CE,gBAAYC,uDAA4BhB,MAAMiB,SAAS,EAAA;QACvDC,mBAAeC,gCAAAA;MACjB,IACAtC;IACR;AAEA,QAAIoB,QAAQA,KAAKhD,SAAS,GAAG;AAC3BsB,WAAK0B,OAAOA,KAAKjC,IAAI,CAACoD,SAAS;QAC7BC,WAAWD,IAAIC;QACfC,QAAQF,IAAIE;QACZC,OAAOH,IAAIG;QACXV,SAASW,mBAAmBJ,IAAIP,OAAO;MACzC,EAAA;IACF;AACA,QAAIX,SAASA,MAAMjD,SAAS,GAAG;AAC7BsB,WAAK2B,QAAQA;IACf;AACA,QAAIC,SAAS;AACX5B,WAAK4B,UAAUA;IACjB;AACA,SAAKlF,cAAcwG,KAAKlD,IAAAA;AAExB,QAAI,KAAKtD,cAAcgC,SAAS9D,oBAAoB;AAClD,WAAK8B,cAAcyG,MAAK;IAC1B;EACF;EAEA,MAAMC,cAAc;AAClB,QAAI,CAAC,KAAK1H,WAAW,KAAKgB,cAAcgC,WAAW,GAAG;AACpD;IACF;AACA,WAAO,KAAK3B,KAAKsG,QAAQ,QAAQ,YAAA;AAM/B,YAAMC,QAAQ,KAAK5G;AACnB,WAAKA,gBAAgB,CAAA;AACrB,UAAI4G,MAAM5E,WAAW,GAAG;AACtB;MACF;AACA,UAAI,CAAC,KAAK/B,aAAa;AACrB,aAAKA,cAAc,IAAI4G,oBAAAA,QAAa,cAAA;MACtC;AAEA,YAAMxC,QAAkB,CAAA;AACxB,eAASf,QAAQsD,OAAO;AACtBtD,eAAO,KAAKC,aAAaD,IAAAA;AAEzB,cAAMwD,YAAY;UAChBtB,MAAMlC,KAAKkC;UACX/B,SAASsD,gBAAgBzD,KAAKG,OAAO;UACrCK,UAAUiD,gBAAgBzD,KAAKQ,QAAQ;UACvC4B,WAAWpC,KAAKoC;UAChBV,MAAM1B,KAAK0B;UACXC,OAAO3B,KAAK2B;UACZC,SAAS5B,KAAK4B;QAChB;AAGA;UAAC4B,UAAUrD,QAAQC;UAAMoD,UAAUhD,SAASJ;UAAMsD,QAAQ,CAACtD,SAAAA;AACzD,cAAIA,MAAM;AAERA,iBAAKuD,SAAS,WAAA;AACZ,qBAAO,KAAKnE,SAAS,QAAA;YACvB;UACF;QACF,CAAA;AAEAuB,cAAMmC,KAAKnI,0BAAOC,KAAK4F,KAAKE,UAAU0C,SAAAA,CAAAA,CAAAA;MACxC;AAEA,YAAM,KAAK7G,YAAYiH,WAAW7C,KAAAA;IACpC,CAAA;EACF;EAEA8C,UAAU;AACR,WAAO,KAAKjH,MAAMuG,MAAK;EACzB;EAEAW,eAAeC,MAAoB;AACjC,SAAKnH,MAAMoH,QAAQD,IAAAA;EACrB;EAEA,MAAME,aAAa;AACjB,WAAO,KAAKlH,KAAKsG,QAAQ,QAAQ,YAAA;AAC/B,UAAI,KAAK1G,aAAa;AACpB,cAAM,KAAKA,YAAYuH,MAAK;AAC5B,aAAKtH,MAAMsG,KAAK,KAAKvG,WAAW;AAChC,aAAKA,cAAc;MACrB;IACF,CAAA;EACF;EAEA,MAAMQ,WAAW;AAKf,QAAI,KAAKL,oBAAoB;AAC3B;IACF;AACA,SAAKA,qBAAqB;AAC1B,QAAI;AACF,YAAM,KAAKsG,YAAW;AACtB,UAAI,KAAKzG,eAAe,KAAKA,YAAYsF,OAAOvH,eAAe;AAC7D,cAAM,KAAKuJ,WAAU;MACvB;AACA,aAAO,KAAKrH,MAAM8B,SAAS/D,WAAW;AACpC,cAAMoJ,OAAO,KAAKnH,MAAMuG,MAAK;AAC7BY,qCAAMI;MACR;AACA,UAAI,KAAK1H,iBAAiB,QAAQ,KAAKA,eAAe2H,KAAKC,IAAG,GAAI;AAChE,aAAK5H,eAAe;MACtB;IACF,UAAA;AACE,WAAKK,qBAAqB;IAC5B;EACF;EAEA,MAAMwH,QAAQ;AACZ,SAAK5H,gBAAgB,CAAA;AACrB,UAAM,KAAKuH,WAAU;AACrB,SAAKrH,MAAM8G,QAAQ,CAACK,SAAAA;AAClBA,WAAKI,OAAM;IACb,CAAA;AACA,SAAKvH,QAAQ,CAAA;EACf;EAEA,MAAMsH,QAAQ;AACZ,SAAKxI,UAAU;AACf,UAAM,KAAK4I,MAAK;AAChB,QAAI,KAAKzH,oBAAoB;AAC3B0H,oBAAc,KAAK1H,kBAAkB;IACvC;EACF;AACF;AApaqBN;AAArB,IAAqBA,gBAArB;AAsaO,SAASiI,eACdvG,SAKa;AAEb,MAAI,CAACA,SAAS;AACZ,WAAO,CAAA;EACT;AACA,MAAIA,mBAAmBwG,SAAS;AAC9B,WAAOjG,MAAMxD,KAAKiD,QAAQ8B,QAAO,CAAA;EACnC;AACA,SAAOD,OAAOC,QAAQ9B,OAAAA,EAASyG,QAAQ,CAAC,CAACpF,KAAKO,KAAAA,MAAM;AAClD,QAAIA,UAAUS,QAAW;AACvB,aAAO,CAAA;IACT;AACA,QAAI9B,MAAMC,QAAQoB,KAAAA,GAAQ;AACxB,aAAOA,MAAMJ,IAAI,CAACV,MAAM;QAACO;QAAKP;OAAE;IAClC;AACA,WAAO;MAAC;QAACO;QAAKO,MAAML,SAAQ;;;EAC9B,CAAA;AACF;AAvBgBgF;AAyBT,SAASG,YAAYvE,MAAW9B,aAA2B;AAChE,MAAI,CAAC8B,QAAQ,CAAC9B,aAAa;AACzB;EACF;AACA,MAAI;AACF,QAAIA,YAAYO,WAAW,kBAAA,GAAqB;AAC9C,UAAI+F,kBAAkBxE,IAAAA,GAAO;AAC3B,eAAOrF,0BAAOC,KAAKoF,IAAAA;MACrB,OAAO;AACL,eAAOrF,0BAAOC,KAAK4F,KAAKE,UAAUV,IAAAA,CAAAA;MACpC;IACF;AACA,QAAI9B,YAAYO,WAAW,OAAA,KAAY,OAAOuB,SAAS,UAAU;AAC/D,aAAOrF,0BAAOC,KAAKoF,IAAAA;IACrB;EACF,SAASqB,OAAO;AACd;EACF;AACF;AAlBgBkD;AAoBhB,SAASC,kBAAkBxE,MAAS;AAClC,MAAI,OAAOA,SAAS,UAAU;AAC5B,WAAO;EACT;AACA,MAAI;AACFQ,SAAKC,MAAMT,IAAAA;AACX,WAAO;EACT,QAAQ;AACN,WAAO;EACT;AACF;AAVSwE;AAYT,SAASnH,cAAcoC,OAAerC,UAAkB;AACtD,SAAOA,SAASmB,KAAK,CAACkG,YAAAA;AACpB,WAAOA,QAAQnE,KAAKb,KAAAA;EACtB,CAAA;AACF;AAJSpC;AAMT,SAASgG,gBAA+C9D,MAAO;AAC7D,SAAOG,OAAOgF,YACZhF,OAAOC,QAAQJ,IAAAA,EAAMqB,OAAO,CAAC,CAAC+D,GAAGhG,CAAAA,MAAE;AACjC,QAAIA,KAAK,QAAQiG,OAAOC,MAAMlG,CAAAA,EAAI,QAAO;AACzC,QAAIP,MAAMC,QAAQM,CAAAA,KAAMhE,0BAAOmK,SAASnG,CAAAA,KAAM,OAAOA,MAAM,UAAU;AACnE,aAAOA,EAAEL,SAAS;IACpB;AACA,WAAO;EACT,CAAA,CAAA;AAEJ;AAVS+E;AAYT,SAASR,mBAAmBkC,KAAW;AACrC,MAAIA,IAAIzG,SAAS7D,oBAAoB;AACnC,UAAMuK,SAAS;AACf,WAAOD,IAAIE,MAAM,GAAGxK,qBAAqBuK,OAAO1G,MAAM,IAAI0G;EAC5D;AACA,SAAOD;AACT;AANSlC;","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
|
+
{"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 = {
|
|
@@ -82,4 +82,4 @@ declare class RequestLogger {
|
|
|
82
82
|
declare function convertHeaders(headers: Headers | IncomingHttpHeaders | OutgoingHttpHeaders | Record<string, string | string[] | number | undefined> | undefined): [string, string][];
|
|
83
83
|
declare function convertBody(body: any, contentType?: string | null): Buffer<any> | undefined;
|
|
84
84
|
|
|
85
|
-
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 = {
|
|
@@ -82,4 +82,4 @@ declare class RequestLogger {
|
|
|
82
82
|
declare function convertHeaders(headers: Headers | IncomingHttpHeaders | OutgoingHttpHeaders | Record<string, string | string[] | number | undefined> | undefined): [string, string][];
|
|
83
83
|
declare function convertBody(body: any, contentType?: string | null): Buffer<any> | undefined;
|
|
84
84
|
|
|
85
|
-
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;
|
|
@@ -500,6 +500,7 @@ function truncateLogMessage(msg) {
|
|
|
500
500
|
}
|
|
501
501
|
__name(truncateLogMessage, "truncateLogMessage");
|
|
502
502
|
export {
|
|
503
|
+
RequestLogger,
|
|
503
504
|
convertBody,
|
|
504
505
|
convertHeaders,
|
|
505
506
|
RequestLogger as default
|
|
@@ -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 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"],"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","maintainInProgress","lock","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","exception","type","message","stacktrace","stack","sentryEventId","log","timestamp","logger","level","truncateLogMessage","push","shift","writeToFile","acquire","items","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
|
+
{"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":";;AAAA,OAAOA,eAAe;AACtB,SAASC,UAAAA,eAAc;AACvB,SAASC,kBAAkB;AAG3B,SAASC,wBAAwB;AACjC,SACEC,0BACAC,mCACK;AAEP,SAASC,iBAAiBC,oBAAoB;AAE9C,MAAMC,gBAAgB;AACtB,MAAMC,gBAAgB;AACtB,MAAMC,YAAY;AAClB,MAAMC,qBAAqB;AAC3B,MAAMC,qBAAqB;AAC3B,MAAMC,iBAAiBZ,QAAOa,KAAK,kBAAA;AACnC,MAAMC,cAAcd,QAAOa,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,qBAAqB;EACrBC,OAAO,IAAI7C,UAAAA;EAEnB,YAAYsC,QAAwC;AAClD,SAAKA,SAAS;MAAE,GAAGf;MAAgB,GAAGe;IAAO;AAC7C,SAAKd,UAAU,KAAKc,OAAOd,WAAWlB,gBAAAA;AAEtC,QAAI,KAAKkB,SAAS;AAChB,WAAKmB,qBAAqBG,YAAY,MAAA;AACpC,aAAKC,SAAQ,EAAGC,MAAM,MAAA;QAAO,CAAA;MAC/B,GAAG,GAAA;IACL;EACF;EAEA,IAAIC,cAAc;AAChB,WAAOzC;EACT;EAEQ0C,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;AAxNtD;AAyNI,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,aAAwC;AACpE,QAAIE,MAAMC,QAAQH,WAAAA,KAAgBA,YAAYI,SAAS,GAAG;AACxDJ,oBAAcA,YAAY,CAAA;IAC5B;AACA,WACE,OAAOA,gBAAgB,YACvBjD,sBAAsBsD,KAAK,CAACC,MAAMN,YAAYO,WAAWD,CAAAA,CAAAA;EAE7D;EAEA,OAAeE,QAAQb,SAAsC;AAC3D,WAAOA,QAAQU,KAAK,CAAC,CAACR,GAAGY,CAAAA,MAAE;AACzB,cAAQZ,EAAEC,YAAW,GAAA;QACnB,KAAK;QACL,KAAK;QACL,KAAK;QACL,KAAK;QACL,KAAK;AACH,iBAAOW,EAAEC,MAAM,GAAA,EAAK,CAAA,EAAGC,KAAI,EAAGb,YAAW,MAAO;QAClD,KAAK;QACL,KAAK;AACH,iBAAOW,EAAEX,YAAW,MAAO;QAC7B,KAAK;AACH,iBAAOW,EAAEC,MAAM,GAAA,EAAK,CAAA,EAAGC,KAAI,EAAGb,YAAW,EAAGc,SAAS,aAAA;QACvD;AACE,iBAAO;MACX;IACF,CAAA;EACF;EAEQ7C,gBAAgB8C,QAAgB;AACtC,UAAMC,SAAS,IAAIC,gBAAgBF,MAAAA;AACnC,eAAW,CAACG,GAAAA,KAAQF,QAAQ;AAC1B,UAAI,KAAKxB,qBAAqB0B,GAAAA,GAAM;AAClCF,eAAOG,IAAID,KAAKlE,MAAAA;MAClB;IACF;AACA,WAAOgE,OAAOI,SAAQ;EACxB;EAEQlD,YAAY2B,SAAiD;AACnE,WAAOA,QAAQwB,IAAI,CAAC,CAACtB,GAAGY,CAAAA,MAAO;MAACZ;MAAG,KAAKL,iBAAiBK,CAAAA,IAAK/C,SAAS2D;KAAE;EAC3E;EAEQW,SAASC,MAAgB;AAC/B,QAAI,OAAOA,SAAS,YAAYA,SAAS,QAAQ,CAACnB,MAAMC,QAAQkB,IAAAA,GAAO;AACrE,YAAMC,SAAc,CAAC;AACrB,iBAAW,CAACN,KAAKO,KAAAA,KAAUC,OAAOC,QAAQJ,IAAAA,GAAO;AAC/C,YAAI,OAAOE,UAAU,YAAY,KAAK9B,oBAAoBuB,GAAAA,GAAM;AAC9DM,iBAAON,GAAAA,IAAOlE;QAChB,OAAO;AACLwE,iBAAON,GAAAA,IAAO,KAAKI,SAASG,KAAAA;QAC9B;MACF;AACA,aAAOD;IACT;AACA,QAAIpB,MAAMC,QAAQkB,IAAAA,GAAO;AACvB,aAAOA,KAAKF,IAAI,CAACO,SAAS,KAAKN,SAASM,IAAAA,CAAAA;IAC1C;AACA,WAAOL;EACT;EAEQM,aAAaD,MAAsB;AAEzC,QACE,KAAKtD,OAAOwD,2BACZF,KAAKG,QAAQC,QACbJ,KAAKG,QAAQC,SAASnF,gBACtB;AACA,UAAI;AACF,cAAMoF,aAAa,KAAK3D,OAAOwD,wBAAwBF,KAAKG,OAAO;AACnEH,aAAKG,QAAQC,OAAOC,cAAclF;MACpC,QAAQ;AACN6E,aAAKG,QAAQC,OAAOE;MACtB;IACF;AAGA,QACE,KAAK5D,OAAO6D,4BACZP,KAAKQ,SAASJ,QACdJ,KAAKQ,SAASJ,SAASnF,gBACvB;AACA,UAAI;AACF,cAAMoF,aAAa,KAAK3D,OAAO6D,yBAC7BP,KAAKG,SACLH,KAAKQ,QAAQ;AAEfR,aAAKQ,SAASJ,OAAOC,cAAclF;MACrC,QAAQ;AACN6E,aAAKQ,SAASJ,OAAOE;MACvB;IACF;AAGA,QAAIN,KAAKG,QAAQC,QAAQJ,KAAKG,QAAQC,KAAK1B,SAAS9D,eAAe;AACjEoF,WAAKG,QAAQC,OAAOnF;IACtB;AACA,QAAI+E,KAAKQ,SAASJ,QAAQJ,KAAKQ,SAASJ,KAAK1B,SAAS9D,eAAe;AACnEoF,WAAKQ,SAASJ,OAAOnF;IACvB;AAGA,eAAWqE,OAAO;MAAC;MAAW;OAAsB;AAClD,YAAMmB,WAAWT,KAAKV,GAAAA,EAAKc;AAC3B,UACE,CAACK,YACDA,aAAaxF,kBACbwF,aAAatF,aACb;AACA;MACF;AAEA,UAAI;AACF,cAAMmD,cAAc,KAAKN,eAAegC,KAAKV,GAAAA,EAAKrB,OAAO;AACzD,YAAI,CAACK,eAAe,YAAYoC,KAAKpC,WAAAA,GAAc;AACjD,gBAAMqC,aAAaC,KAAKC,MAAMJ,SAASjB,SAAQ,CAAA;AAC/C,gBAAMa,aAAa,KAAKX,SAASiB,UAAAA;AACjCX,eAAKV,GAAAA,EAAKc,OAAO/F,QAAOa,KAAK0F,KAAKE,UAAUT,UAAAA,CAAAA;QAC9C,WAAW,cAAcK,KAAKpC,WAAAA,GAAc;AAC1C,gBAAMyC,QAAQN,SACXjB,SAAQ,EACRR,MAAM,IAAA,EACNgC,OAAO,CAACC,SAASA,KAAKhC,KAAI,CAAA;AAC7B,gBAAMiC,cAAcH,MAAMtB,IAAI,CAACwB,SAAAA;AAC7B,gBAAI;AACF,oBAAME,SAASP,KAAKC,MAAMI,IAAAA;AAC1B,oBAAMG,SAAS,KAAK1B,SAASyB,MAAAA;AAC7B,qBAAOP,KAAKE,UAAUM,MAAAA;YACxB,QAAQ;AACN,qBAAOH;YACT;UACF,CAAA;AACAjB,eAAKV,GAAAA,EAAKc,OAAO/F,QAAOa,KAAKgG,YAAYG,KAAK,IAAA,CAAA;QAChD;MACF,QAAQ;MAER;IACF;AAGArB,SAAKG,QAAQlC,UAAU,KAAKvB,OAAOZ,oBAC/B,KAAKQ,YAAY0D,KAAKG,QAAQlC,OAAO,IACrC,CAAA;AACJ+B,SAAKQ,SAASvC,UAAU,KAAKvB,OAAOV,qBAChC,KAAKM,YAAY0D,KAAKQ,SAASvC,OAAO,IACtC,CAAA;AAGJ,UAAMqD,MAAM,IAAIC,IAAIvB,KAAKG,QAAQmB,GAAG;AACpCA,QAAInC,SAAS,KAAKzC,OAAOb,iBACrB,KAAKQ,gBAAgBiF,IAAInC,MAAM,IAC/B;AACJa,SAAKG,QAAQmB,MAAMA,IAAI9B,SAAQ;AAE/B,WAAOQ;EACT;EAEAwB,WACErB,SACAK,UACAiB,OACAC,MACAC,OACAC,SACA;AAtYJ;AAuYI,QAAI,CAAC,KAAKhG,WAAW,KAAKe,iBAAiB,KAAM;AAEjD,UAAM2E,MAAM,IAAIC,IAAIpB,QAAQmB,GAAG;AAC/B,UAAMO,OAAO1B,QAAQ0B,QAAQP,IAAIQ;AACjC,UAAMnE,aAAYwC,aAAQlC,QAAQC,KAChC,CAAC,CAACC,CAAAA,MAAOA,EAAEC,YAAW,MAAO,YAAA,MADb+B,mBAEd;AAEJ,QAAImB,IAAIS,aAAa,WAAWtF,eAAcqC,QAAQqB,QAAQlC,OAAO,GAAG;AACtEqD,UAAIS,WAAW;AACf5B,cAAQmB,MAAMA,IAAI9B,SAAQ;IAC5B;AAEA,QACE,KAAKlC,kBAAkBuE,IAAAA,KACvB,KAAKnE,uBAAuBC,SAAAA,QAC3B,gBAAKjB,QAAOsF,oBAAZ,4BAA8B7B,SAASK,cAAa,QACrD;AACA;IACF;AAEA,QACE,CAAC,KAAK9D,OAAOX,kBACb,CAAC,KAAKsC,wBAAwB8B,QAAQlC,OAAO,GAC7C;AACAkC,cAAQC,OAAOE;IACjB;AACA,QACE,CAAC,KAAK5D,OAAOT,mBACb,CAAC,KAAKoC,wBAAwBmC,SAASvC,OAAO,GAC9C;AACAuC,eAASJ,OAAOE;IAClB;AAEA,QAAIH,QAAQ8B,SAAS3B,UAAaH,QAAQ8B,OAAO,GAAG;AAClD9B,cAAQ8B,OAAO3B;IACjB;AACA,QAAIE,SAASyB,SAAS3B,UAAaE,SAASyB,OAAO,GAAG;AACpDzB,eAASyB,OAAO3B;IAClB;AAEA,UAAMN,OAAuB;MAC3BkC,MAAM5H,WAAAA;MACN6F;MACAK;MACA2B,WACEV,SAAS,KAAK/E,OAAOR,eACjB;QACEkG,MAAMX,MAAM5D;QACZwE,SAAS7H,yBAAyBiH,MAAMY,OAAO;QAC/CC,YAAY7H,4BAA4BgH,MAAMc,SAAS,EAAA;QACvDC,eAAejI,iBAAAA;MACjB,IACA+F;IACR;AAEA,QAAIoB,QAAQA,KAAKhD,SAAS,GAAG;AAC3BsB,WAAK0B,OAAOA,KAAKjC,IAAI,CAACgD,SAAS;QAC7BC,WAAWD,IAAIC;QACfC,QAAQF,IAAIE;QACZC,OAAOH,IAAIG;QACXP,SAASQ,mBAAmBJ,IAAIJ,OAAO;MACzC,EAAA;IACF;AACA,QAAIV,SAASA,MAAMjD,SAAS,GAAG;AAC7BsB,WAAK2B,QAAQA;IACf;AACA,QAAIC,SAAS;AACX5B,WAAK4B,UAAUA;IACjB;AACA,SAAKhF,cAAckG,KAAK9C,IAAAA;AAExB,QAAI,KAAKpD,cAAc8B,SAAS3D,oBAAoB;AAClD,WAAK6B,cAAcmG,MAAK;IAC1B;EACF;EAEA,MAAMC,cAAc;AAClB,QAAI,CAAC,KAAKpH,WAAW,KAAKgB,cAAc8B,WAAW,GAAG;AACpD;IACF;AACA,WAAO,KAAKzB,KAAKgG,QAAQ,QAAQ,YAAA;AAM/B,YAAMC,QAAQ,KAAKtG;AACnB,WAAKA,gBAAgB,CAAA;AACrB,UAAIsG,MAAMxE,WAAW,GAAG;AACtB;MACF;AACA,UAAI,CAAC,KAAK7B,aAAa;AACrB,aAAKA,cAAc,IAAIlC,aAAa,cAAA;MACtC;AAEA,YAAMoG,QAAkB,CAAA;AACxB,eAASf,QAAQkD,OAAO;AACtBlD,eAAO,KAAKC,aAAaD,IAAAA;AAEzB,cAAMmD,YAAY;UAChBjB,MAAMlC,KAAKkC;UACX/B,SAASiD,gBAAgBpD,KAAKG,OAAO;UACrCK,UAAU4C,gBAAgBpD,KAAKQ,QAAQ;UACvC2B,WAAWnC,KAAKmC;UAChBT,MAAM1B,KAAK0B;UACXC,OAAO3B,KAAK2B;UACZC,SAAS5B,KAAK4B;QAChB;AAGA;UAACuB,UAAUhD,QAAQC;UAAM+C,UAAU3C,SAASJ;UAAMiD,QAAQ,CAACjD,SAAAA;AACzD,cAAIA,MAAM;AAERA,iBAAKkD,SAAS,WAAA;AACZ,qBAAO,KAAK9D,SAAS,QAAA;YACvB;UACF;QACF,CAAA;AAEAuB,cAAM+B,KAAKzI,QAAOa,KAAK0F,KAAKE,UAAUqC,SAAAA,CAAAA,CAAAA;MACxC;AAEA,YAAM,KAAKtG,YAAY0G,WAAWxC,KAAAA;IACpC,CAAA;EACF;EAEAyC,UAAU;AACR,WAAO,KAAK1G,MAAMiG,MAAK;EACzB;EAEAU,eAAeC,MAAoB;AACjC,SAAK5G,MAAM6G,QAAQD,IAAAA;EACrB;EAEA,MAAME,aAAa;AACjB,WAAO,KAAK3G,KAAKgG,QAAQ,QAAQ,YAAA;AAC/B,UAAI,KAAKpG,aAAa;AACpB,cAAM,KAAKA,YAAYgH,MAAK;AAC5B,aAAK/G,MAAMgG,KAAK,KAAKjG,WAAW;AAChC,aAAKA,cAAc;MACrB;IACF,CAAA;EACF;EAEA,MAAMM,WAAW;AAKf,QAAI,KAAKH,oBAAoB;AAC3B;IACF;AACA,SAAKA,qBAAqB;AAC1B,QAAI;AACF,YAAM,KAAKgG,YAAW;AACtB,UAAI,KAAKnG,eAAe,KAAKA,YAAYoF,OAAOpH,eAAe;AAC7D,cAAM,KAAK+I,WAAU;MACvB;AACA,aAAO,KAAK9G,MAAM4B,SAAS5D,WAAW;AACpC,cAAM4I,OAAO,KAAK5G,MAAMiG,MAAK;AAC7BW,qCAAMI;MACR;AACA,UAAI,KAAKnH,iBAAiB,QAAQ,KAAKA,eAAeoH,KAAKC,IAAG,GAAI;AAChE,aAAKrH,eAAe;MACtB;IACF,UAAA;AACE,WAAKK,qBAAqB;IAC5B;EACF;EAEA,MAAMiH,QAAQ;AACZ,SAAKrH,gBAAgB,CAAA;AACrB,UAAM,KAAKgH,WAAU;AACrB,SAAK9G,MAAMuG,QAAQ,CAACK,SAAAA;AAClBA,WAAKI,OAAM;IACb,CAAA;AACA,SAAKhH,QAAQ,CAAA;EACf;EAEA,MAAM+G,QAAQ;AACZ,SAAKjI,UAAU;AACf,UAAM,KAAKqI,MAAK;AAChB,QAAI,KAAKlH,oBAAoB;AAC3BmH,oBAAc,KAAKnH,kBAAkB;IACvC;EACF;AACF;AApaqBN;AAArB,IAAqBA,gBAArB;AAsaO,SAAS0H,eACdlG,SAKa;AAEb,MAAI,CAACA,SAAS;AACZ,WAAO,CAAA;EACT;AACA,MAAIA,mBAAmBmG,SAAS;AAC9B,WAAO5F,MAAMtD,KAAK+C,QAAQ8B,QAAO,CAAA;EACnC;AACA,SAAOD,OAAOC,QAAQ9B,OAAAA,EAASoG,QAAQ,CAAC,CAAC/E,KAAKO,KAAAA,MAAM;AAClD,QAAIA,UAAUS,QAAW;AACvB,aAAO,CAAA;IACT;AACA,QAAI9B,MAAMC,QAAQoB,KAAAA,GAAQ;AACxB,aAAOA,MAAMJ,IAAI,CAACV,MAAM;QAACO;QAAKP;OAAE;IAClC;AACA,WAAO;MAAC;QAACO;QAAKO,MAAML,SAAQ;;;EAC9B,CAAA;AACF;AAvBgB2E;AAyBT,SAASG,YAAYlE,MAAW9B,aAA2B;AAChE,MAAI,CAAC8B,QAAQ,CAAC9B,aAAa;AACzB;EACF;AACA,MAAI;AACF,QAAIA,YAAYO,WAAW,kBAAA,GAAqB;AAC9C,UAAI0F,kBAAkBnE,IAAAA,GAAO;AAC3B,eAAO/F,QAAOa,KAAKkF,IAAAA;MACrB,OAAO;AACL,eAAO/F,QAAOa,KAAK0F,KAAKE,UAAUV,IAAAA,CAAAA;MACpC;IACF;AACA,QAAI9B,YAAYO,WAAW,OAAA,KAAY,OAAOuB,SAAS,UAAU;AAC/D,aAAO/F,QAAOa,KAAKkF,IAAAA;IACrB;EACF,SAASqB,OAAO;AACd;EACF;AACF;AAlBgB6C;AAoBhB,SAASC,kBAAkBnE,MAAS;AAClC,MAAI,OAAOA,SAAS,UAAU;AAC5B,WAAO;EACT;AACA,MAAI;AACFQ,SAAKC,MAAMT,IAAAA;AACX,WAAO;EACT,QAAQ;AACN,WAAO;EACT;AACF;AAVSmE;AAYT,SAAS9G,cAAcoC,OAAerC,UAAkB;AACtD,SAAOA,SAASmB,KAAK,CAAC6F,YAAAA;AACpB,WAAOA,QAAQ9D,KAAKb,KAAAA;EACtB,CAAA;AACF;AAJSpC;AAMT,SAAS2F,gBAA+CzD,MAAO;AAC7D,SAAOG,OAAO2E,YACZ3E,OAAOC,QAAQJ,IAAAA,EAAMqB,OAAO,CAAC,CAAC0D,GAAG3F,CAAAA,MAAE;AACjC,QAAIA,KAAK,QAAQ4F,OAAOC,MAAM7F,CAAAA,EAAI,QAAO;AACzC,QAAIP,MAAMC,QAAQM,CAAAA,KAAM1E,QAAOwK,SAAS9F,CAAAA,KAAM,OAAOA,MAAM,UAAU;AACnE,aAAOA,EAAEL,SAAS;IACpB;AACA,WAAO;EACT,CAAA,CAAA;AAEJ;AAVS0E;AAYT,SAASP,mBAAmBiC,KAAW;AACrC,MAAIA,IAAIpG,SAAS1D,oBAAoB;AACnC,UAAM+J,SAAS;AACf,WAAOD,IAAIE,MAAM,GAAGhK,qBAAqB+J,OAAOrG,MAAM,IAAIqG;EAC5D;AACA,SAAOD;AACT;AANSjC;","names":["AsyncLock","Buffer","randomUUID","getSentryEventId","truncateExceptionMessage","truncateExceptionStackTrace","checkWritableFs","TempGzipFile","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","maintainInProgress","lock","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","exception","type","message","stacktrace","stack","sentryEventId","log","timestamp","logger","level","truncateLogMessage","push","shift","writeToFile","acquire","items","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"]}
|
|
@@ -19,6 +19,7 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
19
19
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
20
20
|
var serverErrorCounter_exports = {};
|
|
21
21
|
__export(serverErrorCounter_exports, {
|
|
22
|
+
ServerErrorCounter: () => ServerErrorCounter,
|
|
22
23
|
default: () => ServerErrorCounter,
|
|
23
24
|
truncateExceptionMessage: () => truncateExceptionMessage,
|
|
24
25
|
truncateExceptionStackTrace: () => truncateExceptionStackTrace
|
|
@@ -115,6 +116,7 @@ function truncateExceptionStackTrace(stack) {
|
|
|
115
116
|
__name(truncateExceptionStackTrace, "truncateExceptionStackTrace");
|
|
116
117
|
// Annotate the CommonJS export names for ESM import in node:
|
|
117
118
|
0 && (module.exports = {
|
|
119
|
+
ServerErrorCounter,
|
|
118
120
|
truncateExceptionMessage,
|
|
119
121
|
truncateExceptionStackTrace
|
|
120
122
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/common/serverErrorCounter.ts"],"sourcesContent":["import { createHash } from \"node:crypto\";\n\nimport { getSentryEventId } from \"./sentry.js\";\nimport { ConsumerMethodPath, ServerError, ServerErrorsItem } from \"./types.js\";\n\nconst MAX_MSG_LENGTH = 2048;\nconst MAX_STACKTRACE_LENGTH = 65536;\n\nexport default class ServerErrorCounter {\n private errorCounts: Map<string, number>;\n private errorDetails: Map<string, ConsumerMethodPath & ServerError>;\n private sentryEventIds: Map<string, string>;\n\n constructor() {\n this.errorCounts = new Map();\n this.errorDetails = new Map();\n this.sentryEventIds = new Map();\n }\n\n public addServerError(serverError: ConsumerMethodPath & ServerError) {\n const key = this.getKey(serverError);\n if (!this.errorDetails.has(key)) {\n this.errorDetails.set(key, serverError);\n }\n this.errorCounts.set(key, (this.errorCounts.get(key) || 0) + 1);\n\n const sentryEventId = getSentryEventId();\n if (sentryEventId) {\n this.sentryEventIds.set(key, sentryEventId);\n }\n }\n\n public getAndResetServerErrors() {\n const data: Array<ServerErrorsItem> = [];\n this.errorCounts.forEach((count, key) => {\n const serverError = this.errorDetails.get(key);\n if (serverError) {\n data.push({\n consumer: serverError.consumer || null,\n method: serverError.method,\n path: serverError.path,\n type: serverError.type,\n msg: truncateExceptionMessage(serverError.msg),\n traceback: truncateExceptionStackTrace(serverError.traceback),\n sentry_event_id: this.sentryEventIds.get(key) || null,\n error_count: count,\n });\n }\n });\n this.errorCounts.clear();\n this.errorDetails.clear();\n this.sentryEventIds.clear();\n return data;\n }\n\n private getKey(serverError: ConsumerMethodPath & ServerError) {\n const hashInput = [\n serverError.consumer || \"\",\n serverError.method.toUpperCase(),\n serverError.path,\n serverError.type,\n serverError.msg.trim(),\n serverError.traceback.trim(),\n ].join(\"|\");\n return createHash(\"md5\").update(hashInput).digest(\"hex\");\n }\n}\n\nexport function truncateExceptionMessage(msg: string) {\n if (msg.length <= MAX_MSG_LENGTH) {\n return msg;\n }\n const suffix = \"... (truncated)\";\n const cutoff = MAX_MSG_LENGTH - suffix.length;\n return msg.substring(0, cutoff) + suffix;\n}\n\nexport function truncateExceptionStackTrace(stack: string) {\n if (stack.length <= MAX_STACKTRACE_LENGTH) {\n return stack;\n }\n const suffix = \"... (truncated) ...\";\n const cutoff = MAX_STACKTRACE_LENGTH - suffix.length;\n const lines = stack.trim().split(\"\\n\");\n const truncatedLines: string[] = [];\n let length = 0;\n for (const line of lines) {\n if (length + line.length + 1 > cutoff) {\n truncatedLines.push(suffix);\n break;\n }\n truncatedLines.push(line);\n length += line.length + 1;\n }\n return truncatedLines.join(\"\\n\");\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAAA
|
|
1
|
+
{"version":3,"sources":["../../src/common/serverErrorCounter.ts"],"sourcesContent":["import { createHash } from \"node:crypto\";\n\nimport { getSentryEventId } from \"./sentry.js\";\nimport { ConsumerMethodPath, ServerError, ServerErrorsItem } from \"./types.js\";\n\nconst MAX_MSG_LENGTH = 2048;\nconst MAX_STACKTRACE_LENGTH = 65536;\n\nexport default class ServerErrorCounter {\n private errorCounts: Map<string, number>;\n private errorDetails: Map<string, ConsumerMethodPath & ServerError>;\n private sentryEventIds: Map<string, string>;\n\n constructor() {\n this.errorCounts = new Map();\n this.errorDetails = new Map();\n this.sentryEventIds = new Map();\n }\n\n public addServerError(serverError: ConsumerMethodPath & ServerError) {\n const key = this.getKey(serverError);\n if (!this.errorDetails.has(key)) {\n this.errorDetails.set(key, serverError);\n }\n this.errorCounts.set(key, (this.errorCounts.get(key) || 0) + 1);\n\n const sentryEventId = getSentryEventId();\n if (sentryEventId) {\n this.sentryEventIds.set(key, sentryEventId);\n }\n }\n\n public getAndResetServerErrors() {\n const data: Array<ServerErrorsItem> = [];\n this.errorCounts.forEach((count, key) => {\n const serverError = this.errorDetails.get(key);\n if (serverError) {\n data.push({\n consumer: serverError.consumer || null,\n method: serverError.method,\n path: serverError.path,\n type: serverError.type,\n msg: truncateExceptionMessage(serverError.msg),\n traceback: truncateExceptionStackTrace(serverError.traceback),\n sentry_event_id: this.sentryEventIds.get(key) || null,\n error_count: count,\n });\n }\n });\n this.errorCounts.clear();\n this.errorDetails.clear();\n this.sentryEventIds.clear();\n return data;\n }\n\n private getKey(serverError: ConsumerMethodPath & ServerError) {\n const hashInput = [\n serverError.consumer || \"\",\n serverError.method.toUpperCase(),\n serverError.path,\n serverError.type,\n serverError.msg.trim(),\n serverError.traceback.trim(),\n ].join(\"|\");\n return createHash(\"md5\").update(hashInput).digest(\"hex\");\n }\n}\n\nexport function truncateExceptionMessage(msg: string) {\n if (msg.length <= MAX_MSG_LENGTH) {\n return msg;\n }\n const suffix = \"... (truncated)\";\n const cutoff = MAX_MSG_LENGTH - suffix.length;\n return msg.substring(0, cutoff) + suffix;\n}\n\nexport function truncateExceptionStackTrace(stack: string) {\n if (stack.length <= MAX_STACKTRACE_LENGTH) {\n return stack;\n }\n const suffix = \"... (truncated) ...\";\n const cutoff = MAX_STACKTRACE_LENGTH - suffix.length;\n const lines = stack.trim().split(\"\\n\");\n const truncatedLines: string[] = [];\n let length = 0;\n for (const line of lines) {\n if (length + line.length + 1 > cutoff) {\n truncatedLines.push(suffix);\n break;\n }\n truncatedLines.push(line);\n length += line.length + 1;\n }\n return truncatedLines.join(\"\\n\");\n}\n\nexport { ServerErrorCounter };\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAAA;;;;;;;;AAAA,yBAA2B;AAE3B,oBAAiC;AAGjC,MAAMA,iBAAiB;AACvB,MAAMC,wBAAwB;AAE9B,MAAqBC,sBAArB,MAAqBA,oBAAAA;EACXC;EACAC;EACAC;EAER,cAAc;AACZ,SAAKF,cAAc,oBAAIG,IAAAA;AACvB,SAAKF,eAAe,oBAAIE,IAAAA;AACxB,SAAKD,iBAAiB,oBAAIC,IAAAA;EAC5B;EAEOC,eAAeC,aAA+C;AACnE,UAAMC,MAAM,KAAKC,OAAOF,WAAAA;AACxB,QAAI,CAAC,KAAKJ,aAAaO,IAAIF,GAAAA,GAAM;AAC/B,WAAKL,aAAaQ,IAAIH,KAAKD,WAAAA;IAC7B;AACA,SAAKL,YAAYS,IAAIH,MAAM,KAAKN,YAAYU,IAAIJ,GAAAA,KAAQ,KAAK,CAAA;AAE7D,UAAMK,oBAAgBC,gCAAAA;AACtB,QAAID,eAAe;AACjB,WAAKT,eAAeO,IAAIH,KAAKK,aAAAA;IAC/B;EACF;EAEOE,0BAA0B;AAC/B,UAAMC,OAAgC,CAAA;AACtC,SAAKd,YAAYe,QAAQ,CAACC,OAAOV,QAAAA;AAC/B,YAAMD,cAAc,KAAKJ,aAAaS,IAAIJ,GAAAA;AAC1C,UAAID,aAAa;AACfS,aAAKG,KAAK;UACRC,UAAUb,YAAYa,YAAY;UAClCC,QAAQd,YAAYc;UACpBC,MAAMf,YAAYe;UAClBC,MAAMhB,YAAYgB;UAClBC,KAAKC,yBAAyBlB,YAAYiB,GAAG;UAC7CE,WAAWC,4BAA4BpB,YAAYmB,SAAS;UAC5DE,iBAAiB,KAAKxB,eAAeQ,IAAIJ,GAAAA,KAAQ;UACjDqB,aAAaX;QACf,CAAA;MACF;IACF,CAAA;AACA,SAAKhB,YAAY4B,MAAK;AACtB,SAAK3B,aAAa2B,MAAK;AACvB,SAAK1B,eAAe0B,MAAK;AACzB,WAAOd;EACT;EAEQP,OAAOF,aAA+C;AAC5D,UAAMwB,YAAY;MAChBxB,YAAYa,YAAY;MACxBb,YAAYc,OAAOW,YAAW;MAC9BzB,YAAYe;MACZf,YAAYgB;MACZhB,YAAYiB,IAAIS,KAAI;MACpB1B,YAAYmB,UAAUO,KAAI;MAC1BC,KAAK,GAAA;AACP,eAAOC,+BAAW,KAAA,EAAOC,OAAOL,SAAAA,EAAWM,OAAO,KAAA;EACpD;AACF;AA1DqBpC;AAArB,IAAqBA,qBAArB;AA4DO,SAASwB,yBAAyBD,KAAW;AAClD,MAAIA,IAAIc,UAAUvC,gBAAgB;AAChC,WAAOyB;EACT;AACA,QAAMe,SAAS;AACf,QAAMC,SAASzC,iBAAiBwC,OAAOD;AACvC,SAAOd,IAAIiB,UAAU,GAAGD,MAAAA,IAAUD;AACpC;AAPgBd;AAST,SAASE,4BAA4Be,OAAa;AACvD,MAAIA,MAAMJ,UAAUtC,uBAAuB;AACzC,WAAO0C;EACT;AACA,QAAMH,SAAS;AACf,QAAMC,SAASxC,wBAAwBuC,OAAOD;AAC9C,QAAMK,QAAQD,MAAMT,KAAI,EAAGW,MAAM,IAAA;AACjC,QAAMC,iBAA2B,CAAA;AACjC,MAAIP,SAAS;AACb,aAAWQ,QAAQH,OAAO;AACxB,QAAIL,SAASQ,KAAKR,SAAS,IAAIE,QAAQ;AACrCK,qBAAe1B,KAAKoB,MAAAA;AACpB;IACF;AACAM,mBAAe1B,KAAK2B,IAAAA;AACpBR,cAAUQ,KAAKR,SAAS;EAC1B;AACA,SAAOO,eAAeX,KAAK,IAAA;AAC7B;AAlBgBP;","names":["MAX_MSG_LENGTH","MAX_STACKTRACE_LENGTH","ServerErrorCounter","errorCounts","errorDetails","sentryEventIds","Map","addServerError","serverError","key","getKey","has","set","get","sentryEventId","getSentryEventId","getAndResetServerErrors","data","forEach","count","push","consumer","method","path","type","msg","truncateExceptionMessage","traceback","truncateExceptionStackTrace","sentry_event_id","error_count","clear","hashInput","toUpperCase","trim","join","createHash","update","digest","length","suffix","cutoff","substring","stack","lines","split","truncatedLines","line"]}
|
|
@@ -20,4 +20,4 @@ declare class ServerErrorCounter {
|
|
|
20
20
|
declare function truncateExceptionMessage(msg: string): string;
|
|
21
21
|
declare function truncateExceptionStackTrace(stack: string): string;
|
|
22
22
|
|
|
23
|
-
export { ServerErrorCounter as default, truncateExceptionMessage, truncateExceptionStackTrace };
|
|
23
|
+
export { ServerErrorCounter, ServerErrorCounter as default, truncateExceptionMessage, truncateExceptionStackTrace };
|
|
@@ -20,4 +20,4 @@ declare class ServerErrorCounter {
|
|
|
20
20
|
declare function truncateExceptionMessage(msg: string): string;
|
|
21
21
|
declare function truncateExceptionStackTrace(stack: string): string;
|
|
22
22
|
|
|
23
|
-
export { ServerErrorCounter as default, truncateExceptionMessage, truncateExceptionStackTrace };
|
|
23
|
+
export { ServerErrorCounter, ServerErrorCounter as default, truncateExceptionMessage, truncateExceptionStackTrace };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/common/serverErrorCounter.ts"],"sourcesContent":["import { createHash } from \"node:crypto\";\n\nimport { getSentryEventId } from \"./sentry.js\";\nimport { ConsumerMethodPath, ServerError, ServerErrorsItem } from \"./types.js\";\n\nconst MAX_MSG_LENGTH = 2048;\nconst MAX_STACKTRACE_LENGTH = 65536;\n\nexport default class ServerErrorCounter {\n private errorCounts: Map<string, number>;\n private errorDetails: Map<string, ConsumerMethodPath & ServerError>;\n private sentryEventIds: Map<string, string>;\n\n constructor() {\n this.errorCounts = new Map();\n this.errorDetails = new Map();\n this.sentryEventIds = new Map();\n }\n\n public addServerError(serverError: ConsumerMethodPath & ServerError) {\n const key = this.getKey(serverError);\n if (!this.errorDetails.has(key)) {\n this.errorDetails.set(key, serverError);\n }\n this.errorCounts.set(key, (this.errorCounts.get(key) || 0) + 1);\n\n const sentryEventId = getSentryEventId();\n if (sentryEventId) {\n this.sentryEventIds.set(key, sentryEventId);\n }\n }\n\n public getAndResetServerErrors() {\n const data: Array<ServerErrorsItem> = [];\n this.errorCounts.forEach((count, key) => {\n const serverError = this.errorDetails.get(key);\n if (serverError) {\n data.push({\n consumer: serverError.consumer || null,\n method: serverError.method,\n path: serverError.path,\n type: serverError.type,\n msg: truncateExceptionMessage(serverError.msg),\n traceback: truncateExceptionStackTrace(serverError.traceback),\n sentry_event_id: this.sentryEventIds.get(key) || null,\n error_count: count,\n });\n }\n });\n this.errorCounts.clear();\n this.errorDetails.clear();\n this.sentryEventIds.clear();\n return data;\n }\n\n private getKey(serverError: ConsumerMethodPath & ServerError) {\n const hashInput = [\n serverError.consumer || \"\",\n serverError.method.toUpperCase(),\n serverError.path,\n serverError.type,\n serverError.msg.trim(),\n serverError.traceback.trim(),\n ].join(\"|\");\n return createHash(\"md5\").update(hashInput).digest(\"hex\");\n }\n}\n\nexport function truncateExceptionMessage(msg: string) {\n if (msg.length <= MAX_MSG_LENGTH) {\n return msg;\n }\n const suffix = \"... (truncated)\";\n const cutoff = MAX_MSG_LENGTH - suffix.length;\n return msg.substring(0, cutoff) + suffix;\n}\n\nexport function truncateExceptionStackTrace(stack: string) {\n if (stack.length <= MAX_STACKTRACE_LENGTH) {\n return stack;\n }\n const suffix = \"... (truncated) ...\";\n const cutoff = MAX_STACKTRACE_LENGTH - suffix.length;\n const lines = stack.trim().split(\"\\n\");\n const truncatedLines: string[] = [];\n let length = 0;\n for (const line of lines) {\n if (length + line.length + 1 > cutoff) {\n truncatedLines.push(suffix);\n break;\n }\n truncatedLines.push(line);\n length += line.length + 1;\n }\n return truncatedLines.join(\"\\n\");\n}\n"],"mappings":";;AAAA,SAASA,kBAAkB;AAE3B,SAASC,wBAAwB;AAGjC,MAAMC,iBAAiB;AACvB,MAAMC,wBAAwB;AAE9B,MAAqBC,sBAArB,MAAqBA,oBAAAA;EACXC;EACAC;EACAC;EAER,cAAc;AACZ,SAAKF,cAAc,oBAAIG,IAAAA;AACvB,SAAKF,eAAe,oBAAIE,IAAAA;AACxB,SAAKD,iBAAiB,oBAAIC,IAAAA;EAC5B;EAEOC,eAAeC,aAA+C;AACnE,UAAMC,MAAM,KAAKC,OAAOF,WAAAA;AACxB,QAAI,CAAC,KAAKJ,aAAaO,IAAIF,GAAAA,GAAM;AAC/B,WAAKL,aAAaQ,IAAIH,KAAKD,WAAAA;IAC7B;AACA,SAAKL,YAAYS,IAAIH,MAAM,KAAKN,YAAYU,IAAIJ,GAAAA,KAAQ,KAAK,CAAA;AAE7D,UAAMK,gBAAgBf,iBAAAA;AACtB,QAAIe,eAAe;AACjB,WAAKT,eAAeO,IAAIH,KAAKK,aAAAA;IAC/B;EACF;EAEOC,0BAA0B;AAC/B,UAAMC,OAAgC,CAAA;AACtC,SAAKb,YAAYc,QAAQ,CAACC,OAAOT,QAAAA;AAC/B,YAAMD,cAAc,KAAKJ,aAAaS,IAAIJ,GAAAA;AAC1C,UAAID,aAAa;AACfQ,aAAKG,KAAK;UACRC,UAAUZ,YAAYY,YAAY;UAClCC,QAAQb,YAAYa;UACpBC,MAAMd,YAAYc;UAClBC,MAAMf,YAAYe;UAClBC,KAAKC,yBAAyBjB,YAAYgB,GAAG;UAC7CE,WAAWC,4BAA4BnB,YAAYkB,SAAS;UAC5DE,iBAAiB,KAAKvB,eAAeQ,IAAIJ,GAAAA,KAAQ;UACjDoB,aAAaX;QACf,CAAA;MACF;IACF,CAAA;AACA,SAAKf,YAAY2B,MAAK;AACtB,SAAK1B,aAAa0B,MAAK;AACvB,SAAKzB,eAAeyB,MAAK;AACzB,WAAOd;EACT;EAEQN,OAAOF,aAA+C;AAC5D,UAAMuB,YAAY;MAChBvB,YAAYY,YAAY;MACxBZ,YAAYa,OAAOW,YAAW;MAC9BxB,YAAYc;MACZd,YAAYe;MACZf,YAAYgB,IAAIS,KAAI;MACpBzB,YAAYkB,UAAUO,KAAI;MAC1BC,KAAK,GAAA;AACP,WAAOpC,WAAW,KAAA,EAAOqC,OAAOJ,SAAAA,EAAWK,OAAO,KAAA;EACpD;AACF;AA1DqBlC;AAArB,IAAqBA,qBAArB;AA4DO,SAASuB,yBAAyBD,KAAW;AAClD,MAAIA,IAAIa,UAAUrC,gBAAgB;AAChC,WAAOwB;EACT;AACA,QAAMc,SAAS;AACf,QAAMC,SAASvC,iBAAiBsC,OAAOD;AACvC,SAAOb,IAAIgB,UAAU,GAAGD,MAAAA,IAAUD;AACpC;AAPgBb;AAST,SAASE,4BAA4Bc,OAAa;AACvD,MAAIA,MAAMJ,UAAUpC,uBAAuB;AACzC,WAAOwC;EACT;AACA,QAAMH,SAAS;AACf,QAAMC,SAAStC,wBAAwBqC,OAAOD;AAC9C,QAAMK,QAAQD,MAAMR,KAAI,EAAGU,MAAM,IAAA;AACjC,QAAMC,iBAA2B,CAAA;AACjC,MAAIP,SAAS;AACb,aAAWQ,QAAQH,OAAO;AACxB,QAAIL,SAASQ,KAAKR,SAAS,IAAIE,QAAQ;AACrCK,qBAAezB,KAAKmB,MAAAA;AACpB;IACF;AACAM,mBAAezB,KAAK0B,IAAAA;AACpBR,cAAUQ,KAAKR,SAAS;EAC1B;AACA,SAAOO,eAAeV,KAAK,IAAA;AAC7B;AAlBgBP;","names":["createHash","getSentryEventId","MAX_MSG_LENGTH","MAX_STACKTRACE_LENGTH","ServerErrorCounter","errorCounts","errorDetails","sentryEventIds","Map","addServerError","serverError","key","getKey","has","set","get","sentryEventId","getAndResetServerErrors","data","forEach","count","push","consumer","method","path","type","msg","truncateExceptionMessage","traceback","truncateExceptionStackTrace","sentry_event_id","error_count","clear","hashInput","toUpperCase","trim","join","update","digest","length","suffix","cutoff","substring","stack","lines","split","truncatedLines","line"]}
|
|
1
|
+
{"version":3,"sources":["../../src/common/serverErrorCounter.ts"],"sourcesContent":["import { createHash } from \"node:crypto\";\n\nimport { getSentryEventId } from \"./sentry.js\";\nimport { ConsumerMethodPath, ServerError, ServerErrorsItem } from \"./types.js\";\n\nconst MAX_MSG_LENGTH = 2048;\nconst MAX_STACKTRACE_LENGTH = 65536;\n\nexport default class ServerErrorCounter {\n private errorCounts: Map<string, number>;\n private errorDetails: Map<string, ConsumerMethodPath & ServerError>;\n private sentryEventIds: Map<string, string>;\n\n constructor() {\n this.errorCounts = new Map();\n this.errorDetails = new Map();\n this.sentryEventIds = new Map();\n }\n\n public addServerError(serverError: ConsumerMethodPath & ServerError) {\n const key = this.getKey(serverError);\n if (!this.errorDetails.has(key)) {\n this.errorDetails.set(key, serverError);\n }\n this.errorCounts.set(key, (this.errorCounts.get(key) || 0) + 1);\n\n const sentryEventId = getSentryEventId();\n if (sentryEventId) {\n this.sentryEventIds.set(key, sentryEventId);\n }\n }\n\n public getAndResetServerErrors() {\n const data: Array<ServerErrorsItem> = [];\n this.errorCounts.forEach((count, key) => {\n const serverError = this.errorDetails.get(key);\n if (serverError) {\n data.push({\n consumer: serverError.consumer || null,\n method: serverError.method,\n path: serverError.path,\n type: serverError.type,\n msg: truncateExceptionMessage(serverError.msg),\n traceback: truncateExceptionStackTrace(serverError.traceback),\n sentry_event_id: this.sentryEventIds.get(key) || null,\n error_count: count,\n });\n }\n });\n this.errorCounts.clear();\n this.errorDetails.clear();\n this.sentryEventIds.clear();\n return data;\n }\n\n private getKey(serverError: ConsumerMethodPath & ServerError) {\n const hashInput = [\n serverError.consumer || \"\",\n serverError.method.toUpperCase(),\n serverError.path,\n serverError.type,\n serverError.msg.trim(),\n serverError.traceback.trim(),\n ].join(\"|\");\n return createHash(\"md5\").update(hashInput).digest(\"hex\");\n }\n}\n\nexport function truncateExceptionMessage(msg: string) {\n if (msg.length <= MAX_MSG_LENGTH) {\n return msg;\n }\n const suffix = \"... (truncated)\";\n const cutoff = MAX_MSG_LENGTH - suffix.length;\n return msg.substring(0, cutoff) + suffix;\n}\n\nexport function truncateExceptionStackTrace(stack: string) {\n if (stack.length <= MAX_STACKTRACE_LENGTH) {\n return stack;\n }\n const suffix = \"... (truncated) ...\";\n const cutoff = MAX_STACKTRACE_LENGTH - suffix.length;\n const lines = stack.trim().split(\"\\n\");\n const truncatedLines: string[] = [];\n let length = 0;\n for (const line of lines) {\n if (length + line.length + 1 > cutoff) {\n truncatedLines.push(suffix);\n break;\n }\n truncatedLines.push(line);\n length += line.length + 1;\n }\n return truncatedLines.join(\"\\n\");\n}\n\nexport { ServerErrorCounter };\n"],"mappings":";;AAAA,SAASA,kBAAkB;AAE3B,SAASC,wBAAwB;AAGjC,MAAMC,iBAAiB;AACvB,MAAMC,wBAAwB;AAE9B,MAAqBC,sBAArB,MAAqBA,oBAAAA;EACXC;EACAC;EACAC;EAER,cAAc;AACZ,SAAKF,cAAc,oBAAIG,IAAAA;AACvB,SAAKF,eAAe,oBAAIE,IAAAA;AACxB,SAAKD,iBAAiB,oBAAIC,IAAAA;EAC5B;EAEOC,eAAeC,aAA+C;AACnE,UAAMC,MAAM,KAAKC,OAAOF,WAAAA;AACxB,QAAI,CAAC,KAAKJ,aAAaO,IAAIF,GAAAA,GAAM;AAC/B,WAAKL,aAAaQ,IAAIH,KAAKD,WAAAA;IAC7B;AACA,SAAKL,YAAYS,IAAIH,MAAM,KAAKN,YAAYU,IAAIJ,GAAAA,KAAQ,KAAK,CAAA;AAE7D,UAAMK,gBAAgBf,iBAAAA;AACtB,QAAIe,eAAe;AACjB,WAAKT,eAAeO,IAAIH,KAAKK,aAAAA;IAC/B;EACF;EAEOC,0BAA0B;AAC/B,UAAMC,OAAgC,CAAA;AACtC,SAAKb,YAAYc,QAAQ,CAACC,OAAOT,QAAAA;AAC/B,YAAMD,cAAc,KAAKJ,aAAaS,IAAIJ,GAAAA;AAC1C,UAAID,aAAa;AACfQ,aAAKG,KAAK;UACRC,UAAUZ,YAAYY,YAAY;UAClCC,QAAQb,YAAYa;UACpBC,MAAMd,YAAYc;UAClBC,MAAMf,YAAYe;UAClBC,KAAKC,yBAAyBjB,YAAYgB,GAAG;UAC7CE,WAAWC,4BAA4BnB,YAAYkB,SAAS;UAC5DE,iBAAiB,KAAKvB,eAAeQ,IAAIJ,GAAAA,KAAQ;UACjDoB,aAAaX;QACf,CAAA;MACF;IACF,CAAA;AACA,SAAKf,YAAY2B,MAAK;AACtB,SAAK1B,aAAa0B,MAAK;AACvB,SAAKzB,eAAeyB,MAAK;AACzB,WAAOd;EACT;EAEQN,OAAOF,aAA+C;AAC5D,UAAMuB,YAAY;MAChBvB,YAAYY,YAAY;MACxBZ,YAAYa,OAAOW,YAAW;MAC9BxB,YAAYc;MACZd,YAAYe;MACZf,YAAYgB,IAAIS,KAAI;MACpBzB,YAAYkB,UAAUO,KAAI;MAC1BC,KAAK,GAAA;AACP,WAAOpC,WAAW,KAAA,EAAOqC,OAAOJ,SAAAA,EAAWK,OAAO,KAAA;EACpD;AACF;AA1DqBlC;AAArB,IAAqBA,qBAArB;AA4DO,SAASuB,yBAAyBD,KAAW;AAClD,MAAIA,IAAIa,UAAUrC,gBAAgB;AAChC,WAAOwB;EACT;AACA,QAAMc,SAAS;AACf,QAAMC,SAASvC,iBAAiBsC,OAAOD;AACvC,SAAOb,IAAIgB,UAAU,GAAGD,MAAAA,IAAUD;AACpC;AAPgBb;AAST,SAASE,4BAA4Bc,OAAa;AACvD,MAAIA,MAAMJ,UAAUpC,uBAAuB;AACzC,WAAOwC;EACT;AACA,QAAMH,SAAS;AACf,QAAMC,SAAStC,wBAAwBqC,OAAOD;AAC9C,QAAMK,QAAQD,MAAMR,KAAI,EAAGU,MAAM,IAAA;AACjC,QAAMC,iBAA2B,CAAA;AACjC,MAAIP,SAAS;AACb,aAAWQ,QAAQH,OAAO;AACxB,QAAIL,SAASQ,KAAKR,SAAS,IAAIE,QAAQ;AACrCK,qBAAezB,KAAKmB,MAAAA;AACpB;IACF;AACAM,mBAAezB,KAAK0B,IAAAA;AACpBR,cAAUQ,KAAKR,SAAS;EAC1B;AACA,SAAOO,eAAeV,KAAK,IAAA;AAC7B;AAlBgBP;","names":["createHash","getSentryEventId","MAX_MSG_LENGTH","MAX_STACKTRACE_LENGTH","ServerErrorCounter","errorCounts","errorDetails","sentryEventIds","Map","addServerError","serverError","key","getKey","has","set","get","sentryEventId","getAndResetServerErrors","data","forEach","count","push","consumer","method","path","type","msg","truncateExceptionMessage","traceback","truncateExceptionStackTrace","sentry_event_id","error_count","clear","hashInput","toUpperCase","trim","join","update","digest","length","suffix","cutoff","substring","stack","lines","split","truncatedLines","line"]}
|
|
@@ -20,6 +20,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
20
20
|
var spanCollector_exports = {};
|
|
21
21
|
__export(spanCollector_exports, {
|
|
22
22
|
ApitallySpanProcessor: () => ApitallySpanProcessor,
|
|
23
|
+
SpanCollector: () => SpanCollector,
|
|
23
24
|
default: () => SpanCollector
|
|
24
25
|
});
|
|
25
26
|
module.exports = __toCommonJS(spanCollector_exports);
|
|
@@ -194,6 +195,7 @@ __name(_ApitallySpanProcessor, "ApitallySpanProcessor");
|
|
|
194
195
|
let ApitallySpanProcessor = _ApitallySpanProcessor;
|
|
195
196
|
// Annotate the CommonJS export names for ESM import in node:
|
|
196
197
|
0 && (module.exports = {
|
|
197
|
-
ApitallySpanProcessor
|
|
198
|
+
ApitallySpanProcessor,
|
|
199
|
+
SpanCollector
|
|
198
200
|
});
|
|
199
201
|
//# sourceMappingURL=spanCollector.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/common/spanCollector.ts"],"sourcesContent":["import {\n context,\n SpanKind,\n SpanStatusCode,\n trace,\n type Tracer,\n} from \"@opentelemetry/api\";\nimport {\n type ReadableSpan,\n type Span,\n SpanProcessor,\n} from \"@opentelemetry/sdk-trace-base\";\n\nimport { ApitallyClient } from \"./client.js\";\n\nexport type SpanData = {\n spanId: string;\n parentSpanId: string | null;\n name: string;\n kind: string;\n startTime: string; // bigint as string\n endTime: string; // bigint as string\n status?: string;\n attributes?: Record<string, unknown>;\n};\n\nexport type SpanHandle = {\n traceId?: string;\n setName: (name: string) => void;\n runInContext: <T>(fn: () => T) => T;\n enterContext: () => void;\n end: () => SpanData[] | undefined;\n};\n\nconst TRACE_MAX_AGE = 5 * 60 * 1000; // 5 minutes\n\nexport default class SpanCollector implements SpanProcessor {\n public enabled: boolean;\n private includedSpanIds: Map<string, Set<string>> = new Map();\n private collectedSpans: Map<string, SpanData[]> = new Map();\n private traceStartTimes: Map<string, number> = new Map();\n private maintainIntervalId?: NodeJS.Timeout;\n private tracer?: Tracer;\n\n constructor(enabled: boolean) {\n this.enabled = enabled;\n\n if (enabled) {\n this.tracer = trace.getTracer(\"apitally\");\n this.maintainIntervalId = setInterval(() => {\n this.maintain();\n }, 60_000);\n }\n }\n\n startSpan(): SpanHandle {\n if (!this.enabled || !this.tracer) {\n return {\n setName: () => void 0,\n runInContext: <T>(fn: () => T): T => {\n return fn();\n },\n enterContext: () => void 0,\n end: () => undefined,\n };\n }\n\n const span = this.tracer.startSpan(\"root\");\n const spanCtx = span.spanContext();\n const traceId = spanCtx.traceId;\n const ctx = trace.setSpan(context.active(), span);\n let ended = false;\n\n this.includedSpanIds.set(traceId, new Set([spanCtx.spanId]));\n this.collectedSpans.set(traceId, []);\n this.traceStartTimes.set(traceId, Date.now());\n\n return {\n traceId,\n setName: (name: string) => {\n span.updateName(name);\n },\n runInContext: <T>(fn: () => T): T => {\n return context.with(ctx, fn);\n },\n enterContext: () => {\n try {\n // Access the global context manager's internal AsyncLocalStorage\n const contextManager = (context as any)._getContextManager?.();\n contextManager?._asyncLocalStorage?.enterWith(ctx);\n } catch {\n // Ignore errors accessing internals\n }\n },\n end: () => {\n if (ended) return;\n span.end();\n ended = true;\n return this.getAndClearSpans(traceId);\n },\n };\n }\n\n private getAndClearSpans(traceId: string): SpanData[] {\n const spans = this.collectedSpans.get(traceId) ?? [];\n this.collectedSpans.delete(traceId);\n this.includedSpanIds.delete(traceId);\n this.traceStartTimes.delete(traceId);\n return spans;\n }\n\n onStart(span: Span): void {\n if (!this.enabled) return;\n\n const ctx = span.spanContext();\n const traceId = ctx.traceId;\n const spanId = ctx.spanId;\n\n const includedSpans = this.includedSpanIds.get(traceId);\n if (!includedSpans) return;\n\n const parentSpanId = span.parentSpanContext?.spanId;\n if (parentSpanId && includedSpans.has(parentSpanId)) {\n includedSpans.add(spanId);\n }\n }\n\n onEnd(span: ReadableSpan): void {\n if (!this.enabled) return;\n\n const ctx = span.spanContext();\n const traceId = ctx.traceId;\n const spanId = ctx.spanId;\n\n const includedSpans = this.includedSpanIds.get(traceId);\n if (!includedSpans || !includedSpans.has(spanId)) return;\n\n const spans = this.collectedSpans.get(traceId);\n if (spans) {\n spans.push(this.serializeSpan(span));\n }\n }\n\n private serializeSpan(span: ReadableSpan): SpanData {\n const ctx = span.spanContext();\n\n const data: SpanData = {\n spanId: ctx.spanId,\n parentSpanId: span.parentSpanContext?.spanId || null,\n name: span.name,\n kind: SpanKind[span.kind] ?? \"INTERNAL\",\n // HrTime is [seconds, nanoseconds], convert to nanoseconds as string to avoid precision loss\n startTime: (\n BigInt(span.startTime[0]) * 1_000_000_000n +\n BigInt(span.startTime[1])\n ).toString(),\n endTime: (\n BigInt(span.endTime[0]) * 1_000_000_000n +\n BigInt(span.endTime[1])\n ).toString(),\n };\n\n if (span.status.code !== SpanStatusCode.UNSET) {\n data.status = SpanStatusCode[span.status.code];\n }\n\n if (span.attributes && Object.keys(span.attributes).length > 0) {\n data.attributes = { ...span.attributes };\n }\n\n return data;\n }\n\n private maintain() {\n const now = Date.now();\n for (const [traceId, startTime] of this.traceStartTimes) {\n if (now - startTime > TRACE_MAX_AGE) {\n this.collectedSpans.delete(traceId);\n this.includedSpanIds.delete(traceId);\n this.traceStartTimes.delete(traceId);\n }\n }\n }\n\n async shutdown(): Promise<void> {\n this.enabled = false;\n this.includedSpanIds.clear();\n this.collectedSpans.clear();\n this.traceStartTimes.clear();\n if (this.maintainIntervalId) {\n clearInterval(this.maintainIntervalId);\n }\n }\n\n async forceFlush(): Promise<void> {\n // Nothing to flush since we collect spans synchronously\n }\n}\n\nexport class ApitallySpanProcessor implements SpanProcessor {\n private getCollector(): SpanCollector | undefined {\n try {\n return ApitallyClient.getInstance().spanCollector;\n } catch {\n return undefined;\n }\n }\n\n onStart(span: Span) {\n this.getCollector()?.onStart(span);\n }\n\n onEnd(span: ReadableSpan) {\n this.getCollector()?.onEnd(span);\n }\n\n async shutdown() {\n this.getCollector()?.shutdown();\n }\n\n async forceFlush() {\n this.getCollector()?.forceFlush();\n }\n}\n"],"mappings":"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","names":["TRACE_MAX_AGE","SpanCollector","enabled","includedSpanIds","Map","collectedSpans","traceStartTimes","maintainIntervalId","tracer","trace","getTracer","setInterval","maintain","startSpan","setName","runInContext","fn","enterContext","end","undefined","span","spanCtx","spanContext","traceId","ctx","setSpan","context","active","ended","set","Set","spanId","Date","now","name","updateName","with","contextManager","_getContextManager","_asyncLocalStorage","enterWith","getAndClearSpans","spans","get","delete","onStart","includedSpans","parentSpanId","parentSpanContext","has","add","onEnd","push","serializeSpan","data","kind","SpanKind","startTime","BigInt","toString","endTime","status","code","SpanStatusCode","UNSET","attributes","Object","keys","length","shutdown","clear","clearInterval","forceFlush","ApitallySpanProcessor","getCollector","ApitallyClient","getInstance","spanCollector"]}
|
|
1
|
+
{"version":3,"sources":["../../src/common/spanCollector.ts"],"sourcesContent":["import {\n context,\n SpanKind,\n SpanStatusCode,\n trace,\n type Tracer,\n} from \"@opentelemetry/api\";\nimport {\n type ReadableSpan,\n type Span,\n SpanProcessor,\n} from \"@opentelemetry/sdk-trace-base\";\n\nimport { ApitallyClient } from \"./client.js\";\n\nexport type SpanData = {\n spanId: string;\n parentSpanId: string | null;\n name: string;\n kind: string;\n startTime: string; // bigint as string\n endTime: string; // bigint as string\n status?: string;\n attributes?: Record<string, unknown>;\n};\n\nexport type SpanHandle = {\n traceId?: string;\n setName: (name: string) => void;\n runInContext: <T>(fn: () => T) => T;\n enterContext: () => void;\n end: () => SpanData[] | undefined;\n};\n\nconst TRACE_MAX_AGE = 5 * 60 * 1000; // 5 minutes\n\nexport default class SpanCollector implements SpanProcessor {\n public enabled: boolean;\n private includedSpanIds: Map<string, Set<string>> = new Map();\n private collectedSpans: Map<string, SpanData[]> = new Map();\n private traceStartTimes: Map<string, number> = new Map();\n private maintainIntervalId?: NodeJS.Timeout;\n private tracer?: Tracer;\n\n constructor(enabled: boolean) {\n this.enabled = enabled;\n\n if (enabled) {\n this.tracer = trace.getTracer(\"apitally\");\n this.maintainIntervalId = setInterval(() => {\n this.maintain();\n }, 60_000);\n }\n }\n\n startSpan(): SpanHandle {\n if (!this.enabled || !this.tracer) {\n return {\n setName: () => void 0,\n runInContext: <T>(fn: () => T): T => {\n return fn();\n },\n enterContext: () => void 0,\n end: () => undefined,\n };\n }\n\n const span = this.tracer.startSpan(\"root\");\n const spanCtx = span.spanContext();\n const traceId = spanCtx.traceId;\n const ctx = trace.setSpan(context.active(), span);\n let ended = false;\n\n this.includedSpanIds.set(traceId, new Set([spanCtx.spanId]));\n this.collectedSpans.set(traceId, []);\n this.traceStartTimes.set(traceId, Date.now());\n\n return {\n traceId,\n setName: (name: string) => {\n span.updateName(name);\n },\n runInContext: <T>(fn: () => T): T => {\n return context.with(ctx, fn);\n },\n enterContext: () => {\n try {\n // Access the global context manager's internal AsyncLocalStorage\n const contextManager = (context as any)._getContextManager?.();\n contextManager?._asyncLocalStorage?.enterWith(ctx);\n } catch {\n // Ignore errors accessing internals\n }\n },\n end: () => {\n if (ended) return;\n span.end();\n ended = true;\n return this.getAndClearSpans(traceId);\n },\n };\n }\n\n private getAndClearSpans(traceId: string): SpanData[] {\n const spans = this.collectedSpans.get(traceId) ?? [];\n this.collectedSpans.delete(traceId);\n this.includedSpanIds.delete(traceId);\n this.traceStartTimes.delete(traceId);\n return spans;\n }\n\n onStart(span: Span): void {\n if (!this.enabled) return;\n\n const ctx = span.spanContext();\n const traceId = ctx.traceId;\n const spanId = ctx.spanId;\n\n const includedSpans = this.includedSpanIds.get(traceId);\n if (!includedSpans) return;\n\n const parentSpanId = span.parentSpanContext?.spanId;\n if (parentSpanId && includedSpans.has(parentSpanId)) {\n includedSpans.add(spanId);\n }\n }\n\n onEnd(span: ReadableSpan): void {\n if (!this.enabled) return;\n\n const ctx = span.spanContext();\n const traceId = ctx.traceId;\n const spanId = ctx.spanId;\n\n const includedSpans = this.includedSpanIds.get(traceId);\n if (!includedSpans || !includedSpans.has(spanId)) return;\n\n const spans = this.collectedSpans.get(traceId);\n if (spans) {\n spans.push(this.serializeSpan(span));\n }\n }\n\n private serializeSpan(span: ReadableSpan): SpanData {\n const ctx = span.spanContext();\n\n const data: SpanData = {\n spanId: ctx.spanId,\n parentSpanId: span.parentSpanContext?.spanId || null,\n name: span.name,\n kind: SpanKind[span.kind] ?? \"INTERNAL\",\n // HrTime is [seconds, nanoseconds], convert to nanoseconds as string to avoid precision loss\n startTime: (\n BigInt(span.startTime[0]) * 1_000_000_000n +\n BigInt(span.startTime[1])\n ).toString(),\n endTime: (\n BigInt(span.endTime[0]) * 1_000_000_000n +\n BigInt(span.endTime[1])\n ).toString(),\n };\n\n if (span.status.code !== SpanStatusCode.UNSET) {\n data.status = SpanStatusCode[span.status.code];\n }\n\n if (span.attributes && Object.keys(span.attributes).length > 0) {\n data.attributes = { ...span.attributes };\n }\n\n return data;\n }\n\n private maintain() {\n const now = Date.now();\n for (const [traceId, startTime] of this.traceStartTimes) {\n if (now - startTime > TRACE_MAX_AGE) {\n this.collectedSpans.delete(traceId);\n this.includedSpanIds.delete(traceId);\n this.traceStartTimes.delete(traceId);\n }\n }\n }\n\n async shutdown(): Promise<void> {\n this.enabled = false;\n this.includedSpanIds.clear();\n this.collectedSpans.clear();\n this.traceStartTimes.clear();\n if (this.maintainIntervalId) {\n clearInterval(this.maintainIntervalId);\n }\n }\n\n async forceFlush(): Promise<void> {\n // Nothing to flush since we collect spans synchronously\n }\n}\n\nexport class ApitallySpanProcessor implements SpanProcessor {\n private getCollector(): SpanCollector | undefined {\n try {\n return ApitallyClient.getInstance().spanCollector;\n } catch {\n return undefined;\n }\n }\n\n onStart(span: Span) {\n this.getCollector()?.onStart(span);\n }\n\n onEnd(span: ReadableSpan) {\n this.getCollector()?.onEnd(span);\n }\n\n async shutdown() {\n this.getCollector()?.shutdown();\n }\n\n async forceFlush() {\n this.getCollector()?.forceFlush();\n }\n}\n\nexport { SpanCollector };\n"],"mappings":"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","names":["TRACE_MAX_AGE","SpanCollector","enabled","includedSpanIds","Map","collectedSpans","traceStartTimes","maintainIntervalId","tracer","trace","getTracer","setInterval","maintain","startSpan","setName","runInContext","fn","enterContext","end","undefined","span","spanCtx","spanContext","traceId","ctx","setSpan","context","active","ended","set","Set","spanId","Date","now","name","updateName","with","contextManager","_getContextManager","_asyncLocalStorage","enterWith","getAndClearSpans","spans","get","delete","onStart","includedSpans","parentSpanId","parentSpanContext","has","add","onEnd","push","serializeSpan","data","kind","SpanKind","startTime","BigInt","toString","endTime","status","code","SpanStatusCode","UNSET","attributes","Object","keys","length","shutdown","clear","clearInterval","forceFlush","ApitallySpanProcessor","getCollector","ApitallyClient","getInstance","spanCollector"]}
|
|
@@ -42,4 +42,4 @@ declare class ApitallySpanProcessor implements SpanProcessor {
|
|
|
42
42
|
forceFlush(): Promise<void>;
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
-
export { ApitallySpanProcessor, type SpanData, type SpanHandle, SpanCollector as default };
|
|
45
|
+
export { ApitallySpanProcessor, SpanCollector, type SpanData, type SpanHandle, SpanCollector as default };
|
|
@@ -42,4 +42,4 @@ declare class ApitallySpanProcessor implements SpanProcessor {
|
|
|
42
42
|
forceFlush(): Promise<void>;
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
-
export { ApitallySpanProcessor, type SpanData, type SpanHandle, SpanCollector as default };
|
|
45
|
+
export { ApitallySpanProcessor, SpanCollector, type SpanData, type SpanHandle, SpanCollector as default };
|