apitally 0.24.0 → 0.25.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (163) hide show
  1. package/dist/adonisjs/configure.cjs +1 -1
  2. package/dist/adonisjs/index.cjs +4 -1
  3. package/dist/adonisjs/index.cjs.map +1 -1
  4. package/dist/adonisjs/index.d.cts +2 -0
  5. package/dist/adonisjs/index.d.ts +2 -0
  6. package/dist/adonisjs/index.js +2 -0
  7. package/dist/adonisjs/index.js.map +1 -1
  8. package/dist/adonisjs/middleware.cjs +9 -5
  9. package/dist/adonisjs/middleware.cjs.map +1 -1
  10. package/dist/adonisjs/middleware.d.cts +2 -0
  11. package/dist/adonisjs/middleware.d.ts +2 -0
  12. package/dist/adonisjs/middleware.js +6 -2
  13. package/dist/adonisjs/middleware.js.map +1 -1
  14. package/dist/adonisjs/provider.cjs +3 -3
  15. package/dist/adonisjs/provider.d.cts +2 -0
  16. package/dist/adonisjs/provider.d.ts +2 -0
  17. package/dist/adonisjs/stubs/config/apitally.stub +1 -0
  18. package/dist/common/client.cjs +13 -9
  19. package/dist/common/client.cjs.map +1 -1
  20. package/dist/common/client.d.cts +3 -0
  21. package/dist/common/client.d.ts +3 -0
  22. package/dist/common/client.js +4 -0
  23. package/dist/common/client.js.map +1 -1
  24. package/dist/common/consumerRegistry.d.cts +2 -0
  25. package/dist/common/consumerRegistry.d.ts +2 -0
  26. package/dist/common/requestCounter.d.cts +2 -0
  27. package/dist/common/requestCounter.d.ts +2 -0
  28. package/dist/common/requestLogger.cjs +14 -5
  29. package/dist/common/requestLogger.cjs.map +1 -1
  30. package/dist/common/requestLogger.d.cts +6 -1
  31. package/dist/common/requestLogger.d.ts +6 -1
  32. package/dist/common/requestLogger.js +11 -2
  33. package/dist/common/requestLogger.js.map +1 -1
  34. package/dist/common/serverErrorCounter.cjs +1 -1
  35. package/dist/common/serverErrorCounter.d.cts +2 -0
  36. package/dist/common/serverErrorCounter.d.ts +2 -0
  37. package/dist/common/spanCollector.cjs +217 -0
  38. package/dist/common/spanCollector.cjs.map +1 -0
  39. package/dist/common/spanCollector.d.cts +50 -0
  40. package/dist/common/spanCollector.d.ts +50 -0
  41. package/dist/common/spanCollector.js +194 -0
  42. package/dist/common/spanCollector.js.map +1 -0
  43. package/dist/common/types.d.cts +2 -0
  44. package/dist/common/types.d.ts +2 -0
  45. package/dist/common/validationErrorCounter.d.cts +2 -0
  46. package/dist/common/validationErrorCounter.d.ts +2 -0
  47. package/dist/elysia/index.cjs +4 -1
  48. package/dist/elysia/index.cjs.map +1 -1
  49. package/dist/elysia/index.d.cts +2 -0
  50. package/dist/elysia/index.d.ts +2 -0
  51. package/dist/elysia/index.js +2 -0
  52. package/dist/elysia/index.js.map +1 -1
  53. package/dist/elysia/plugin.cjs +16 -8
  54. package/dist/elysia/plugin.cjs.map +1 -1
  55. package/dist/elysia/plugin.d.cts +4 -0
  56. package/dist/elysia/plugin.d.ts +4 -0
  57. package/dist/elysia/plugin.js +9 -1
  58. package/dist/elysia/plugin.js.map +1 -1
  59. package/dist/elysia/utils.cjs +1 -1
  60. package/dist/elysia/utils.d.cts +2 -0
  61. package/dist/elysia/utils.d.ts +2 -0
  62. package/dist/express/index.cjs +4 -1
  63. package/dist/express/index.cjs.map +1 -1
  64. package/dist/express/index.d.cts +2 -0
  65. package/dist/express/index.d.ts +2 -0
  66. package/dist/express/index.js +2 -0
  67. package/dist/express/index.js.map +1 -1
  68. package/dist/express/middleware.cjs +92 -84
  69. package/dist/express/middleware.cjs.map +1 -1
  70. package/dist/express/middleware.d.cts +2 -0
  71. package/dist/express/middleware.d.ts +2 -0
  72. package/dist/express/middleware.js +85 -77
  73. package/dist/express/middleware.js.map +1 -1
  74. package/dist/fastify/index.cjs +4 -1
  75. package/dist/fastify/index.cjs.map +1 -1
  76. package/dist/fastify/index.d.cts +2 -0
  77. package/dist/fastify/index.d.ts +2 -0
  78. package/dist/fastify/index.js +2 -0
  79. package/dist/fastify/index.js.map +1 -1
  80. package/dist/fastify/plugin.cjs +20 -11
  81. package/dist/fastify/plugin.cjs.map +1 -1
  82. package/dist/fastify/plugin.d.cts +4 -0
  83. package/dist/fastify/plugin.d.ts +4 -0
  84. package/dist/fastify/plugin.js +14 -5
  85. package/dist/fastify/plugin.js.map +1 -1
  86. package/dist/h3/index.cjs +4 -1
  87. package/dist/h3/index.cjs.map +1 -1
  88. package/dist/h3/index.d.cts +2 -0
  89. package/dist/h3/index.d.ts +2 -0
  90. package/dist/h3/index.js +2 -0
  91. package/dist/h3/index.js.map +1 -1
  92. package/dist/h3/plugin.cjs +20 -12
  93. package/dist/h3/plugin.cjs.map +1 -1
  94. package/dist/h3/plugin.d.cts +4 -0
  95. package/dist/h3/plugin.d.ts +4 -0
  96. package/dist/h3/plugin.js +13 -5
  97. package/dist/h3/plugin.js.map +1 -1
  98. package/dist/h3/utils.cjs +1 -1
  99. package/dist/hapi/index.cjs +4 -1
  100. package/dist/hapi/index.cjs.map +1 -1
  101. package/dist/hapi/index.d.cts +2 -0
  102. package/dist/hapi/index.d.ts +2 -0
  103. package/dist/hapi/index.js +2 -0
  104. package/dist/hapi/index.js.map +1 -1
  105. package/dist/hapi/plugin.cjs +15 -8
  106. package/dist/hapi/plugin.cjs.map +1 -1
  107. package/dist/hapi/plugin.d.cts +4 -0
  108. package/dist/hapi/plugin.d.ts +4 -0
  109. package/dist/hapi/plugin.js +10 -3
  110. package/dist/hapi/plugin.js.map +1 -1
  111. package/dist/hapi/utils.cjs +1 -1
  112. package/dist/hapi/utils.d.cts +2 -0
  113. package/dist/hapi/utils.d.ts +2 -0
  114. package/dist/hono/index.cjs +4 -1
  115. package/dist/hono/index.cjs.map +1 -1
  116. package/dist/hono/index.d.cts +2 -0
  117. package/dist/hono/index.d.ts +2 -0
  118. package/dist/hono/index.js +2 -0
  119. package/dist/hono/index.js.map +1 -1
  120. package/dist/hono/middleware.cjs +13 -9
  121. package/dist/hono/middleware.cjs.map +1 -1
  122. package/dist/hono/middleware.d.cts +2 -0
  123. package/dist/hono/middleware.d.ts +2 -0
  124. package/dist/hono/middleware.js +6 -2
  125. package/dist/hono/middleware.js.map +1 -1
  126. package/dist/hono/utils.cjs +1 -1
  127. package/dist/hono/utils.d.cts +2 -0
  128. package/dist/hono/utils.d.ts +2 -0
  129. package/dist/koa/index.cjs +4 -1
  130. package/dist/koa/index.cjs.map +1 -1
  131. package/dist/koa/index.d.cts +2 -0
  132. package/dist/koa/index.d.ts +2 -0
  133. package/dist/koa/index.js +2 -0
  134. package/dist/koa/index.js.map +1 -1
  135. package/dist/koa/middleware.cjs +13 -9
  136. package/dist/koa/middleware.cjs.map +1 -1
  137. package/dist/koa/middleware.d.cts +2 -0
  138. package/dist/koa/middleware.d.ts +2 -0
  139. package/dist/koa/middleware.js +8 -4
  140. package/dist/koa/middleware.js.map +1 -1
  141. package/dist/loggers/console.d.cts +4 -0
  142. package/dist/loggers/console.d.ts +4 -0
  143. package/dist/loggers/hapi.d.cts +4 -0
  144. package/dist/loggers/hapi.d.ts +4 -0
  145. package/dist/loggers/index.cjs +5 -5
  146. package/dist/loggers/index.d.cts +4 -0
  147. package/dist/loggers/index.d.ts +4 -0
  148. package/dist/loggers/nestjs.cjs +1 -1
  149. package/dist/loggers/nestjs.d.cts +4 -0
  150. package/dist/loggers/nestjs.d.ts +4 -0
  151. package/dist/loggers/pino.cjs +1 -1
  152. package/dist/loggers/pino.d.cts +4 -0
  153. package/dist/loggers/pino.d.ts +4 -0
  154. package/dist/loggers/winston.cjs +1 -1
  155. package/dist/loggers/winston.d.cts +4 -0
  156. package/dist/loggers/winston.d.ts +4 -0
  157. package/dist/nestjs/index.cjs +3 -0
  158. package/dist/nestjs/index.cjs.map +1 -1
  159. package/dist/nestjs/index.d.cts +2 -0
  160. package/dist/nestjs/index.d.ts +2 -0
  161. package/dist/nestjs/index.js +2 -0
  162. package/dist/nestjs/index.js.map +1 -1
  163. package/package.json +6 -2
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/common/client.ts"],"sourcesContent":["import fetchRetry from \"fetch-retry\";\nimport { randomUUID } from \"node:crypto\";\n\nimport ConsumerRegistry from \"./consumerRegistry.js\";\nimport { getOrCreateInstanceUuid } from \"./instance.js\";\nimport { Logger, getLogger } from \"./logging.js\";\nimport { isValidClientId, isValidEnv } from \"./paramValidation.js\";\nimport RequestCounter from \"./requestCounter.js\";\nimport RequestLogger from \"./requestLogger.js\";\nimport { getCpuMemoryUsage } from \"./resources.js\";\nimport ServerErrorCounter from \"./serverErrorCounter.js\";\nimport {\n ApitallyConfig,\n StartupData,\n StartupPayload,\n SyncPayload,\n} from \"./types.js\";\nimport ValidationErrorCounter from \"./validationErrorCounter.js\";\n\nconst SYNC_INTERVAL = 60000; // 60 seconds\nconst INITIAL_SYNC_INTERVAL = 10000; // 10 seconds\nconst INITIAL_SYNC_INTERVAL_DURATION = 3600000; // 1 hour\nconst MAX_QUEUE_TIME = 3.6e6; // 1 hour\n\nclass HTTPError extends Error {\n public response: Response;\n\n constructor(response: Response) {\n const reason = response.status\n ? `status code ${response.status}`\n : \"an unknown error\";\n super(`Request failed with ${reason}`);\n this.response = response;\n }\n}\n\nexport class ApitallyClient {\n private clientId: string;\n private env: string;\n\n private static instance?: ApitallyClient;\n private instanceUuid: string;\n private syncDataQueue: SyncPayload[];\n private syncIntervalId?: NodeJS.Timeout;\n public startupData?: StartupData;\n private startupDataSent: boolean = false;\n private enabled: boolean = true;\n\n public requestCounter: RequestCounter;\n public requestLogger: RequestLogger;\n public validationErrorCounter: ValidationErrorCounter;\n public serverErrorCounter: ServerErrorCounter;\n public consumerRegistry: ConsumerRegistry;\n public logger: Logger;\n\n constructor({\n clientId,\n env = \"dev\",\n requestLogging,\n requestLoggingConfig,\n logger,\n }: ApitallyConfig) {\n if (ApitallyClient.instance) {\n throw new Error(\"Apitally client is already initialized\");\n }\n\n this.logger = logger ?? getLogger();\n\n if (!isValidClientId(clientId)) {\n this.logger.error(\n `Invalid Apitally client ID '${clientId}' (expecting hexadecimal UUID format)`,\n );\n this.enabled = false;\n }\n if (!isValidEnv(env)) {\n this.logger.error(\n `Invalid Apitally env '${env}' (expecting 1-32 alphanumeric characters and hyphens only)`,\n );\n this.enabled = false;\n }\n if (requestLoggingConfig && !requestLogging) {\n console.warn(\n \"requestLoggingConfig is deprecated, use requestLogging instead.\",\n );\n }\n\n ApitallyClient.instance = this;\n this.clientId = clientId;\n this.env = env;\n this.instanceUuid = getOrCreateInstanceUuid(clientId, env);\n this.syncDataQueue = [];\n this.requestCounter = new RequestCounter();\n this.requestLogger = new RequestLogger(\n requestLogging ?? requestLoggingConfig,\n );\n this.validationErrorCounter = new ValidationErrorCounter();\n this.serverErrorCounter = new ServerErrorCounter();\n this.consumerRegistry = new ConsumerRegistry();\n this.handleShutdown = this.handleShutdown.bind(this);\n }\n\n public static getInstance() {\n if (!ApitallyClient.instance) {\n throw new Error(\"Apitally client is not initialized\");\n }\n return ApitallyClient.instance;\n }\n\n public isEnabled() {\n return this.enabled;\n }\n\n public static async shutdown() {\n if (ApitallyClient.instance) {\n await ApitallyClient.instance.handleShutdown();\n }\n }\n\n public async handleShutdown() {\n this.enabled = false;\n this.stopSync();\n await this.sendSyncData();\n await this.sendLogData();\n await this.requestLogger.close();\n ApitallyClient.instance = undefined;\n }\n\n private getHubUrlPrefix() {\n const baseURL =\n process.env.APITALLY_HUB_BASE_URL || \"https://hub.apitally.io\";\n const version = \"v2\";\n return `${baseURL}/${version}/${this.clientId}/${this.env}/`;\n }\n\n private async sendData(url: string, payload: any) {\n const fetchWithRetry = fetchRetry(fetch, {\n retries: 3,\n retryDelay: 1000,\n retryOn: [408, 429, 500, 502, 503, 504],\n });\n const response = await fetchWithRetry(this.getHubUrlPrefix() + url, {\n method: \"POST\",\n body: JSON.stringify(payload),\n headers: { \"Content-Type\": \"application/json\" },\n });\n if (!response.ok) {\n throw new HTTPError(response);\n }\n }\n\n public startSync() {\n if (!this.enabled) {\n return;\n }\n this.sync();\n this.syncIntervalId = setInterval(() => {\n this.sync();\n }, INITIAL_SYNC_INTERVAL);\n setTimeout(() => {\n if (this.syncIntervalId) {\n clearInterval(this.syncIntervalId);\n this.syncIntervalId = setInterval(() => {\n this.sync();\n }, SYNC_INTERVAL);\n }\n }, INITIAL_SYNC_INTERVAL_DURATION);\n }\n\n private async sync() {\n try {\n const promises = [this.sendSyncData(), this.sendLogData()];\n if (!this.startupDataSent) {\n promises.push(this.sendStartupData());\n }\n await Promise.all(promises);\n } catch (error) {\n this.logger.error(\"Error while syncing with Apitally Hub\", {\n error,\n });\n }\n }\n\n private stopSync() {\n if (this.syncIntervalId) {\n clearInterval(this.syncIntervalId);\n this.syncIntervalId = undefined;\n }\n }\n\n public setStartupData(data: StartupData) {\n this.startupData = data;\n this.startupDataSent = false;\n }\n\n private async sendStartupData() {\n if (this.startupData) {\n this.logger.debug(\"Sending startup data to Apitally Hub\");\n const payload: StartupPayload = {\n instance_uuid: this.instanceUuid,\n message_uuid: randomUUID(),\n ...this.startupData,\n };\n try {\n await this.sendData(\"startup\", payload);\n this.startupDataSent = true;\n } catch (error) {\n const handled = this.handleHubError(error);\n if (!handled) {\n this.logger.error((error as Error).message);\n this.logger.debug(\n \"Error while sending startup data to Apitally Hub (will retry)\",\n { error },\n );\n }\n }\n }\n }\n\n private async sendSyncData() {\n this.logger.debug(\"Synchronizing data with Apitally Hub\");\n const newPayload: SyncPayload = {\n timestamp: Date.now() / 1000,\n instance_uuid: this.instanceUuid,\n message_uuid: randomUUID(),\n requests: this.requestCounter.getAndResetRequests(),\n validation_errors:\n this.validationErrorCounter.getAndResetValidationErrors(),\n server_errors: this.serverErrorCounter.getAndResetServerErrors(),\n consumers: this.consumerRegistry.getAndResetUpdatedConsumers(),\n resources: getCpuMemoryUsage(),\n };\n this.syncDataQueue.push(newPayload);\n\n let i = 0;\n while (this.syncDataQueue.length > 0) {\n const payload = this.syncDataQueue.shift();\n if (payload) {\n try {\n if (Date.now() - payload.timestamp * 1000 <= MAX_QUEUE_TIME) {\n if (i > 0) {\n await this.randomDelay();\n }\n await this.sendData(\"sync\", payload);\n i += 1;\n }\n } catch (error) {\n const handled = this.handleHubError(error);\n if (!handled) {\n this.logger.debug(\n \"Error while synchronizing data with Apitally Hub (will retry)\",\n { error },\n );\n this.syncDataQueue.push(payload);\n break;\n }\n }\n }\n }\n }\n\n private async sendLogData() {\n this.logger.debug(\"Sending request log data to Apitally Hub\");\n await this.requestLogger.rotateFile();\n\n const fetchWithRetry = fetchRetry(fetch, {\n retries: 3,\n retryDelay: 1000,\n retryOn: [408, 429, 500, 502, 503, 504],\n });\n\n let i = 0;\n let logFile;\n while ((logFile = this.requestLogger.getFile())) {\n if (i > 0) {\n await this.randomDelay();\n }\n\n try {\n const response = await fetchWithRetry(\n `${this.getHubUrlPrefix()}log?uuid=${logFile.uuid}`,\n {\n method: \"POST\",\n body: (await logFile.getContent()) as any,\n },\n );\n\n if (response.status === 402 && response.headers.has(\"Retry-After\")) {\n const retryAfter = parseInt(\n response.headers.get(\"Retry-After\") ?? \"0\",\n );\n if (retryAfter > 0) {\n this.requestLogger.suspendUntil = Date.now() + retryAfter * 1000;\n this.requestLogger.clear();\n return;\n }\n }\n\n if (!response.ok) {\n throw new HTTPError(response);\n }\n\n logFile.delete();\n } catch (error) {\n this.requestLogger.retryFileLater(logFile);\n break;\n }\n\n i++;\n if (i >= 10) break;\n }\n }\n\n private handleHubError(error: unknown) {\n if (error instanceof HTTPError) {\n if (error.response.status === 404) {\n this.logger.error(`Invalid Apitally client ID: '${this.clientId}'`);\n this.enabled = false;\n this.stopSync();\n return true;\n }\n if (error.response.status === 422) {\n this.logger.error(\"Received validation error from Apitally Hub\");\n return true;\n }\n }\n return false;\n }\n\n private async randomDelay() {\n const delay = 100 + Math.random() * 400;\n await new Promise((resolve) => setTimeout(resolve, delay));\n }\n}\n"],"mappings":"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","names":["fetchRetry","randomUUID","ConsumerRegistry","getOrCreateInstanceUuid","getLogger","isValidClientId","isValidEnv","RequestCounter","RequestLogger","getCpuMemoryUsage","ServerErrorCounter","ValidationErrorCounter","SYNC_INTERVAL","INITIAL_SYNC_INTERVAL","INITIAL_SYNC_INTERVAL_DURATION","MAX_QUEUE_TIME","HTTPError","Error","response","reason","status","ApitallyClient","clientId","env","instanceUuid","syncDataQueue","syncIntervalId","startupData","startupDataSent","enabled","requestCounter","requestLogger","validationErrorCounter","serverErrorCounter","consumerRegistry","logger","requestLogging","requestLoggingConfig","instance","error","console","warn","handleShutdown","bind","getInstance","isEnabled","shutdown","stopSync","sendSyncData","sendLogData","close","undefined","getHubUrlPrefix","baseURL","process","APITALLY_HUB_BASE_URL","version","sendData","url","payload","fetchWithRetry","fetch","retries","retryDelay","retryOn","method","body","JSON","stringify","headers","ok","startSync","sync","setInterval","setTimeout","clearInterval","promises","push","sendStartupData","Promise","all","setStartupData","data","debug","instance_uuid","message_uuid","handled","handleHubError","message","newPayload","timestamp","Date","now","requests","getAndResetRequests","validation_errors","getAndResetValidationErrors","server_errors","getAndResetServerErrors","consumers","getAndResetUpdatedConsumers","resources","i","length","shift","randomDelay","rotateFile","logFile","getFile","uuid","getContent","has","retryAfter","parseInt","get","suspendUntil","clear","delete","retryFileLater","delay","Math","random","resolve"]}
1
+ {"version":3,"sources":["../../src/common/client.ts"],"sourcesContent":["import fetchRetry from \"fetch-retry\";\nimport { randomUUID } from \"node:crypto\";\n\nimport ConsumerRegistry from \"./consumerRegistry.js\";\nimport { getOrCreateInstanceUuid } from \"./instance.js\";\nimport { Logger, getLogger } from \"./logging.js\";\nimport { isValidClientId, isValidEnv } from \"./paramValidation.js\";\nimport RequestCounter from \"./requestCounter.js\";\nimport RequestLogger from \"./requestLogger.js\";\nimport { getCpuMemoryUsage } from \"./resources.js\";\nimport ServerErrorCounter from \"./serverErrorCounter.js\";\nimport SpanCollector from \"./spanCollector.js\";\nimport {\n ApitallyConfig,\n StartupData,\n StartupPayload,\n SyncPayload,\n} from \"./types.js\";\nimport ValidationErrorCounter from \"./validationErrorCounter.js\";\n\nconst SYNC_INTERVAL = 60000; // 60 seconds\nconst INITIAL_SYNC_INTERVAL = 10000; // 10 seconds\nconst INITIAL_SYNC_INTERVAL_DURATION = 3600000; // 1 hour\nconst MAX_QUEUE_TIME = 3.6e6; // 1 hour\n\nclass HTTPError extends Error {\n public response: Response;\n\n constructor(response: Response) {\n const reason = response.status\n ? `status code ${response.status}`\n : \"an unknown error\";\n super(`Request failed with ${reason}`);\n this.response = response;\n }\n}\n\nexport class ApitallyClient {\n private clientId: string;\n private env: string;\n\n private static instance?: ApitallyClient;\n private instanceUuid: string;\n private syncDataQueue: SyncPayload[];\n private syncIntervalId?: NodeJS.Timeout;\n public startupData?: StartupData;\n private startupDataSent: boolean = false;\n private enabled: boolean = true;\n\n public requestCounter: RequestCounter;\n public requestLogger: RequestLogger;\n public spanCollector: SpanCollector;\n public validationErrorCounter: ValidationErrorCounter;\n public serverErrorCounter: ServerErrorCounter;\n public consumerRegistry: ConsumerRegistry;\n public logger: Logger;\n\n constructor({\n clientId,\n env = \"dev\",\n requestLogging,\n requestLoggingConfig,\n logger,\n }: ApitallyConfig) {\n if (ApitallyClient.instance) {\n throw new Error(\"Apitally client is already initialized\");\n }\n\n this.logger = logger ?? getLogger();\n\n if (!isValidClientId(clientId)) {\n this.logger.error(\n `Invalid Apitally client ID '${clientId}' (expecting hexadecimal UUID format)`,\n );\n this.enabled = false;\n }\n if (!isValidEnv(env)) {\n this.logger.error(\n `Invalid Apitally env '${env}' (expecting 1-32 alphanumeric characters and hyphens only)`,\n );\n this.enabled = false;\n }\n if (requestLoggingConfig && !requestLogging) {\n console.warn(\n \"requestLoggingConfig is deprecated, use requestLogging instead.\",\n );\n }\n\n ApitallyClient.instance = this;\n this.clientId = clientId;\n this.env = env;\n this.instanceUuid = getOrCreateInstanceUuid(clientId, env);\n this.syncDataQueue = [];\n this.requestCounter = new RequestCounter();\n this.requestLogger = new RequestLogger(\n requestLogging ?? requestLoggingConfig,\n );\n this.spanCollector = new SpanCollector(\n this.enabled &&\n this.requestLogger.enabled &&\n this.requestLogger.config.captureTraces,\n this.logger,\n );\n this.validationErrorCounter = new ValidationErrorCounter();\n this.serverErrorCounter = new ServerErrorCounter();\n this.consumerRegistry = new ConsumerRegistry();\n this.handleShutdown = this.handleShutdown.bind(this);\n }\n\n public static getInstance() {\n if (!ApitallyClient.instance) {\n throw new Error(\"Apitally client is not initialized\");\n }\n return ApitallyClient.instance;\n }\n\n public isEnabled() {\n return this.enabled;\n }\n\n public static async shutdown() {\n if (ApitallyClient.instance) {\n await ApitallyClient.instance.handleShutdown();\n }\n }\n\n public async handleShutdown() {\n this.enabled = false;\n this.stopSync();\n await this.sendSyncData();\n await this.sendLogData();\n await this.spanCollector.shutdown();\n await this.requestLogger.close();\n ApitallyClient.instance = undefined;\n }\n\n private getHubUrlPrefix() {\n const baseURL =\n process.env.APITALLY_HUB_BASE_URL || \"https://hub.apitally.io\";\n const version = \"v2\";\n return `${baseURL}/${version}/${this.clientId}/${this.env}/`;\n }\n\n private async sendData(url: string, payload: any) {\n const fetchWithRetry = fetchRetry(fetch, {\n retries: 3,\n retryDelay: 1000,\n retryOn: [408, 429, 500, 502, 503, 504],\n });\n const response = await fetchWithRetry(this.getHubUrlPrefix() + url, {\n method: \"POST\",\n body: JSON.stringify(payload),\n headers: { \"Content-Type\": \"application/json\" },\n });\n if (!response.ok) {\n throw new HTTPError(response);\n }\n }\n\n public startSync() {\n if (!this.enabled) {\n return;\n }\n this.sync();\n this.syncIntervalId = setInterval(() => {\n this.sync();\n }, INITIAL_SYNC_INTERVAL);\n setTimeout(() => {\n if (this.syncIntervalId) {\n clearInterval(this.syncIntervalId);\n this.syncIntervalId = setInterval(() => {\n this.sync();\n }, SYNC_INTERVAL);\n }\n }, INITIAL_SYNC_INTERVAL_DURATION);\n }\n\n private async sync() {\n try {\n const promises = [this.sendSyncData(), this.sendLogData()];\n if (!this.startupDataSent) {\n promises.push(this.sendStartupData());\n }\n await Promise.all(promises);\n } catch (error) {\n this.logger.error(\"Error while syncing with Apitally Hub\", {\n error,\n });\n }\n }\n\n private stopSync() {\n if (this.syncIntervalId) {\n clearInterval(this.syncIntervalId);\n this.syncIntervalId = undefined;\n }\n }\n\n public setStartupData(data: StartupData) {\n this.startupData = data;\n this.startupDataSent = false;\n }\n\n private async sendStartupData() {\n if (this.startupData) {\n this.logger.debug(\"Sending startup data to Apitally Hub\");\n const payload: StartupPayload = {\n instance_uuid: this.instanceUuid,\n message_uuid: randomUUID(),\n ...this.startupData,\n };\n try {\n await this.sendData(\"startup\", payload);\n this.startupDataSent = true;\n } catch (error) {\n const handled = this.handleHubError(error);\n if (!handled) {\n this.logger.error((error as Error).message);\n this.logger.debug(\n \"Error while sending startup data to Apitally Hub (will retry)\",\n { error },\n );\n }\n }\n }\n }\n\n private async sendSyncData() {\n this.logger.debug(\"Synchronizing data with Apitally Hub\");\n const newPayload: SyncPayload = {\n timestamp: Date.now() / 1000,\n instance_uuid: this.instanceUuid,\n message_uuid: randomUUID(),\n requests: this.requestCounter.getAndResetRequests(),\n validation_errors:\n this.validationErrorCounter.getAndResetValidationErrors(),\n server_errors: this.serverErrorCounter.getAndResetServerErrors(),\n consumers: this.consumerRegistry.getAndResetUpdatedConsumers(),\n resources: getCpuMemoryUsage(),\n };\n this.syncDataQueue.push(newPayload);\n\n let i = 0;\n while (this.syncDataQueue.length > 0) {\n const payload = this.syncDataQueue.shift();\n if (payload) {\n try {\n if (Date.now() - payload.timestamp * 1000 <= MAX_QUEUE_TIME) {\n if (i > 0) {\n await this.randomDelay();\n }\n await this.sendData(\"sync\", payload);\n i += 1;\n }\n } catch (error) {\n const handled = this.handleHubError(error);\n if (!handled) {\n this.logger.debug(\n \"Error while synchronizing data with Apitally Hub (will retry)\",\n { error },\n );\n this.syncDataQueue.push(payload);\n break;\n }\n }\n }\n }\n }\n\n private async sendLogData() {\n this.logger.debug(\"Sending request log data to Apitally Hub\");\n await this.requestLogger.rotateFile();\n\n const fetchWithRetry = fetchRetry(fetch, {\n retries: 3,\n retryDelay: 1000,\n retryOn: [408, 429, 500, 502, 503, 504],\n });\n\n let i = 0;\n let logFile;\n while ((logFile = this.requestLogger.getFile())) {\n if (i > 0) {\n await this.randomDelay();\n }\n\n try {\n const response = await fetchWithRetry(\n `${this.getHubUrlPrefix()}log?uuid=${logFile.uuid}`,\n {\n method: \"POST\",\n body: (await logFile.getContent()) as any,\n },\n );\n\n if (response.status === 402 && response.headers.has(\"Retry-After\")) {\n const retryAfter = parseInt(\n response.headers.get(\"Retry-After\") ?? \"0\",\n );\n if (retryAfter > 0) {\n this.requestLogger.suspendUntil = Date.now() + retryAfter * 1000;\n this.requestLogger.clear();\n return;\n }\n }\n\n if (!response.ok) {\n throw new HTTPError(response);\n }\n\n logFile.delete();\n } catch (error) {\n this.requestLogger.retryFileLater(logFile);\n break;\n }\n\n i++;\n if (i >= 10) break;\n }\n }\n\n private handleHubError(error: unknown) {\n if (error instanceof HTTPError) {\n if (error.response.status === 404) {\n this.logger.error(`Invalid Apitally client ID: '${this.clientId}'`);\n this.enabled = false;\n this.stopSync();\n return true;\n }\n if (error.response.status === 422) {\n this.logger.error(\"Received validation error from Apitally Hub\");\n return true;\n }\n }\n return false;\n }\n\n private async randomDelay() {\n const delay = 100 + Math.random() * 400;\n await new Promise((resolve) => setTimeout(resolve, delay));\n }\n}\n"],"mappings":"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","names":["fetchRetry","randomUUID","ConsumerRegistry","getOrCreateInstanceUuid","getLogger","isValidClientId","isValidEnv","RequestCounter","RequestLogger","getCpuMemoryUsage","ServerErrorCounter","SpanCollector","ValidationErrorCounter","SYNC_INTERVAL","INITIAL_SYNC_INTERVAL","INITIAL_SYNC_INTERVAL_DURATION","MAX_QUEUE_TIME","HTTPError","Error","response","reason","status","ApitallyClient","clientId","env","instanceUuid","syncDataQueue","syncIntervalId","startupData","startupDataSent","enabled","requestCounter","requestLogger","spanCollector","validationErrorCounter","serverErrorCounter","consumerRegistry","logger","requestLogging","requestLoggingConfig","instance","error","console","warn","config","captureTraces","handleShutdown","bind","getInstance","isEnabled","shutdown","stopSync","sendSyncData","sendLogData","close","undefined","getHubUrlPrefix","baseURL","process","APITALLY_HUB_BASE_URL","version","sendData","url","payload","fetchWithRetry","fetch","retries","retryDelay","retryOn","method","body","JSON","stringify","headers","ok","startSync","sync","setInterval","setTimeout","clearInterval","promises","push","sendStartupData","Promise","all","setStartupData","data","debug","instance_uuid","message_uuid","handled","handleHubError","message","newPayload","timestamp","Date","now","requests","getAndResetRequests","validation_errors","getAndResetValidationErrors","server_errors","getAndResetServerErrors","consumers","getAndResetUpdatedConsumers","resources","i","length","shift","randomDelay","rotateFile","logFile","getFile","uuid","getContent","has","retryAfter","parseInt","get","suspendUntil","clear","delete","retryFileLater","delay","Math","random","resolve"]}
@@ -4,6 +4,8 @@ import 'winston';
4
4
  import './requestLogger.cjs';
