{"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":["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"]}