5
5
  import 'node:buffer';
6
6
  import 'node:http';
7
+ import './spanCollector.cjs';
8
+ import '@opentelemetry/sdk-trace-base';
7
9
  import './tempGzipFile.cjs';
8
10
 
9
11
  declare const consumerFromStringOrObject: (consumer: ApitallyConsumer | string) => ApitallyConsumer | null;
@@ -4,6 +4,8 @@ import 'winston';
4
4
  import './requestLogger.js';
5
5
  import 'node:buffer';
6
6
  import 'node:http';
7
+ import './spanCollector.js';
8
+ import '@opentelemetry/sdk-trace-base';
7
9
  import './tempGzipFile.js';
8
10
 
9
11
  declare const consumerFromStringOrObject: (consumer: ApitallyConsumer | string) => ApitallyConsumer | null;
@@ -4,6 +4,8 @@ import 'winston';
4
4
  import './requestLogger.cjs';
5
5
  import 'node:buffer';
6
6
  import 'node:http';
7
+ import './spanCollector.cjs';
8
+ import '@opentelemetry/sdk-trace-base';
7
9
  import './tempGzipFile.cjs';
8
10
 
9
11
  declare class RequestCounter {
@@ -4,6 +4,8 @@ import 'winston';
4
4
  import './requestLogger.js';
5
5
  import 'node:buffer';
6
6
  import 'node:http';
7
+ import './spanCollector.js';
8
+ import '@opentelemetry/sdk-trace-base';
7
9
  import './tempGzipFile.js';
8
10
 
9
11
  declare class RequestCounter {
@@ -37,9 +37,9 @@ module.exports = __toCommonJS(requestLogger_exports);
37
37
  var import_async_lock = __toESM(require("async-lock"), 1);
38
38
  var import_node_buffer = require("node:buffer");
39
39
  var import_node_crypto = require("node:crypto");
40
- var import_sentry = require("./sentry.js");
41
- var import_serverErrorCounter = require("./serverErrorCounter.js");
42
- var import_tempGzipFile = __toESM(require("./tempGzipFile.js"), 1);
40
+ var import_sentry = require('./sentry.cjs');
41
+ var import_serverErrorCounter = require('./serverErrorCounter.cjs');
42
+ var import_tempGzipFile = __toESM(require('./tempGzipFile.cjs'), 1);
43
43
  const MAX_BODY_SIZE = 5e4;
44
44
  const MAX_FILE_SIZE = 1e6;
45
45
  const MAX_FILES = 50;
@@ -114,6 +114,7 @@ const DEFAULT_CONFIG = {
114
114
  logResponseBody: false,
115
115
  logException: true,
116
116
  captureLogs: false,
117
+ captureTraces: false,
117
118
  maskQueryParams: [],
118
119
  maskHeaders: [],
119
120
  maskBodyFields: [],
@@ -277,7 +278,7 @@ const _RequestLogger = class _RequestLogger {
277
278
  item.request.url = url.toString();
278
279
  return item;
279
280
  }
280
- logRequest(request, response, error, logs) {
281
+ logRequest(request, response, error, logs, spans, traceId) {
281
282
  var _a, _b, _c;
282
283
  if (!this.enabled || this.suspendUntil !== null) return;
283
284
  const url = new URL(request.url);
@@ -317,6 +318,12 @@ const _RequestLogger = class _RequestLogger {
317
318
  message: truncateLogMessage(log.message)
318
319
  }));
319
320
  }
321
+ if (spans && spans.length > 0) {
322
+ item.spans = spans;
323
+ }
324
+ if (traceId) {
325
+ item.traceId = traceId;
326
+ }
320
327
  this.pendingWrites.push(item);
321
328
  if (this.pendingWrites.length > MAX_PENDING_WRITES) {
322
329
  this.pendingWrites.shift();
@@ -339,7 +346,9 @@ const _RequestLogger = class _RequestLogger {
339
346
  request: skipEmptyValues(item.request),
340
347
  response: skipEmptyValues(item.response),
341
348
  exception: item.exception,
342
- logs: item.logs
349
+ logs: item.logs,
350
+ spans: item.spans,
351
+ traceId: item.traceId
343
352
  };
344
353
  [
345
354
  finalItem.request.body,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/common/requestLogger.ts"],"sourcesContent":["import AsyncLock from \"async-lock\";\nimport { Buffer } from \"node:buffer\";\nimport { randomUUID } from \"node:crypto\";\nimport { IncomingHttpHeaders, OutgoingHttpHeaders } from \"node:http\";\n\nimport { getSentryEventId } from \"./sentry.js\";\nimport {\n truncateExceptionMessage,\n truncateExceptionStackTrace,\n} from \"./serverErrorCounter.js\";\nimport TempGzipFile, { checkWritableFs } from \"./tempGzipFile.js\";\n\nconst MAX_BODY_SIZE = 50_000; // 50 KB (uncompressed)\nconst MAX_FILE_SIZE = 1_000_000; // 1 MB (compressed)\nconst MAX_FILES = 50;\nconst MAX_PENDING_WRITES = 100;\nconst MAX_LOG_MSG_LENGTH = 2048;\nconst BODY_TOO_LARGE = Buffer.from(\"<body too large>\");\nconst BODY_MASKED = Buffer.from(\"<masked>\");\nconst MASKED = \"******\";\nconst ALLOWED_CONTENT_TYPES = [\n \"application/json\",\n \"application/ld+json\",\n \"application/problem+json\",\n \"application/vnd.api+json\",\n \"application/x-ndjson\",\n \"text/plain\",\n \"text/html\",\n];\nconst EXCLUDE_PATH_PATTERNS = [\n /\\/_?healthz?$/i,\n /\\/_?health[_-]?checks?$/i,\n /\\/_?heart[_-]?beats?$/i,\n /\\/ping$/i,\n /\\/ready$/i,\n /\\/live$/i,\n /\\/favicon(?:-[\\w-]+)?\\.(ico|png|svg)$/,\n /\\/apple-touch-icon(?:-[\\w-]+)?\\.png$/,\n /\\/robots\\.txt$/,\n /\\/sitemap\\.xml$/,\n /\\/manifest\\.json$/,\n /\\/site\\.webmanifest$/,\n /\\/service-worker\\.js$/,\n /\\/sw\\.js$/,\n /\\/\\.well-known\\//,\n];\nconst EXCLUDE_USER_AGENT_PATTERNS = [\n /health[-_ ]?check/i,\n /microsoft-azure-application-lb/i,\n /googlehc/i,\n /kube-probe/i,\n];\nconst MASK_QUERY_PARAM_PATTERNS = [\n /auth/i,\n /api-?key/i,\n /secret/i,\n /token/i,\n /password/i,\n /pwd/i,\n];\nconst MASK_HEADER_PATTERNS = [\n /auth/i,\n /api-?key/i,\n /secret/i,\n /token/i,\n /cookie/i,\n];\nconst MASK_BODY_FIELD_PATTERNS = [\n /password/i,\n /pwd/i,\n /token/i,\n /secret/i,\n /auth/i,\n /card[-_ ]?number/i,\n /ccv/i,\n /ssn/i,\n];\n\nexport type Request = {\n timestamp: number;\n method: string;\n path?: string;\n url: string;\n headers: [string, string][];\n size?: number;\n consumer?: string;\n body?: Buffer;\n};\n\nexport type Response = {\n statusCode: number;\n responseTime: number;\n headers: [string, string][];\n size?: number;\n body?: Buffer;\n};\n\nexport type LogRecord = {\n timestamp: number;\n logger?: string;\n level: string;\n message: string;\n};\n\nexport type RequestLoggingConfig = {\n enabled: boolean;\n logQueryParams: boolean;\n logRequestHeaders: boolean;\n logRequestBody: boolean;\n logResponseHeaders: boolean;\n logResponseBody: boolean;\n logException: boolean;\n captureLogs: boolean;\n maskQueryParams: RegExp[];\n maskHeaders: RegExp[];\n maskBodyFields: RegExp[];\n maskRequestBodyCallback?: (request: Request) => Buffer | null | undefined;\n maskResponseBodyCallback?: (\n request: Request,\n response: Response,\n ) => Buffer | null | undefined;\n excludePaths: RegExp[];\n excludeCallback?: (request: Request, response: Response) => boolean;\n};\n\nconst DEFAULT_CONFIG: RequestLoggingConfig = {\n enabled: false,\n logQueryParams: true,\n logRequestHeaders: false,\n logRequestBody: false,\n logResponseHeaders: true,\n logResponseBody: false,\n logException: true,\n captureLogs: false,\n maskQueryParams: [],\n maskHeaders: [],\n maskBodyFields: [],\n excludePaths: [],\n};\n\ntype RequestLogItem = {\n uuid: string;\n request: Request;\n response: Response;\n exception?: {\n type: string;\n message: string;\n stacktrace: string;\n sentryEventId?: string;\n };\n logs?: LogRecord[];\n};\n\nexport default class RequestLogger {\n public config: RequestLoggingConfig;\n public enabled: boolean;\n public suspendUntil: number | null = null;\n private pendingWrites: RequestLogItem[] = [];\n private currentFile: TempGzipFile | null = null;\n private files: TempGzipFile[] = [];\n private maintainIntervalId?: NodeJS.Timeout;\n private lock = new AsyncLock();\n\n constructor(config?: Partial<RequestLoggingConfig>) {\n this.config = { ...DEFAULT_CONFIG, ...config };\n this.enabled = this.config.enabled && checkWritableFs();\n\n if (this.enabled) {\n this.maintainIntervalId = setInterval(() => {\n this.maintain();\n }, 1000);\n }\n }\n\n get maxBodySize() {\n return MAX_BODY_SIZE;\n }\n\n private shouldExcludePath(urlPath: string) {\n const patterns = [...this.config.excludePaths, ...EXCLUDE_PATH_PATTERNS];\n return matchPatterns(urlPath, patterns);\n }\n\n private shouldExcludeUserAgent(userAgent?: string) {\n return userAgent\n ? matchPatterns(userAgent, EXCLUDE_USER_AGENT_PATTERNS)\n : false;\n }\n\n private shouldMaskQueryParam(name: string) {\n const patterns = [\n ...this.config.maskQueryParams,\n ...MASK_QUERY_PARAM_PATTERNS,\n ];\n return matchPatterns(name, patterns);\n }\n\n private shouldMaskHeader(name: string) {\n const patterns = [...this.config.maskHeaders, ...MASK_HEADER_PATTERNS];\n return matchPatterns(name, patterns);\n }\n\n private shouldMaskBodyField(name: string) {\n const patterns = [\n ...this.config.maskBodyFields,\n ...MASK_BODY_FIELD_PATTERNS,\n ];\n return matchPatterns(name, patterns);\n }\n\n private getContentType(headers: [string, string][]) {\n return headers.find(([k]) => k.toLowerCase() === \"content-type\")?.[1];\n }\n\n private hasSupportedContentType(headers: [string, string][]) {\n const contentType = this.getContentType(headers);\n return this.isSupportedContentType(contentType);\n }\n\n public isSupportedContentType(contentType?: string | null) {\n return (\n typeof contentType === \"string\" &&\n ALLOWED_CONTENT_TYPES.some((t) => contentType.startsWith(t))\n );\n }\n\n private maskQueryParams(search: string) {\n const params = new URLSearchParams(search);\n for (const [key] of params) {\n if (this.shouldMaskQueryParam(key)) {\n params.set(key, MASKED);\n }\n }\n return params.toString();\n }\n\n private maskHeaders(headers: [string, string][]): [string, string][] {\n return headers.map(([k, v]) => [k, this.shouldMaskHeader(k) ? MASKED : v]);\n }\n\n private maskBody(data: any): any {\n if (typeof data === \"object\" && data !== null && !Array.isArray(data)) {\n const result: any = {};\n for (const [key, value] of Object.entries(data)) {\n if (typeof value === \"string\" && this.shouldMaskBodyField(key)) {\n result[key] = MASKED;\n } else {\n result[key] = this.maskBody(value);\n }\n }\n return result;\n }\n if (Array.isArray(data)) {\n return data.map((item) => this.maskBody(item));\n }\n return data;\n }\n\n private applyMasking(item: RequestLogItem) {\n // Apply user-provided maskRequestBodyCallback function\n if (\n this.config.maskRequestBodyCallback &&\n item.request.body &&\n item.request.body !== BODY_TOO_LARGE\n ) {\n try {\n const maskedBody = this.config.maskRequestBodyCallback(item.request);\n item.request.body = maskedBody ?? BODY_MASKED;\n } catch {\n item.request.body = undefined;\n }\n }\n\n // Apply user-provided maskResponseBodyCallback function\n if (\n this.config.maskResponseBodyCallback &&\n item.response.body &&\n item.response.body !== BODY_TOO_LARGE\n ) {\n try {\n const maskedBody = this.config.maskResponseBodyCallback(\n item.request,\n item.response,\n );\n item.response.body = maskedBody ?? BODY_MASKED;\n } catch {\n item.response.body = undefined;\n }\n }\n\n // Check request and response body sizes\n if (item.request.body && item.request.body.length > MAX_BODY_SIZE) {\n item.request.body = BODY_TOO_LARGE;\n }\n if (item.response.body && item.response.body.length > MAX_BODY_SIZE) {\n item.response.body = BODY_TOO_LARGE;\n }\n\n // Mask request and response body fields\n for (const key of [\"request\", \"response\"] as const) {\n const bodyData = item[key].body;\n if (\n !bodyData ||\n bodyData === BODY_TOO_LARGE ||\n bodyData === BODY_MASKED\n ) {\n continue;\n }\n\n try {\n const contentType = this.getContentType(item[key].headers);\n if (!contentType || /\\bjson\\b/i.test(contentType)) {\n const parsedBody = JSON.parse(bodyData.toString());\n const maskedBody = this.maskBody(parsedBody);\n item[key].body = Buffer.from(JSON.stringify(maskedBody));\n } else if (/\\bndjson\\b/i.test(contentType)) {\n const lines = bodyData\n .toString()\n .split(\"\\n\")\n .filter((line) => line.trim());\n const maskedLines = lines.map((line) => {\n try {\n const parsed = JSON.parse(line);\n const masked = this.maskBody(parsed);\n return JSON.stringify(masked);\n } catch {\n return line; // Keep unparseable lines as is\n }\n });\n item[key].body = Buffer.from(maskedLines.join(\"\\n\"));\n }\n } catch {\n // If parsing fails, leave body as is\n }\n }\n\n // Mask request and response headers\n item.request.headers = this.config.logRequestHeaders\n ? this.maskHeaders(item.request.headers)\n : [];\n item.response.headers = this.config.logResponseHeaders\n ? this.maskHeaders(item.response.headers)\n : [];\n\n // Mask query params\n const url = new URL(item.request.url);\n url.search = this.config.logQueryParams\n ? this.maskQueryParams(url.search)\n : \"\";\n item.request.url = url.toString();\n\n return item;\n }\n\n logRequest(\n request: Request,\n response: Response,\n error?: Error,\n logs?: LogRecord[],\n ) {\n if (!this.enabled || this.suspendUntil !== null) return;\n\n const url = new URL(request.url);\n const path = request.path ?? url.pathname;\n const userAgent = request.headers.find(\n ([k]) => k.toLowerCase() === \"user-agent\",\n )?.[1];\n\n if (\n this.shouldExcludePath(path) ||\n this.shouldExcludeUserAgent(userAgent) ||\n (this.config.excludeCallback?.(request, response) ?? false)\n ) {\n return;\n }\n\n if (\n !this.config.logRequestBody ||\n !this.hasSupportedContentType(request.headers)\n ) {\n request.body = undefined;\n }\n if (\n !this.config.logResponseBody ||\n !this.hasSupportedContentType(response.headers)\n ) {\n response.body = undefined;\n }\n\n if (request.size !== undefined && request.size < 0) {\n request.size = undefined;\n }\n if (response.size !== undefined && response.size < 0) {\n response.size = undefined;\n }\n\n const item: RequestLogItem = {\n uuid: randomUUID(),\n request: request,\n response: response,\n exception:\n error && this.config.logException\n ? {\n type: error.name,\n message: truncateExceptionMessage(error.message),\n stacktrace: truncateExceptionStackTrace(error.stack || \"\"),\n sentryEventId: getSentryEventId(),\n }\n : undefined,\n };\n\n if (logs && logs.length > 0) {\n item.logs = logs.map((log) => ({\n timestamp: log.timestamp,\n logger: log.logger,\n level: log.level,\n message: truncateLogMessage(log.message),\n }));\n }\n this.pendingWrites.push(item);\n\n if (this.pendingWrites.length > MAX_PENDING_WRITES) {\n this.pendingWrites.shift();\n }\n }\n\n async writeToFile() {\n if (!this.enabled || this.pendingWrites.length === 0) {\n return;\n }\n return this.lock.acquire(\"file\", async () => {\n if (!this.currentFile) {\n this.currentFile = new TempGzipFile(\"request_logs\");\n }\n while (this.pendingWrites.length > 0) {\n let item = this.pendingWrites.shift();\n if (item) {\n item = this.applyMasking(item);\n\n const finalItem = {\n uuid: item.uuid,\n request: skipEmptyValues(item.request),\n response: skipEmptyValues(item.response),\n exception: item.exception,\n logs: item.logs,\n };\n\n // Set up body serialization for JSON\n [finalItem.request.body, finalItem.response.body].forEach((body) => {\n if (body) {\n // @ts-expect-error Override Buffer's default JSON serialization\n body.toJSON = function () {\n return this.toString(\"base64\");\n };\n }\n });\n\n await this.currentFile.writeLine(\n Buffer.from(JSON.stringify(finalItem)),\n );\n }\n }\n });\n }\n\n getFile() {\n return this.files.shift();\n }\n\n retryFileLater(file: TempGzipFile) {\n this.files.unshift(file);\n }\n\n async rotateFile() {\n return this.lock.acquire(\"file\", async () => {\n if (this.currentFile) {\n await this.currentFile.close();\n this.files.push(this.currentFile);\n this.currentFile = null;\n }\n });\n }\n\n async maintain() {\n await this.writeToFile();\n if (this.currentFile && this.currentFile.size > MAX_FILE_SIZE) {\n await this.rotateFile();\n }\n while (this.files.length > MAX_FILES) {\n const file = this.files.shift();\n file?.delete();\n }\n if (this.suspendUntil !== null && this.suspendUntil < Date.now()) {\n this.suspendUntil = null;\n }\n }\n\n async clear() {\n this.pendingWrites = [];\n await this.rotateFile();\n this.files.forEach((file) => {\n file.delete();\n });\n this.files = [];\n }\n\n async close() {\n this.enabled = false;\n await this.clear();\n if (this.maintainIntervalId) {\n clearInterval(this.maintainIntervalId);\n }\n }\n}\n\nexport function convertHeaders(\n headers:\n | Headers\n | IncomingHttpHeaders\n | OutgoingHttpHeaders\n | Record<string, string | string[] | number | undefined>\n | undefined,\n) {\n if (!headers) {\n return [];\n }\n if (headers instanceof Headers) {\n return Array.from(headers.entries());\n }\n return Object.entries(headers).flatMap(([key, value]) => {\n if (value === undefined) {\n return [];\n }\n if (Array.isArray(value)) {\n return value.map((v) => [key, v]);\n }\n return [[key, value.toString()]];\n }) as [string, string][];\n}\n\nexport function convertBody(body: any, contentType?: string | null) {\n if (!body || !contentType) {\n return;\n }\n try {\n if (contentType.startsWith(\"application/json\")) {\n if (isValidJsonString(body)) {\n return Buffer.from(body);\n } else {\n return Buffer.from(JSON.stringify(body));\n }\n }\n if (contentType.startsWith(\"text/\") && typeof body === \"string\") {\n return Buffer.from(body);\n }\n } catch (error) {\n return;\n }\n}\n\nfunction isValidJsonString(body: any) {\n if (typeof body !== \"string\") {\n return false;\n }\n try {\n JSON.parse(body);\n return true;\n } catch {\n return false;\n }\n}\n\nfunction matchPatterns(value: string, patterns: RegExp[]) {\n return patterns.some((pattern) => {\n return pattern.test(value);\n });\n}\n\nfunction skipEmptyValues<T extends Record<string, any>>(data: T) {\n return Object.fromEntries(\n Object.entries(data).filter(([_, v]) => {\n if (v == null || Number.isNaN(v)) return false;\n if (Array.isArray(v) || Buffer.isBuffer(v) || typeof v === \"string\") {\n return v.length > 0;\n }\n return true;\n }),\n ) as Partial<T>;\n}\n\nfunction truncateLogMessage(msg: string) {\n if (msg.length > MAX_LOG_MSG_LENGTH) {\n const suffix = \"... (truncated)\";\n return msg.slice(0, MAX_LOG_MSG_LENGTH - suffix.length) + suffix;\n }\n return msg;\n}\n"],"mappings":"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","names":["MAX_BODY_SIZE","MAX_FILE_SIZE","MAX_FILES","MAX_PENDING_WRITES","MAX_LOG_MSG_LENGTH","BODY_TOO_LARGE","Buffer","from","BODY_MASKED","MASKED","ALLOWED_CONTENT_TYPES","EXCLUDE_PATH_PATTERNS","EXCLUDE_USER_AGENT_PATTERNS","MASK_QUERY_PARAM_PATTERNS","MASK_HEADER_PATTERNS","MASK_BODY_FIELD_PATTERNS","DEFAULT_CONFIG","enabled","logQueryParams","logRequestHeaders","logRequestBody","logResponseHeaders","logResponseBody","logException","captureLogs","maskQueryParams","maskHeaders","maskBodyFields","excludePaths","RequestLogger","config","suspendUntil","pendingWrites","currentFile","files","maintainIntervalId","lock","AsyncLock","checkWritableFs","setInterval","maintain","maxBodySize","shouldExcludePath","urlPath","patterns","matchPatterns","shouldExcludeUserAgent","userAgent","shouldMaskQueryParam","name","shouldMaskHeader","shouldMaskBodyField","getContentType","headers","find","k","toLowerCase","hasSupportedContentType","contentType","isSupportedContentType","some","t","startsWith","search","params","URLSearchParams","key","set","toString","map","v","maskBody","data","Array","isArray","result","value","Object","entries","item","applyMasking","maskRequestBodyCallback","request","body","maskedBody","undefined","maskResponseBodyCallback","response","length","bodyData","test","parsedBody","JSON","parse","stringify","lines","split","filter","line","trim","maskedLines","parsed","masked","join","url","URL","logRequest","error","logs","path","pathname","excludeCallback","size","uuid","randomUUID","exception","type","message","truncateExceptionMessage","stacktrace","truncateExceptionStackTrace","stack","sentryEventId","getSentryEventId","log","timestamp","logger","level","truncateLogMessage","push","shift","writeToFile","acquire","TempGzipFile","finalItem","skipEmptyValues","forEach","toJSON","writeLine","getFile","retryFileLater","file","unshift","rotateFile","close","delete","Date","now","clear","clearInterval","convertHeaders","Headers","flatMap","convertBody","isValidJsonString","pattern","fromEntries","_","Number","isNaN","isBuffer","msg","suffix","slice"]}
1
+ {"version":3,"sources":["../../src/common/requestLogger.ts"],"sourcesContent":["import AsyncLock from \"async-lock\";\nimport { Buffer } from \"node:buffer\";\nimport { randomUUID } from \"node:crypto\";\nimport type { IncomingHttpHeaders, OutgoingHttpHeaders } from \"node:http\";\n\nimport { getSentryEventId } from \"./sentry.js\";\nimport {\n truncateExceptionMessage,\n truncateExceptionStackTrace,\n} from \"./serverErrorCounter.js\";\nimport type { SpanData } from \"./spanCollector.js\";\nimport TempGzipFile, { checkWritableFs } from \"./tempGzipFile.js\";\n\nconst MAX_BODY_SIZE = 50_000; // 50 KB (uncompressed)\nconst MAX_FILE_SIZE = 1_000_000; // 1 MB (compressed)\nconst MAX_FILES = 50;\nconst MAX_PENDING_WRITES = 100;\nconst MAX_LOG_MSG_LENGTH = 2048;\nconst BODY_TOO_LARGE = Buffer.from(\"<body too large>\");\nconst BODY_MASKED = Buffer.from(\"<masked>\");\nconst MASKED = \"******\";\nconst ALLOWED_CONTENT_TYPES = [\n \"application/json\",\n \"application/ld+json\",\n \"application/problem+json\",\n \"application/vnd.api+json\",\n \"application/x-ndjson\",\n \"text/plain\",\n \"text/html\",\n];\nconst EXCLUDE_PATH_PATTERNS = [\n /\\/_?healthz?$/i,\n /\\/_?health[_-]?checks?$/i,\n /\\/_?heart[_-]?beats?$/i,\n /\\/ping$/i,\n /\\/ready$/i,\n /\\/live$/i,\n /\\/favicon(?:-[\\w-]+)?\\.(ico|png|svg)$/,\n /\\/apple-touch-icon(?:-[\\w-]+)?\\.png$/,\n /\\/robots\\.txt$/,\n /\\/sitemap\\.xml$/,\n /\\/manifest\\.json$/,\n /\\/site\\.webmanifest$/,\n /\\/service-worker\\.js$/,\n /\\/sw\\.js$/,\n /\\/\\.well-known\\//,\n];\nconst EXCLUDE_USER_AGENT_PATTERNS = [\n /health[-_ ]?check/i,\n /microsoft-azure-application-lb/i,\n /googlehc/i,\n /kube-probe/i,\n];\nconst MASK_QUERY_PARAM_PATTERNS = [\n /auth/i,\n /api-?key/i,\n /secret/i,\n /token/i,\n /password/i,\n /pwd/i,\n];\nconst MASK_HEADER_PATTERNS = [\n /auth/i,\n /api-?key/i,\n /secret/i,\n /token/i,\n /cookie/i,\n];\nconst MASK_BODY_FIELD_PATTERNS = [\n /password/i,\n /pwd/i,\n /token/i,\n /secret/i,\n /auth/i,\n /card[-_ ]?number/i,\n /ccv/i,\n /ssn/i,\n];\n\nexport type Request = {\n timestamp: number;\n method: string;\n path?: string;\n url: string;\n headers: [string, string][];\n size?: number;\n consumer?: string;\n body?: Buffer;\n};\n\nexport type Response = {\n statusCode: number;\n responseTime: number;\n headers: [string, string][];\n size?: number;\n body?: Buffer;\n};\n\nexport type LogRecord = {\n timestamp: number;\n logger?: string;\n level: string;\n message: string;\n};\n\nexport type RequestLoggingConfig = {\n enabled: boolean;\n logQueryParams: boolean;\n logRequestHeaders: boolean;\n logRequestBody: boolean;\n logResponseHeaders: boolean;\n logResponseBody: boolean;\n logException: boolean;\n captureLogs: boolean;\n captureTraces: boolean;\n maskQueryParams: RegExp[];\n maskHeaders: RegExp[];\n maskBodyFields: RegExp[];\n maskRequestBodyCallback?: (request: Request) => Buffer | null | undefined;\n maskResponseBodyCallback?: (\n request: Request,\n response: Response,\n ) => Buffer | null | undefined;\n excludePaths: RegExp[];\n excludeCallback?: (request: Request, response: Response) => boolean;\n};\n\nconst DEFAULT_CONFIG: RequestLoggingConfig = {\n enabled: false,\n logQueryParams: true,\n logRequestHeaders: false,\n logRequestBody: false,\n logResponseHeaders: true,\n logResponseBody: false,\n logException: true,\n captureLogs: false,\n captureTraces: false,\n maskQueryParams: [],\n maskHeaders: [],\n maskBodyFields: [],\n excludePaths: [],\n};\n\ntype RequestLogItem = {\n uuid: string;\n request: Request;\n response: Response;\n exception?: {\n type: string;\n message: string;\n stacktrace: string;\n sentryEventId?: string;\n };\n logs?: LogRecord[];\n spans?: SpanData[];\n traceId?: string;\n};\n\nexport default class RequestLogger {\n public config: RequestLoggingConfig;\n public enabled: boolean;\n public suspendUntil: number | null = null;\n private pendingWrites: RequestLogItem[] = [];\n private currentFile: TempGzipFile | null = null;\n private files: TempGzipFile[] = [];\n private maintainIntervalId?: NodeJS.Timeout;\n private lock = new AsyncLock();\n\n constructor(config?: Partial<RequestLoggingConfig>) {\n this.config = { ...DEFAULT_CONFIG, ...config };\n this.enabled = this.config.enabled && checkWritableFs();\n\n if (this.enabled) {\n this.maintainIntervalId = setInterval(() => {\n this.maintain();\n }, 1000);\n }\n }\n\n get maxBodySize() {\n return MAX_BODY_SIZE;\n }\n\n private shouldExcludePath(urlPath: string) {\n const patterns = [...this.config.excludePaths, ...EXCLUDE_PATH_PATTERNS];\n return matchPatterns(urlPath, patterns);\n }\n\n private shouldExcludeUserAgent(userAgent?: string) {\n return userAgent\n ? matchPatterns(userAgent, EXCLUDE_USER_AGENT_PATTERNS)\n : false;\n }\n\n private shouldMaskQueryParam(name: string) {\n const patterns = [\n ...this.config.maskQueryParams,\n ...MASK_QUERY_PARAM_PATTERNS,\n ];\n return matchPatterns(name, patterns);\n }\n\n private shouldMaskHeader(name: string) {\n const patterns = [...this.config.maskHeaders, ...MASK_HEADER_PATTERNS];\n return matchPatterns(name, patterns);\n }\n\n private shouldMaskBodyField(name: string) {\n const patterns = [\n ...this.config.maskBodyFields,\n ...MASK_BODY_FIELD_PATTERNS,\n ];\n return matchPatterns(name, patterns);\n }\n\n private getContentType(headers: [string, string][]) {\n return headers.find(([k]) => k.toLowerCase() === \"content-type\")?.[1];\n }\n\n private hasSupportedContentType(headers: [string, string][]) {\n const contentType = this.getContentType(headers);\n return this.isSupportedContentType(contentType);\n }\n\n public isSupportedContentType(contentType?: string | null) {\n return (\n typeof contentType === \"string\" &&\n ALLOWED_CONTENT_TYPES.some((t) => contentType.startsWith(t))\n );\n }\n\n private maskQueryParams(search: string) {\n const params = new URLSearchParams(search);\n for (const [key] of params) {\n if (this.shouldMaskQueryParam(key)) {\n params.set(key, MASKED);\n }\n }\n return params.toString();\n }\n\n private maskHeaders(headers: [string, string][]): [string, string][] {\n return headers.map(([k, v]) => [k, this.shouldMaskHeader(k) ? MASKED : v]);\n }\n\n private maskBody(data: any): any {\n if (typeof data === \"object\" && data !== null && !Array.isArray(data)) {\n const result: any = {};\n for (const [key, value] of Object.entries(data)) {\n if (typeof value === \"string\" && this.shouldMaskBodyField(key)) {\n result[key] = MASKED;\n } else {\n result[key] = this.maskBody(value);\n }\n }\n return result;\n }\n if (Array.isArray(data)) {\n return data.map((item) => this.maskBody(item));\n }\n return data;\n }\n\n private applyMasking(item: RequestLogItem) {\n // Apply user-provided maskRequestBodyCallback function\n if (\n this.config.maskRequestBodyCallback &&\n item.request.body &&\n item.request.body !== BODY_TOO_LARGE\n ) {\n try {\n const maskedBody = this.config.maskRequestBodyCallback(item.request);\n item.request.body = maskedBody ?? BODY_MASKED;\n } catch {\n item.request.body = undefined;\n }\n }\n\n // Apply user-provided maskResponseBodyCallback function\n if (\n this.config.maskResponseBodyCallback &&\n item.response.body &&\n item.response.body !== BODY_TOO_LARGE\n ) {\n try {\n const maskedBody = this.config.maskResponseBodyCallback(\n item.request,\n item.response,\n );\n item.response.body = maskedBody ?? BODY_MASKED;\n } catch {\n item.response.body = undefined;\n }\n }\n\n // Check request and response body sizes\n if (item.request.body && item.request.body.length > MAX_BODY_SIZE) {\n item.request.body = BODY_TOO_LARGE;\n }\n if (item.response.body && item.response.body.length > MAX_BODY_SIZE) {\n item.response.body = BODY_TOO_LARGE;\n }\n\n // Mask request and response body fields\n for (const key of [\"request\", \"response\"] as const) {\n const bodyData = item[key].body;\n if (\n !bodyData ||\n bodyData === BODY_TOO_LARGE ||\n bodyData === BODY_MASKED\n ) {\n continue;\n }\n\n try {\n const contentType = this.getContentType(item[key].headers);\n if (!contentType || /\\bjson\\b/i.test(contentType)) {\n const parsedBody = JSON.parse(bodyData.toString());\n const maskedBody = this.maskBody(parsedBody);\n item[key].body = Buffer.from(JSON.stringify(maskedBody));\n } else if (/\\bndjson\\b/i.test(contentType)) {\n const lines = bodyData\n .toString()\n .split(\"\\n\")\n .filter((line) => line.trim());\n const maskedLines = lines.map((line) => {\n try {\n const parsed = JSON.parse(line);\n const masked = this.maskBody(parsed);\n return JSON.stringify(masked);\n } catch {\n return line; // Keep unparseable lines as is\n }\n });\n item[key].body = Buffer.from(maskedLines.join(\"\\n\"));\n }\n } catch {\n // If parsing fails, leave body as is\n }\n }\n\n // Mask request and response headers\n item.request.headers = this.config.logRequestHeaders\n ? this.maskHeaders(item.request.headers)\n : [];\n item.response.headers = this.config.logResponseHeaders\n ? this.maskHeaders(item.response.headers)\n : [];\n\n // Mask query params\n const url = new URL(item.request.url);\n url.search = this.config.logQueryParams\n ? this.maskQueryParams(url.search)\n : \"\";\n item.request.url = url.toString();\n\n return item;\n }\n\n logRequest(\n request: Request,\n response: Response,\n error?: Error,\n logs?: LogRecord[],\n spans?: SpanData[],\n traceId?: string,\n ) {\n if (!this.enabled || this.suspendUntil !== null) return;\n\n const url = new URL(request.url);\n const path = request.path ?? url.pathname;\n const userAgent = request.headers.find(\n ([k]) => k.toLowerCase() === \"user-agent\",\n )?.[1];\n\n if (\n this.shouldExcludePath(path) ||\n this.shouldExcludeUserAgent(userAgent) ||\n (this.config.excludeCallback?.(request, response) ?? false)\n ) {\n return;\n }\n\n if (\n !this.config.logRequestBody ||\n !this.hasSupportedContentType(request.headers)\n ) {\n request.body = undefined;\n }\n if (\n !this.config.logResponseBody ||\n !this.hasSupportedContentType(response.headers)\n ) {\n response.body = undefined;\n }\n\n if (request.size !== undefined && request.size < 0) {\n request.size = undefined;\n }\n if (response.size !== undefined && response.size < 0) {\n response.size = undefined;\n }\n\n const item: RequestLogItem = {\n uuid: randomUUID(),\n request: request,\n response: response,\n exception:\n error && this.config.logException\n ? {\n type: error.name,\n message: truncateExceptionMessage(error.message),\n stacktrace: truncateExceptionStackTrace(error.stack || \"\"),\n sentryEventId: getSentryEventId(),\n }\n : undefined,\n };\n\n if (logs && logs.length > 0) {\n item.logs = logs.map((log) => ({\n timestamp: log.timestamp,\n logger: log.logger,\n level: log.level,\n message: truncateLogMessage(log.message),\n }));\n }\n if (spans && spans.length > 0) {\n item.spans = spans;\n }\n if (traceId) {\n item.traceId = traceId;\n }\n this.pendingWrites.push(item);\n\n if (this.pendingWrites.length > MAX_PENDING_WRITES) {\n this.pendingWrites.shift();\n }\n }\n\n async writeToFile() {\n if (!this.enabled || this.pendingWrites.length === 0) {\n return;\n }\n return this.lock.acquire(\"file\", async () => {\n if (!this.currentFile) {\n this.currentFile = new TempGzipFile(\"request_logs\");\n }\n while (this.pendingWrites.length > 0) {\n let item = this.pendingWrites.shift();\n if (item) {\n item = this.applyMasking(item);\n\n const finalItem = {\n uuid: item.uuid,\n request: skipEmptyValues(item.request),\n response: skipEmptyValues(item.response),\n exception: item.exception,\n logs: item.logs,\n spans: item.spans,\n traceId: item.traceId,\n };\n\n // Set up body serialization for JSON\n [finalItem.request.body, finalItem.response.body].forEach((body) => {\n if (body) {\n // @ts-expect-error Override Buffer's default JSON serialization\n body.toJSON = function () {\n return this.toString(\"base64\");\n };\n }\n });\n\n await this.currentFile.writeLine(\n Buffer.from(JSON.stringify(finalItem)),\n );\n }\n }\n });\n }\n\n getFile() {\n return this.files.shift();\n }\n\n retryFileLater(file: TempGzipFile) {\n this.files.unshift(file);\n }\n\n async rotateFile() {\n return this.lock.acquire(\"file\", async () => {\n if (this.currentFile) {\n await this.currentFile.close();\n this.files.push(this.currentFile);\n this.currentFile = null;\n }\n });\n }\n\n async maintain() {\n await this.writeToFile();\n if (this.currentFile && this.currentFile.size > MAX_FILE_SIZE) {\n await this.rotateFile();\n }\n while (this.files.length > MAX_FILES) {\n const file = this.files.shift();\n file?.delete();\n }\n if (this.suspendUntil !== null && this.suspendUntil < Date.now()) {\n this.suspendUntil = null;\n }\n }\n\n async clear() {\n this.pendingWrites = [];\n await this.rotateFile();\n this.files.forEach((file) => {\n file.delete();\n });\n this.files = [];\n }\n\n async close() {\n this.enabled = false;\n await this.clear();\n if (this.maintainIntervalId) {\n clearInterval(this.maintainIntervalId);\n }\n }\n}\n\nexport function convertHeaders(\n headers:\n | Headers\n | IncomingHttpHeaders\n | OutgoingHttpHeaders\n | Record<string, string | string[] | number | undefined>\n | undefined,\n) {\n if (!headers) {\n return [];\n }\n if (headers instanceof Headers) {\n return Array.from(headers.entries());\n }\n return Object.entries(headers).flatMap(([key, value]) => {\n if (value === undefined) {\n return [];\n }\n if (Array.isArray(value)) {\n return value.map((v) => [key, v]);\n }\n return [[key, value.toString()]];\n }) as [string, string][];\n}\n\nexport function convertBody(body: any, contentType?: string | null) {\n if (!body || !contentType) {\n return;\n }\n try {\n if (contentType.startsWith(\"application/json\")) {\n if (isValidJsonString(body)) {\n return Buffer.from(body);\n } else {\n return Buffer.from(JSON.stringify(body));\n }\n }\n if (contentType.startsWith(\"text/\") && typeof body === \"string\") {\n return Buffer.from(body);\n }\n } catch (error) {\n return;\n }\n}\n\nfunction isValidJsonString(body: any) {\n if (typeof body !== \"string\") {\n return false;\n }\n try {\n JSON.parse(body);\n return true;\n } catch {\n return false;\n }\n}\n\nfunction matchPatterns(value: string, patterns: RegExp[]) {\n return patterns.some((pattern) => {\n return pattern.test(value);\n });\n}\n\nfunction skipEmptyValues<T extends Record<string, any>>(data: T) {\n return Object.fromEntries(\n Object.entries(data).filter(([_, v]) => {\n if (v == null || Number.isNaN(v)) return false;\n if (Array.isArray(v) || Buffer.isBuffer(v) || typeof v === \"string\") {\n return v.length > 0;\n }\n return true;\n }),\n ) as Partial<T>;\n}\n\nfunction truncateLogMessage(msg: string) {\n if (msg.length > MAX_LOG_MSG_LENGTH) {\n const suffix = \"... (truncated)\";\n return msg.slice(0, MAX_LOG_MSG_LENGTH - suffix.length) + suffix;\n }\n return msg;\n}\n"],"mappings":"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","names":["MAX_BODY_SIZE","MAX_FILE_SIZE","MAX_FILES","MAX_PENDING_WRITES","MAX_LOG_MSG_LENGTH","BODY_TOO_LARGE","Buffer","from","BODY_MASKED","MASKED","ALLOWED_CONTENT_TYPES","EXCLUDE_PATH_PATTERNS","EXCLUDE_USER_AGENT_PATTERNS","MASK_QUERY_PARAM_PATTERNS","MASK_HEADER_PATTERNS","MASK_BODY_FIELD_PATTERNS","DEFAULT_CONFIG","enabled","logQueryParams","logRequestHeaders","logRequestBody","logResponseHeaders","logResponseBody","logException","captureLogs","captureTraces","maskQueryParams","maskHeaders","maskBodyFields","excludePaths","RequestLogger","config","suspendUntil","pendingWrites","currentFile","files","maintainIntervalId","lock","AsyncLock","checkWritableFs","setInterval","maintain","maxBodySize","shouldExcludePath","urlPath","patterns","matchPatterns","shouldExcludeUserAgent","userAgent","shouldMaskQueryParam","name","shouldMaskHeader","shouldMaskBodyField","getContentType","headers","find","k","toLowerCase","hasSupportedContentType","contentType","isSupportedContentType","some","t","startsWith","search","params","URLSearchParams","key","set","toString","map","v","maskBody","data","Array","isArray","result","value","Object","entries","item","applyMasking","maskRequestBodyCallback","request","body","maskedBody","undefined","maskResponseBodyCallback","response","length","bodyData","test","parsedBody","JSON","parse","stringify","lines","split","filter","line","trim","maskedLines","parsed","masked","join","url","URL","logRequest","error","logs","spans","traceId","path","pathname","excludeCallback","size","uuid","randomUUID","exception","type","message","truncateExceptionMessage","stacktrace","truncateExceptionStackTrace","stack","sentryEventId","getSentryEventId","log","timestamp","logger","level","truncateLogMessage","push","shift","writeToFile","acquire","TempGzipFile","finalItem","skipEmptyValues","forEach","toJSON","writeLine","getFile","retryFileLater","file","unshift","rotateFile","close","delete","Date","now","clear","clearInterval","convertHeaders","Headers","flatMap","convertBody","isValidJsonString","pattern","fromEntries","_","Number","isNaN","isBuffer","msg","suffix","slice"]}
@@ -1,6 +1,10 @@
1
1
  import { Buffer } from 'node:buffer';
2
2
  import { IncomingHttpHeaders, OutgoingHttpHeaders } from 'node:http';
3
+ import { SpanData } from './spanCollector.cjs';
3
4
  import TempGzipFile from './tempGzipFile.cjs';
5
+ import '@opentelemetry/sdk-trace-base';
6
+ import './logging.cjs';
7
+ import 'winston';
4
8
 
5
9
  type Request = {
6
10
  timestamp: number;
@@ -34,6 +38,7 @@ type RequestLoggingConfig = {
34
38
  logResponseBody: boolean;
35
39
  logException: boolean;
36
40
  captureLogs: boolean;
41
+ captureTraces: boolean;
37
42
  maskQueryParams: RegExp[];
38
43
  maskHeaders: RegExp[];
39
44
  maskBodyFields: RegExp[];
@@ -65,7 +70,7 @@ declare class RequestLogger {
65
70
  private maskHeaders;
66
71
  private maskBody;
67
72
  private applyMasking;
68
- logRequest(request: Request, response: Response, error?: Error, logs?: LogRecord[]): void;
73
+ logRequest(request: Request, response: Response, error?: Error, logs?: LogRecord[], spans?: SpanData[], traceId?: string): void;
69
74
  writeToFile(): Promise<void>;
70
75
  getFile(): TempGzipFile | undefined;
71
76
  retryFileLater(file: TempGzipFile): void;
@@ -1,6 +1,10 @@
1
1
  import { Buffer } from 'node:buffer';
2
2
  import { IncomingHttpHeaders, OutgoingHttpHeaders } from 'node:http';
3
+ import { SpanData } from './spanCollector.js';
3
4
  import TempGzipFile from './tempGzipFile.js';
5
+ import '@opentelemetry/sdk-trace-base';
6
+ import './logging.js';
7
+ import 'winston';
4
8
 
5
9
  type Request = {
6
10
  timestamp: number;
@@ -34,6 +38,7 @@ type RequestLoggingConfig = {
34
38
  logResponseBody: boolean;
35
39
  logException: boolean;
36
40
  captureLogs: boolean;
41
+ captureTraces: boolean;
37
42
  maskQueryParams: RegExp[];
38
43
  maskHeaders: RegExp[];
39
44
  maskBodyFields: RegExp[];
@@ -65,7 +70,7 @@ declare class RequestLogger {
65
70
  private maskHeaders;
66
71
  private maskBody;
67
72
  private applyMasking;
68
- logRequest(request: Request, response: Response, error?: Error, logs?: LogRecord[]): void;
73
+ logRequest(request: Request, response: Response, error?: Error, logs?: LogRecord[], spans?: SpanData[], traceId?: string): void;
69
74
  writeToFile(): Promise<void>;
70
75
  getFile(): TempGzipFile | undefined;
71
76
  retryFileLater(file: TempGzipFile): void;
@@ -80,6 +80,7 @@ const DEFAULT_CONFIG = {
80
80
  logResponseBody: false,
81
81
  logException: true,
82
82
  captureLogs: false,
83
+ captureTraces: false,
83
84
  maskQueryParams: [],
84
85
  maskHeaders: [],
85
86
  maskBodyFields: [],
@@ -243,7 +244,7 @@ const _RequestLogger = class _RequestLogger {
243
244
  item.request.url = url.toString();
244
245
  return item;
245
246
  }
246
- logRequest(request, response, error, logs) {
247
+ logRequest(request, response, error, logs, spans, traceId) {
247
248
  var _a, _b, _c;
248
249
  if (!this.enabled || this.suspendUntil !== null) return;
249
250
  const url = new URL(request.url);
@@ -283,6 +284,12 @@ const _RequestLogger = class _RequestLogger {
283
284
  message: truncateLogMessage(log.message)
284
285
  }));
285
286
  }
287
+ if (spans && spans.length > 0) {
288
+ item.spans = spans;
289
+ }
290
+ if (traceId) {
291
+ item.traceId = traceId;
292
+ }
286
293
  this.pendingWrites.push(item);
287
294
  if (this.pendingWrites.length > MAX_PENDING_WRITES) {
288
295
  this.pendingWrites.shift();
@@ -305,7 +312,9 @@ const _RequestLogger = class _RequestLogger {
305
312
  request: skipEmptyValues(item.request),
306
313
  response: skipEmptyValues(item.response),
307
314
  exception: item.exception,
308
- logs: item.logs
315
+ logs: item.logs,
316
+ spans: item.spans,
317
+ traceId: item.traceId
309
318
  };
310
319
  [
311
320
  finalItem.request.body,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/common/requestLogger.ts"],"sourcesContent":["import AsyncLock from \"async-lock\";\nimport { Buffer } from \"node:buffer\";\nimport { randomUUID } from \"node:crypto\";\nimport { IncomingHttpHeaders, OutgoingHttpHeaders } from \"node:http\";\n\nimport { getSentryEventId } from \"./sentry.js\";\nimport {\n truncateExceptionMessage,\n truncateExceptionStackTrace,\n} from \"./serverErrorCounter.js\";\nimport TempGzipFile, { checkWritableFs } from \"./tempGzipFile.js\";\n\nconst MAX_BODY_SIZE = 50_000; // 50 KB (uncompressed)\nconst MAX_FILE_SIZE = 1_000_000; // 1 MB (compressed)\nconst MAX_FILES = 50;\nconst MAX_PENDING_WRITES = 100;\nconst MAX_LOG_MSG_LENGTH = 2048;\nconst BODY_TOO_LARGE = Buffer.from(\"<body too large>\");\nconst BODY_MASKED = Buffer.from(\"<masked>\");\nconst MASKED = \"******\";\nconst ALLOWED_CONTENT_TYPES = [\n \"application/json\",\n \"application/ld+json\",\n \"application/problem+json\",\n \"application/vnd.api+json\",\n \"application/x-ndjson\",\n \"text/plain\",\n \"text/html\",\n];\nconst EXCLUDE_PATH_PATTERNS = [\n /\\/_?healthz?$/i,\n /\\/_?health[_-]?checks?$/i,\n /\\/_?heart[_-]?beats?$/i,\n /\\/ping$/i,\n /\\/ready$/i,\n /\\/live$/i,\n /\\/favicon(?:-[\\w-]+)?\\.(ico|png|svg)$/,\n /\\/apple-touch-icon(?:-[\\w-]+)?\\.png$/,\n /\\/robots\\.txt$/,\n /\\/sitemap\\.xml$/,\n /\\/manifest\\.json$/,\n /\\/site\\.webmanifest$/,\n /\\/service-worker\\.js$/,\n /\\/sw\\.js$/,\n /\\/\\.well-known\\//,\n];\nconst EXCLUDE_USER_AGENT_PATTERNS = [\n /health[-_ ]?check/i,\n /microsoft-azure-application-lb/i,\n /googlehc/i,\n /kube-probe/i,\n];\nconst MASK_QUERY_PARAM_PATTERNS = [\n /auth/i,\n /api-?key/i,\n /secret/i,\n /token/i,\n /password/i,\n /pwd/i,\n];\nconst MASK_HEADER_PATTERNS = [\n /auth/i,\n /api-?key/i,\n /secret/i,\n /token/i,\n /cookie/i,\n];\nconst MASK_BODY_FIELD_PATTERNS = [\n /password/i,\n /pwd/i,\n /token/i,\n /secret/i,\n /auth/i,\n /card[-_ ]?number/i,\n /ccv/i,\n /ssn/i,\n];\n\nexport type Request = {\n timestamp: number;\n method: string;\n path?: string;\n url: string;\n headers: [string, string][];\n size?: number;\n consumer?: string;\n body?: Buffer;\n};\n\nexport type Response = {\n statusCode: number;\n responseTime: number;\n headers: [string, string][];\n size?: number;\n body?: Buffer;\n};\n\nexport type LogRecord = {\n timestamp: number;\n logger?: string;\n level: string;\n message: string;\n};\n\nexport type RequestLoggingConfig = {\n enabled: boolean;\n logQueryParams: boolean;\n logRequestHeaders: boolean;\n logRequestBody: boolean;\n logResponseHeaders: boolean;\n logResponseBody: boolean;\n logException: boolean;\n captureLogs: boolean;\n maskQueryParams: RegExp[];\n maskHeaders: RegExp[];\n maskBodyFields: RegExp[];\n maskRequestBodyCallback?: (request: Request) => Buffer | null | undefined;\n maskResponseBodyCallback?: (\n request: Request,\n response: Response,\n ) => Buffer | null | undefined;\n excludePaths: RegExp[];\n excludeCallback?: (request: Request, response: Response) => boolean;\n};\n\nconst DEFAULT_CONFIG: RequestLoggingConfig = {\n enabled: false,\n logQueryParams: true,\n logRequestHeaders: false,\n logRequestBody: false,\n logResponseHeaders: true,\n logResponseBody: false,\n logException: true,\n captureLogs: false,\n maskQueryParams: [],\n maskHeaders: [],\n maskBodyFields: [],\n excludePaths: [],\n};\n\ntype RequestLogItem = {\n uuid: string;\n request: Request;\n response: Response;\n exception?: {\n type: string;\n message: string;\n stacktrace: string;\n sentryEventId?: string;\n };\n logs?: LogRecord[];\n};\n\nexport default class RequestLogger {\n public config: RequestLoggingConfig;\n public enabled: boolean;\n public suspendUntil: number | null = null;\n private pendingWrites: RequestLogItem[] = [];\n private currentFile: TempGzipFile | null = null;\n private files: TempGzipFile[] = [];\n private maintainIntervalId?: NodeJS.Timeout;\n private lock = new AsyncLock();\n\n constructor(config?: Partial<RequestLoggingConfig>) {\n this.config = { ...DEFAULT_CONFIG, ...config };\n this.enabled = this.config.enabled && checkWritableFs();\n\n if (this.enabled) {\n this.maintainIntervalId = setInterval(() => {\n this.maintain();\n }, 1000);\n }\n }\n\n get maxBodySize() {\n return MAX_BODY_SIZE;\n }\n\n private shouldExcludePath(urlPath: string) {\n const patterns = [...this.config.excludePaths, ...EXCLUDE_PATH_PATTERNS];\n return matchPatterns(urlPath, patterns);\n }\n\n private shouldExcludeUserAgent(userAgent?: string) {\n return userAgent\n ? matchPatterns(userAgent, EXCLUDE_USER_AGENT_PATTERNS)\n : false;\n }\n\n private shouldMaskQueryParam(name: string) {\n const patterns = [\n ...this.config.maskQueryParams,\n ...MASK_QUERY_PARAM_PATTERNS,\n ];\n return matchPatterns(name, patterns);\n }\n\n private shouldMaskHeader(name: string) {\n const patterns = [...this.config.maskHeaders, ...MASK_HEADER_PATTERNS];\n return matchPatterns(name, patterns);\n }\n\n private shouldMaskBodyField(name: string) {\n const patterns = [\n ...this.config.maskBodyFields,\n ...MASK_BODY_FIELD_PATTERNS,\n ];\n return matchPatterns(name, patterns);\n }\n\n private getContentType(headers: [string, string][]) {\n return headers.find(([k]) => k.toLowerCase() === \"content-type\")?.[1];\n }\n\n private hasSupportedContentType(headers: [string, string][]) {\n const contentType = this.getContentType(headers);\n return this.isSupportedContentType(contentType);\n }\n\n public isSupportedContentType(contentType?: string | null) {\n return (\n typeof contentType === \"string\" &&\n ALLOWED_CONTENT_TYPES.some((t) => contentType.startsWith(t))\n );\n }\n\n private maskQueryParams(search: string) {\n const params = new URLSearchParams(search);\n for (const [key] of params) {\n if (this.shouldMaskQueryParam(key)) {\n params.set(key, MASKED);\n }\n }\n return params.toString();\n }\n\n private maskHeaders(headers: [string, string][]): [string, string][] {\n return headers.map(([k, v]) => [k, this.shouldMaskHeader(k) ? MASKED : v]);\n }\n\n private maskBody(data: any): any {\n if (typeof data === \"object\" && data !== null && !Array.isArray(data)) {\n const result: any = {};\n for (const [key, value] of Object.entries(data)) {\n if (typeof value === \"string\" && this.shouldMaskBodyField(key)) {\n result[key] = MASKED;\n } else {\n result[key] = this.maskBody(value);\n }\n }\n return result;\n }\n if (Array.isArray(data)) {\n return data.map((item) => this.maskBody(item));\n }\n return data;\n }\n\n private applyMasking(item: RequestLogItem) {\n // Apply user-provided maskRequestBodyCallback function\n if (\n this.config.maskRequestBodyCallback &&\n item.request.body &&\n item.request.body !== BODY_TOO_LARGE\n ) {\n try {\n const maskedBody = this.config.maskRequestBodyCallback(item.request);\n item.request.body = maskedBody ?? BODY_MASKED;\n } catch {\n item.request.body = undefined;\n }\n }\n\n // Apply user-provided maskResponseBodyCallback function\n if (\n this.config.maskResponseBodyCallback &&\n item.response.body &&\n item.response.body !== BODY_TOO_LARGE\n ) {\n try {\n const maskedBody = this.config.maskResponseBodyCallback(\n item.request,\n item.response,\n );\n item.response.body = maskedBody ?? BODY_MASKED;\n } catch {\n item.response.body = undefined;\n }\n }\n\n // Check request and response body sizes\n if (item.request.body && item.request.body.length > MAX_BODY_SIZE) {\n item.request.body = BODY_TOO_LARGE;\n }\n if (item.response.body && item.response.body.length > MAX_BODY_SIZE) {\n item.response.body = BODY_TOO_LARGE;\n }\n\n // Mask request and response body fields\n for (const key of [\"request\", \"response\"] as const) {\n const bodyData = item[key].body;\n if (\n !bodyData ||\n bodyData === BODY_TOO_LARGE ||\n bodyData === BODY_MASKED\n ) {\n continue;\n }\n\n try {\n const contentType = this.getContentType(item[key].headers);\n if (!contentType || /\\bjson\\b/i.test(contentType)) {\n const parsedBody = JSON.parse(bodyData.toString());\n const maskedBody = this.maskBody(parsedBody);\n item[key].body = Buffer.from(JSON.stringify(maskedBody));\n } else if (/\\bndjson\\b/i.test(contentType)) {\n const lines = bodyData\n .toString()\n .split(\"\\n\")\n .filter((line) => line.trim());\n const maskedLines = lines.map((line) => {\n try {\n const parsed = JSON.parse(line);\n const masked = this.maskBody(parsed);\n return JSON.stringify(masked);\n } catch {\n return line; // Keep unparseable lines as is\n }\n });\n item[key].body = Buffer.from(maskedLines.join(\"\\n\"));\n }\n } catch {\n // If parsing fails, leave body as is\n }\n }\n\n // Mask request and response headers\n item.request.headers = this.config.logRequestHeaders\n ? this.maskHeaders(item.request.headers)\n : [];\n item.response.headers = this.config.logResponseHeaders\n ? this.maskHeaders(item.response.headers)\n : [];\n\n // Mask query params\n const url = new URL(item.request.url);\n url.search = this.config.logQueryParams\n ? this.maskQueryParams(url.search)\n : \"\";\n item.request.url = url.toString();\n\n return item;\n }\n\n logRequest(\n request: Request,\n response: Response,\n error?: Error,\n logs?: LogRecord[],\n ) {\n if (!this.enabled || this.suspendUntil !== null) return;\n\n const url = new URL(request.url);\n const path = request.path ?? url.pathname;\n const userAgent = request.headers.find(\n ([k]) => k.toLowerCase() === \"user-agent\",\n )?.[1];\n\n if (\n this.shouldExcludePath(path) ||\n this.shouldExcludeUserAgent(userAgent) ||\n (this.config.excludeCallback?.(request, response) ?? false)\n ) {\n return;\n }\n\n if (\n !this.config.logRequestBody ||\n !this.hasSupportedContentType(request.headers)\n ) {\n request.body = undefined;\n }\n if (\n !this.config.logResponseBody ||\n !this.hasSupportedContentType(response.headers)\n ) {\n response.body = undefined;\n }\n\n if (request.size !== undefined && request.size < 0) {\n request.size = undefined;\n }\n if (response.size !== undefined && response.size < 0) {\n response.size = undefined;\n }\n\n const item: RequestLogItem = {\n uuid: randomUUID(),\n request: request,\n response: response,\n exception:\n error && this.config.logException\n ? {\n type: error.name,\n message: truncateExceptionMessage(error.message),\n stacktrace: truncateExceptionStackTrace(error.stack || \"\"),\n sentryEventId: getSentryEventId(),\n }\n : undefined,\n };\n\n if (logs && logs.length > 0) {\n item.logs = logs.map((log) => ({\n timestamp: log.timestamp,\n logger: log.logger,\n level: log.level,\n message: truncateLogMessage(log.message),\n }));\n }\n this.pendingWrites.push(item);\n\n if (this.pendingWrites.length > MAX_PENDING_WRITES) {\n this.pendingWrites.shift();\n }\n }\n\n async writeToFile() {\n if (!this.enabled || this.pendingWrites.length === 0) {\n return;\n }\n return this.lock.acquire(\"file\", async () => {\n if (!this.currentFile) {\n this.currentFile = new TempGzipFile(\"request_logs\");\n }\n while (this.pendingWrites.length > 0) {\n let item = this.pendingWrites.shift();\n if (item) {\n item = this.applyMasking(item);\n\n const finalItem = {\n uuid: item.uuid,\n request: skipEmptyValues(item.request),\n response: skipEmptyValues(item.response),\n exception: item.exception,\n logs: item.logs,\n };\n\n // Set up body serialization for JSON\n [finalItem.request.body, finalItem.response.body].forEach((body) => {\n if (body) {\n // @ts-expect-error Override Buffer's default JSON serialization\n body.toJSON = function () {\n return this.toString(\"base64\");\n };\n }\n });\n\n await this.currentFile.writeLine(\n Buffer.from(JSON.stringify(finalItem)),\n );\n }\n }\n });\n }\n\n getFile() {\n return this.files.shift();\n }\n\n retryFileLater(file: TempGzipFile) {\n this.files.unshift(file);\n }\n\n async rotateFile() {\n return this.lock.acquire(\"file\", async () => {\n if (this.currentFile) {\n await this.currentFile.close();\n this.files.push(this.currentFile);\n this.currentFile = null;\n }\n });\n }\n\n async maintain() {\n await this.writeToFile();\n if (this.currentFile && this.currentFile.size > MAX_FILE_SIZE) {\n await this.rotateFile();\n }\n while (this.files.length > MAX_FILES) {\n const file = this.files.shift();\n file?.delete();\n }\n if (this.suspendUntil !== null && this.suspendUntil < Date.now()) {\n this.suspendUntil = null;\n }\n }\n\n async clear() {\n this.pendingWrites = [];\n await this.rotateFile();\n this.files.forEach((file) => {\n file.delete();\n });\n this.files = [];\n }\n\n async close() {\n this.enabled = false;\n await this.clear();\n if (this.maintainIntervalId) {\n clearInterval(this.maintainIntervalId);\n }\n }\n}\n\nexport function convertHeaders(\n headers:\n | Headers\n | IncomingHttpHeaders\n | OutgoingHttpHeaders\n | Record<string, string | string[] | number | undefined>\n | undefined,\n) {\n if (!headers) {\n return [];\n }\n if (headers instanceof Headers) {\n return Array.from(headers.entries());\n }\n return Object.entries(headers).flatMap(([key, value]) => {\n if (value === undefined) {\n return [];\n }\n if (Array.isArray(value)) {\n return value.map((v) => [key, v]);\n }\n return [[key, value.toString()]];\n }) as [string, string][];\n}\n\nexport function convertBody(body: any, contentType?: string | null) {\n if (!body || !contentType) {\n return;\n }\n try {\n if (contentType.startsWith(\"application/json\")) {\n if (isValidJsonString(body)) {\n return Buffer.from(body);\n } else {\n return Buffer.from(JSON.stringify(body));\n }\n }\n if (contentType.startsWith(\"text/\") && typeof body === \"string\") {\n return Buffer.from(body);\n }\n } catch (error) {\n return;\n }\n}\n\nfunction isValidJsonString(body: any) {\n if (typeof body !== \"string\") {\n return false;\n }\n try {\n JSON.parse(body);\n return true;\n } catch {\n return false;\n }\n}\n\nfunction matchPatterns(value: string, patterns: RegExp[]) {\n return patterns.some((pattern) => {\n return pattern.test(value);\n });\n}\n\nfunction skipEmptyValues<T extends Record<string, any>>(data: T) {\n return Object.fromEntries(\n Object.entries(data).filter(([_, v]) => {\n if (v == null || Number.isNaN(v)) return false;\n if (Array.isArray(v) || Buffer.isBuffer(v) || typeof v === \"string\") {\n return v.length > 0;\n }\n return true;\n }),\n ) as Partial<T>;\n}\n\nfunction truncateLogMessage(msg: string) {\n if (msg.length > MAX_LOG_MSG_LENGTH) {\n const suffix = \"... (truncated)\";\n return msg.slice(0, MAX_LOG_MSG_LENGTH - suffix.length) + suffix;\n }\n return msg;\n}\n"],"mappings":"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","names":["AsyncLock","Buffer","randomUUID","getSentryEventId","truncateExceptionMessage","truncateExceptionStackTrace","TempGzipFile","checkWritableFs","MAX_BODY_SIZE","MAX_FILE_SIZE","MAX_FILES","MAX_PENDING_WRITES","MAX_LOG_MSG_LENGTH","BODY_TOO_LARGE","from","BODY_MASKED","MASKED","ALLOWED_CONTENT_TYPES","EXCLUDE_PATH_PATTERNS","EXCLUDE_USER_AGENT_PATTERNS","MASK_QUERY_PARAM_PATTERNS","MASK_HEADER_PATTERNS","MASK_BODY_FIELD_PATTERNS","DEFAULT_CONFIG","enabled","logQueryParams","logRequestHeaders","logRequestBody","logResponseHeaders","logResponseBody","logException","captureLogs","maskQueryParams","maskHeaders","maskBodyFields","excludePaths","RequestLogger","config","suspendUntil","pendingWrites","currentFile","files","maintainIntervalId","lock","setInterval","maintain","maxBodySize","shouldExcludePath","urlPath","patterns","matchPatterns","shouldExcludeUserAgent","userAgent","shouldMaskQueryParam","name","shouldMaskHeader","shouldMaskBodyField","getContentType","headers","find","k","toLowerCase","hasSupportedContentType","contentType","isSupportedContentType","some","t","startsWith","search","params","URLSearchParams","key","set","toString","map","v","maskBody","data","Array","isArray","result","value","Object","entries","item","applyMasking","maskRequestBodyCallback","request","body","maskedBody","undefined","maskResponseBodyCallback","response","length","bodyData","test","parsedBody","JSON","parse","stringify","lines","split","filter","line","trim","maskedLines","parsed","masked","join","url","URL","logRequest","error","logs","path","pathname","excludeCallback","size","uuid","exception","type","message","stacktrace","stack","sentryEventId","log","timestamp","logger","level","truncateLogMessage","push","shift","writeToFile","acquire","finalItem","skipEmptyValues","forEach","toJSON","writeLine","getFile","retryFileLater","file","unshift","rotateFile","close","delete","Date","now","clear","clearInterval","convertHeaders","Headers","flatMap","convertBody","isValidJsonString","pattern","fromEntries","_","Number","isNaN","isBuffer","msg","suffix","slice"]}
1
+ {"version":3,"sources":["../../src/common/requestLogger.ts"],"sourcesContent":["import AsyncLock from \"async-lock\";\nimport { Buffer } from \"node:buffer\";\nimport { randomUUID } from \"node:crypto\";\nimport type { IncomingHttpHeaders, OutgoingHttpHeaders } from \"node:http\";\n\nimport { getSentryEventId } from \"./sentry.js\";\nimport {\n truncateExceptionMessage,\n truncateExceptionStackTrace,\n} from \"./serverErrorCounter.js\";\nimport type { SpanData } from \"./spanCollector.js\";\nimport TempGzipFile, { checkWritableFs } from \"./tempGzipFile.js\";\n\nconst MAX_BODY_SIZE = 50_000; // 50 KB (uncompressed)\nconst MAX_FILE_SIZE = 1_000_000; // 1 MB (compressed)\nconst MAX_FILES = 50;\nconst MAX_PENDING_WRITES = 100;\nconst MAX_LOG_MSG_LENGTH = 2048;\nconst BODY_TOO_LARGE = Buffer.from(\"<body too large>\");\nconst BODY_MASKED = Buffer.from(\"<masked>\");\nconst MASKED = \"******\";\nconst ALLOWED_CONTENT_TYPES = [\n \"application/json\",\n \"application/ld+json\",\n \"application/problem+json\",\n \"application/vnd.api+json\",\n \"application/x-ndjson\",\n \"text/plain\",\n \"text/html\",\n];\nconst EXCLUDE_PATH_PATTERNS = [\n /\\/_?healthz?$/i,\n /\\/_?health[_-]?checks?$/i,\n /\\/_?heart[_-]?beats?$/i,\n /\\/ping$/i,\n /\\/ready$/i,\n /\\/live$/i,\n /\\/favicon(?:-[\\w-]+)?\\.(ico|png|svg)$/,\n /\\/apple-touch-icon(?:-[\\w-]+)?\\.png$/,\n /\\/robots\\.txt$/,\n /\\/sitemap\\.xml$/,\n /\\/manifest\\.json$/,\n /\\/site\\.webmanifest$/,\n /\\/service-worker\\.js$/,\n /\\/sw\\.js$/,\n /\\/\\.well-known\\//,\n];\nconst EXCLUDE_USER_AGENT_PATTERNS = [\n /health[-_ ]?check/i,\n /microsoft-azure-application-lb/i,\n /googlehc/i,\n /kube-probe/i,\n];\nconst MASK_QUERY_PARAM_PATTERNS = [\n /auth/i,\n /api-?key/i,\n /secret/i,\n /token/i,\n /password/i,\n /pwd/i,\n];\nconst MASK_HEADER_PATTERNS = [\n /auth/i,\n /api-?key/i,\n /secret/i,\n /token/i,\n /cookie/i,\n];\nconst MASK_BODY_FIELD_PATTERNS = [\n /password/i,\n /pwd/i,\n /token/i,\n /secret/i,\n /auth/i,\n /card[-_ ]?number/i,\n /ccv/i,\n /ssn/i,\n];\n\nexport type Request = {\n timestamp: number;\n method: string;\n path?: string;\n url: string;\n headers: [string, string][];\n size?: number;\n consumer?: string;\n body?: Buffer;\n};\n\nexport type Response = {\n statusCode: number;\n responseTime: number;\n headers: [string, string][];\n size?: number;\n body?: Buffer;\n};\n\nexport type LogRecord = {\n timestamp: number;\n logger?: string;\n level: string;\n message: string;\n};\n\nexport type RequestLoggingConfig = {\n enabled: boolean;\n logQueryParams: boolean;\n logRequestHeaders: boolean;\n logRequestBody: boolean;\n logResponseHeaders: boolean;\n logResponseBody: boolean;\n logException: boolean;\n captureLogs: boolean;\n captureTraces: boolean;\n maskQueryParams: RegExp[];\n maskHeaders: RegExp[];\n maskBodyFields: RegExp[];\n maskRequestBodyCallback?: (request: Request) => Buffer | null | undefined;\n maskResponseBodyCallback?: (\n request: Request,\n response: Response,\n ) => Buffer | null | undefined;\n excludePaths: RegExp[];\n excludeCallback?: (request: Request, response: Response) => boolean;\n};\n\nconst DEFAULT_CONFIG: RequestLoggingConfig = {\n enabled: false,\n logQueryParams: true,\n logRequestHeaders: false,\n logRequestBody: false,\n logResponseHeaders: true,\n logResponseBody: false,\n logException: true,\n captureLogs: false,\n captureTraces: false,\n maskQueryParams: [],\n maskHeaders: [],\n maskBodyFields: [],\n excludePaths: [],\n};\n\ntype RequestLogItem = {\n uuid: string;\n request: Request;\n response: Response;\n exception?: {\n type: string;\n message: string;\n stacktrace: string;\n sentryEventId?: string;\n };\n logs?: LogRecord[];\n spans?: SpanData[];\n traceId?: string;\n};\n\nexport default class RequestLogger {\n public config: RequestLoggingConfig;\n public enabled: boolean;\n public suspendUntil: number | null = null;\n private pendingWrites: RequestLogItem[] = [];\n private currentFile: TempGzipFile | null = null;\n private files: TempGzipFile[] = [];\n private maintainIntervalId?: NodeJS.Timeout;\n private lock = new AsyncLock();\n\n constructor(config?: Partial<RequestLoggingConfig>) {\n this.config = { ...DEFAULT_CONFIG, ...config };\n this.enabled = this.config.enabled && checkWritableFs();\n\n if (this.enabled) {\n this.maintainIntervalId = setInterval(() => {\n this.maintain();\n }, 1000);\n }\n }\n\n get maxBodySize() {\n return MAX_BODY_SIZE;\n }\n\n private shouldExcludePath(urlPath: string) {\n const patterns = [...this.config.excludePaths, ...EXCLUDE_PATH_PATTERNS];\n return matchPatterns(urlPath, patterns);\n }\n\n private shouldExcludeUserAgent(userAgent?: string) {\n return userAgent\n ? matchPatterns(userAgent, EXCLUDE_USER_AGENT_PATTERNS)\n : false;\n }\n\n private shouldMaskQueryParam(name: string) {\n const patterns = [\n ...this.config.maskQueryParams,\n ...MASK_QUERY_PARAM_PATTERNS,\n ];\n return matchPatterns(name, patterns);\n }\n\n private shouldMaskHeader(name: string) {\n const patterns = [...this.config.maskHeaders, ...MASK_HEADER_PATTERNS];\n return matchPatterns(name, patterns);\n }\n\n private shouldMaskBodyField(name: string) {\n const patterns = [\n ...this.config.maskBodyFields,\n ...MASK_BODY_FIELD_PATTERNS,\n ];\n return matchPatterns(name, patterns);\n }\n\n private getContentType(headers: [string, string][]) {\n return headers.find(([k]) => k.toLowerCase() === \"content-type\")?.[1];\n }\n\n private hasSupportedContentType(headers: [string, string][]) {\n const contentType = this.getContentType(headers);\n return this.isSupportedContentType(contentType);\n }\n\n public isSupportedContentType(contentType?: string | null) {\n return (\n typeof contentType === \"string\" &&\n ALLOWED_CONTENT_TYPES.some((t) => contentType.startsWith(t))\n );\n }\n\n private maskQueryParams(search: string) {\n const params = new URLSearchParams(search);\n for (const [key] of params) {\n if (this.shouldMaskQueryParam(key)) {\n params.set(key, MASKED);\n }\n }\n return params.toString();\n }\n\n private maskHeaders(headers: [string, string][]): [string, string][] {\n return headers.map(([k, v]) => [k, this.shouldMaskHeader(k) ? MASKED : v]);\n }\n\n private maskBody(data: any): any {\n if (typeof data === \"object\" && data !== null && !Array.isArray(data)) {\n const result: any = {};\n for (const [key, value] of Object.entries(data)) {\n if (typeof value === \"string\" && this.shouldMaskBodyField(key)) {\n result[key] = MASKED;\n } else {\n result[key] = this.maskBody(value);\n }\n }\n return result;\n }\n if (Array.isArray(data)) {\n return data.map((item) => this.maskBody(item));\n }\n return data;\n }\n\n private applyMasking(item: RequestLogItem) {\n // Apply user-provided maskRequestBodyCallback function\n if (\n this.config.maskRequestBodyCallback &&\n item.request.body &&\n item.request.body !== BODY_TOO_LARGE\n ) {\n try {\n const maskedBody = this.config.maskRequestBodyCallback(item.request);\n item.request.body = maskedBody ?? BODY_MASKED;\n } catch {\n item.request.body = undefined;\n }\n }\n\n // Apply user-provided maskResponseBodyCallback function\n if (\n this.config.maskResponseBodyCallback &&\n item.response.body &&\n item.response.body !== BODY_TOO_LARGE\n ) {\n try {\n const maskedBody = this.config.maskResponseBodyCallback(\n item.request,\n item.response,\n );\n item.response.body = maskedBody ?? BODY_MASKED;\n } catch {\n item.response.body = undefined;\n }\n }\n\n // Check request and response body sizes\n if (item.request.body && item.request.body.length > MAX_BODY_SIZE) {\n item.request.body = BODY_TOO_LARGE;\n }\n if (item.response.body && item.response.body.length > MAX_BODY_SIZE) {\n item.response.body = BODY_TOO_LARGE;\n }\n\n // Mask request and response body fields\n for (const key of [\"request\", \"response\"] as const) {\n const bodyData = item[key].body;\n if (\n !bodyData ||\n bodyData === BODY_TOO_LARGE ||\n bodyData === BODY_MASKED\n ) {\n continue;\n }\n\n try {\n const contentType = this.getContentType(item[key].headers);\n if (!contentType || /\\bjson\\b/i.test(contentType)) {\n const parsedBody = JSON.parse(bodyData.toString());\n const maskedBody = this.maskBody(parsedBody);\n item[key].body = Buffer.from(JSON.stringify(maskedBody));\n } else if (/\\bndjson\\b/i.test(contentType)) {\n const lines = bodyData\n .toString()\n .split(\"\\n\")\n .filter((line) => line.trim());\n const maskedLines = lines.map((line) => {\n try {\n const parsed = JSON.parse(line);\n const masked = this.maskBody(parsed);\n return JSON.stringify(masked);\n } catch {\n return line; // Keep unparseable lines as is\n }\n });\n item[key].body = Buffer.from(maskedLines.join(\"\\n\"));\n }\n } catch {\n // If parsing fails, leave body as is\n }\n }\n\n // Mask request and response headers\n item.request.headers = this.config.logRequestHeaders\n ? this.maskHeaders(item.request.headers)\n : [];\n item.response.headers = this.config.logResponseHeaders\n ? this.maskHeaders(item.response.headers)\n : [];\n\n // Mask query params\n const url = new URL(item.request.url);\n url.search = this.config.logQueryParams\n ? this.maskQueryParams(url.search)\n : \"\";\n item.request.url = url.toString();\n\n return item;\n }\n\n logRequest(\n request: Request,\n response: Response,\n error?: Error,\n logs?: LogRecord[],\n spans?: SpanData[],\n traceId?: string,\n ) {\n if (!this.enabled || this.suspendUntil !== null) return;\n\n const url = new URL(request.url);\n const path = request.path ?? url.pathname;\n const userAgent = request.headers.find(\n ([k]) => k.toLowerCase() === \"user-agent\",\n )?.[1];\n\n if (\n this.shouldExcludePath(path) ||\n this.shouldExcludeUserAgent(userAgent) ||\n (this.config.excludeCallback?.(request, response) ?? false)\n ) {\n return;\n }\n\n if (\n !this.config.logRequestBody ||\n !this.hasSupportedContentType(request.headers)\n ) {\n request.body = undefined;\n }\n if (\n !this.config.logResponseBody ||\n !this.hasSupportedContentType(response.headers)\n ) {\n response.body = undefined;\n }\n\n if (request.size !== undefined && request.size < 0) {\n request.size = undefined;\n }\n if (response.size !== undefined && response.size < 0) {\n response.size = undefined;\n }\n\n const item: RequestLogItem = {\n uuid: randomUUID(),\n request: request,\n response: response,\n exception:\n error && this.config.logException\n ? {\n type: error.name,\n message: truncateExceptionMessage(error.message),\n stacktrace: truncateExceptionStackTrace(error.stack || \"\"),\n sentryEventId: getSentryEventId(),\n }\n : undefined,\n };\n\n if (logs && logs.length > 0) {\n item.logs = logs.map((log) => ({\n timestamp: log.timestamp,\n logger: log.logger,\n level: log.level,\n message: truncateLogMessage(log.message),\n }));\n }\n if (spans && spans.length > 0) {\n item.spans = spans;\n }\n if (traceId) {\n item.traceId = traceId;\n }\n this.pendingWrites.push(item);\n\n if (this.pendingWrites.length > MAX_PENDING_WRITES) {\n this.pendingWrites.shift();\n }\n }\n\n async writeToFile() {\n if (!this.enabled || this.pendingWrites.length === 0) {\n return;\n }\n return this.lock.acquire(\"file\", async () => {\n if (!this.currentFile) {\n this.currentFile = new TempGzipFile(\"request_logs\");\n }\n while (this.pendingWrites.length > 0) {\n let item = this.pendingWrites.shift();\n if (item) {\n item = this.applyMasking(item);\n\n const finalItem = {\n uuid: item.uuid,\n request: skipEmptyValues(item.request),\n response: skipEmptyValues(item.response),\n exception: item.exception,\n logs: item.logs,\n spans: item.spans,\n traceId: item.traceId,\n };\n\n // Set up body serialization for JSON\n [finalItem.request.body, finalItem.response.body].forEach((body) => {\n if (body) {\n // @ts-expect-error Override Buffer's default JSON serialization\n body.toJSON = function () {\n return this.toString(\"base64\");\n };\n }\n });\n\n await this.currentFile.writeLine(\n Buffer.from(JSON.stringify(finalItem)),\n );\n }\n }\n });\n }\n\n getFile() {\n return this.files.shift();\n }\n\n retryFileLater(file: TempGzipFile) {\n this.files.unshift(file);\n }\n\n async rotateFile() {\n return this.lock.acquire(\"file\", async () => {\n if (this.currentFile) {\n await this.currentFile.close();\n this.files.push(this.currentFile);\n this.currentFile = null;\n }\n });\n }\n\n async maintain() {\n await this.writeToFile();\n if (this.currentFile && this.currentFile.size > MAX_FILE_SIZE) {\n await this.rotateFile();\n }\n while (this.files.length > MAX_FILES) {\n const file = this.files.shift();\n file?.delete();\n }\n if (this.suspendUntil !== null && this.suspendUntil < Date.now()) {\n this.suspendUntil = null;\n }\n }\n\n async clear() {\n this.pendingWrites = [];\n await this.rotateFile();\n this.files.forEach((file) => {\n file.delete();\n });\n this.files = [];\n }\n\n async close() {\n this.enabled = false;\n await this.clear();\n if (this.maintainIntervalId) {\n clearInterval(this.maintainIntervalId);\n }\n }\n}\n\nexport function convertHeaders(\n headers:\n | Headers\n | IncomingHttpHeaders\n | OutgoingHttpHeaders\n | Record<string, string | string[] | number | undefined>\n | undefined,\n) {\n if (!headers) {\n return [];\n }\n if (headers instanceof Headers) {\n return Array.from(headers.entries());\n }\n return Object.entries(headers).flatMap(([key, value]) => {\n if (value === undefined) {\n return [];\n }\n if (Array.isArray(value)) {\n return value.map((v) => [key, v]);\n }\n return [[key, value.toString()]];\n }) as [string, string][];\n}\n\nexport function convertBody(body: any, contentType?: string | null) {\n if (!body || !contentType) {\n return;\n }\n try {\n if (contentType.startsWith(\"application/json\")) {\n if (isValidJsonString(body)) {\n return Buffer.from(body);\n } else {\n return Buffer.from(JSON.stringify(body));\n }\n }\n if (contentType.startsWith(\"text/\") && typeof body === \"string\") {\n return Buffer.from(body);\n }\n } catch (error) {\n return;\n }\n}\n\nfunction isValidJsonString(body: any) {\n if (typeof body !== \"string\") {\n return false;\n }\n try {\n JSON.parse(body);\n return true;\n } catch {\n return false;\n }\n}\n\nfunction matchPatterns(value: string, patterns: RegExp[]) {\n return patterns.some((pattern) => {\n return pattern.test(value);\n });\n}\n\nfunction skipEmptyValues<T extends Record<string, any>>(data: T) {\n return Object.fromEntries(\n Object.entries(data).filter(([_, v]) => {\n if (v == null || Number.isNaN(v)) return false;\n if (Array.isArray(v) || Buffer.isBuffer(v) || typeof v === \"string\") {\n return v.length > 0;\n }\n return true;\n }),\n ) as Partial<T>;\n}\n\nfunction truncateLogMessage(msg: string) {\n if (msg.length > MAX_LOG_MSG_LENGTH) {\n const suffix = \"... (truncated)\";\n return msg.slice(0, MAX_LOG_MSG_LENGTH - suffix.length) + suffix;\n }\n return msg;\n}\n"],"mappings":"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","names":["AsyncLock","Buffer","randomUUID","getSentryEventId","truncateExceptionMessage","truncateExceptionStackTrace","TempGzipFile","checkWritableFs","MAX_BODY_SIZE","MAX_FILE_SIZE","MAX_FILES","MAX_PENDING_WRITES","MAX_LOG_MSG_LENGTH","BODY_TOO_LARGE","from","BODY_MASKED","MASKED","ALLOWED_CONTENT_TYPES","EXCLUDE_PATH_PATTERNS","EXCLUDE_USER_AGENT_PATTERNS","MASK_QUERY_PARAM_PATTERNS","MASK_HEADER_PATTERNS","MASK_BODY_FIELD_PATTERNS","DEFAULT_CONFIG","enabled","logQueryParams","logRequestHeaders","logRequestBody","logResponseHeaders","logResponseBody","logException","captureLogs","captureTraces","maskQueryParams","maskHeaders","maskBodyFields","excludePaths","RequestLogger","config","suspendUntil","pendingWrites","currentFile","files","maintainIntervalId","lock","setInterval","maintain","maxBodySize","shouldExcludePath","urlPath","patterns","matchPatterns","shouldExcludeUserAgent","userAgent","shouldMaskQueryParam","name","shouldMaskHeader","shouldMaskBodyField","getContentType","headers","find","k","toLowerCase","hasSupportedContentType","contentType","isSupportedContentType","some","t","startsWith","search","params","URLSearchParams","key","set","toString","map","v","maskBody","data","Array","isArray","result","value","Object","entries","item","applyMasking","maskRequestBodyCallback","request","body","maskedBody","undefined","maskResponseBodyCallback","response","length","bodyData","test","parsedBody","JSON","parse","stringify","lines","split","filter","line","trim","maskedLines","parsed","masked","join","url","URL","logRequest","error","logs","spans","traceId","path","pathname","excludeCallback","size","uuid","exception","type","message","stacktrace","stack","sentryEventId","log","timestamp","logger","level","truncateLogMessage","push","shift","writeToFile","acquire","finalItem","skipEmptyValues","forEach","toJSON","writeLine","getFile","retryFileLater","file","unshift","rotateFile","close","delete","Date","now","clear","clearInterval","convertHeaders","Headers","flatMap","convertBody","isValidJsonString","pattern","fromEntries","_","Number","isNaN","isBuffer","msg","suffix","slice"]}
@@ -25,7 +25,7 @@ __export(serverErrorCounter_exports, {
25
25
  });
26
26
  module.exports = __toCommonJS(serverErrorCounter_exports);
27
27
  var import_node_crypto = require("node:crypto");
28
- var import_sentry = require("./sentry.js");
28
+ var import_sentry = require('./sentry.cjs');
29
29
  const MAX_MSG_LENGTH = 2048;
30
30
  const MAX_STACKTRACE_LENGTH = 65536;
31
31
  const _ServerErrorCounter = class _ServerErrorCounter {
@@ -4,6 +4,8 @@ import 'winston';
4
4
  import './requestLogger.cjs';
5
5
  import 'node:buffer';
6
6
  import 'node:http';
7
+ import './spanCollector.cjs';
8
+ import '@opentelemetry/sdk-trace-base';
7
9
  import './tempGzipFile.cjs';
8
10
 
9
11
  declare class ServerErrorCounter {
@@ -4,6 +4,8 @@ import 'winston';
4
4
  import './requestLogger.js';
5
5
  import 'node:buffer';
6
6
  import 'node:http';
7
+ import './spanCollector.js';
8
+ import '@opentelemetry/sdk-trace-base';
7
9
  import './tempGzipFile.js';
8
10
 
9
11
  declare class ServerErrorCounter {