apitally 0.24.0 → 0.25.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (152) hide show
  1. package/dist/adonisjs/index.cjs +3 -0
  2. package/dist/adonisjs/index.cjs.map +1 -1
  3. package/dist/adonisjs/index.d.cts +2 -0
  4. package/dist/adonisjs/index.d.ts +2 -0
  5. package/dist/adonisjs/index.js +2 -0
  6. package/dist/adonisjs/index.js.map +1 -1
  7. package/dist/adonisjs/middleware.cjs +6 -2
  8. package/dist/adonisjs/middleware.cjs.map +1 -1
  9. package/dist/adonisjs/middleware.d.cts +2 -0
  10. package/dist/adonisjs/middleware.d.ts +2 -0
  11. package/dist/adonisjs/middleware.js +6 -2
  12. package/dist/adonisjs/middleware.js.map +1 -1
  13. package/dist/adonisjs/provider.d.cts +2 -0
  14. package/dist/adonisjs/provider.d.ts +2 -0
  15. package/dist/adonisjs/stubs/config/apitally.stub +1 -0
  16. package/dist/common/client.cjs +4 -0
  17. package/dist/common/client.cjs.map +1 -1
  18. package/dist/common/client.d.cts +3 -0
  19. package/dist/common/client.d.ts +3 -0
  20. package/dist/common/client.js +4 -0
  21. package/dist/common/client.js.map +1 -1
  22. package/dist/common/consumerRegistry.d.cts +2 -0
  23. package/dist/common/consumerRegistry.d.ts +2 -0
  24. package/dist/common/requestCounter.d.cts +2 -0
  25. package/dist/common/requestCounter.d.ts +2 -0
  26. package/dist/common/requestLogger.cjs +11 -2
  27. package/dist/common/requestLogger.cjs.map +1 -1
  28. package/dist/common/requestLogger.d.cts +6 -1
  29. package/dist/common/requestLogger.d.ts +6 -1
  30. package/dist/common/requestLogger.js +11 -2
  31. package/dist/common/requestLogger.js.map +1 -1
  32. package/dist/common/serverErrorCounter.d.cts +2 -0
  33. package/dist/common/serverErrorCounter.d.ts +2 -0
  34. package/dist/common/spanCollector.cjs +217 -0
  35. package/dist/common/spanCollector.cjs.map +1 -0
  36. package/dist/common/spanCollector.d.cts +50 -0
  37. package/dist/common/spanCollector.d.ts +50 -0
  38. package/dist/common/spanCollector.js +194 -0
  39. package/dist/common/spanCollector.js.map +1 -0
  40. package/dist/common/types.d.cts +2 -0
  41. package/dist/common/types.d.ts +2 -0
  42. package/dist/common/validationErrorCounter.d.cts +2 -0
  43. package/dist/common/validationErrorCounter.d.ts +2 -0
  44. package/dist/elysia/index.cjs +3 -0
  45. package/dist/elysia/index.cjs.map +1 -1
  46. package/dist/elysia/index.d.cts +2 -0
  47. package/dist/elysia/index.d.ts +2 -0
  48. package/dist/elysia/index.js +2 -0
  49. package/dist/elysia/index.js.map +1 -1
  50. package/dist/elysia/plugin.cjs +9 -1
  51. package/dist/elysia/plugin.cjs.map +1 -1
  52. package/dist/elysia/plugin.d.cts +4 -0
  53. package/dist/elysia/plugin.d.ts +4 -0
  54. package/dist/elysia/plugin.js +9 -1
  55. package/dist/elysia/plugin.js.map +1 -1
  56. package/dist/elysia/utils.d.cts +2 -0
  57. package/dist/elysia/utils.d.ts +2 -0
  58. package/dist/express/index.cjs +3 -0
  59. package/dist/express/index.cjs.map +1 -1
  60. package/dist/express/index.d.cts +2 -0
  61. package/dist/express/index.d.ts +2 -0
  62. package/dist/express/index.js +2 -0
  63. package/dist/express/index.js.map +1 -1
  64. package/dist/express/middleware.cjs +85 -77
  65. package/dist/express/middleware.cjs.map +1 -1
  66. package/dist/express/middleware.d.cts +2 -0
  67. package/dist/express/middleware.d.ts +2 -0
  68. package/dist/express/middleware.js +85 -77
  69. package/dist/express/middleware.js.map +1 -1
  70. package/dist/fastify/index.cjs +3 -0
  71. package/dist/fastify/index.cjs.map +1 -1
  72. package/dist/fastify/index.d.cts +2 -0
  73. package/dist/fastify/index.d.ts +2 -0
  74. package/dist/fastify/index.js +2 -0
  75. package/dist/fastify/index.js.map +1 -1
  76. package/dist/fastify/plugin.cjs +14 -5
  77. package/dist/fastify/plugin.cjs.map +1 -1
  78. package/dist/fastify/plugin.d.cts +4 -0
  79. package/dist/fastify/plugin.d.ts +4 -0
  80. package/dist/fastify/plugin.js +14 -5
  81. package/dist/fastify/plugin.js.map +1 -1
  82. package/dist/h3/index.cjs +3 -0
  83. package/dist/h3/index.cjs.map +1 -1
  84. package/dist/h3/index.d.cts +2 -0
  85. package/dist/h3/index.d.ts +2 -0
  86. package/dist/h3/index.js +2 -0
  87. package/dist/h3/index.js.map +1 -1
  88. package/dist/h3/plugin.cjs +13 -5
  89. package/dist/h3/plugin.cjs.map +1 -1
  90. package/dist/h3/plugin.d.cts +4 -0
  91. package/dist/h3/plugin.d.ts +4 -0
  92. package/dist/h3/plugin.js +13 -5
  93. package/dist/h3/plugin.js.map +1 -1
  94. package/dist/hapi/index.cjs +3 -0
  95. package/dist/hapi/index.cjs.map +1 -1
  96. package/dist/hapi/index.d.cts +2 -0
  97. package/dist/hapi/index.d.ts +2 -0
  98. package/dist/hapi/index.js +2 -0
  99. package/dist/hapi/index.js.map +1 -1
  100. package/dist/hapi/plugin.cjs +10 -3
  101. package/dist/hapi/plugin.cjs.map +1 -1
  102. package/dist/hapi/plugin.d.cts +4 -0
  103. package/dist/hapi/plugin.d.ts +4 -0
  104. package/dist/hapi/plugin.js +10 -3
  105. package/dist/hapi/plugin.js.map +1 -1
  106. package/dist/hapi/utils.d.cts +2 -0
  107. package/dist/hapi/utils.d.ts +2 -0
  108. package/dist/hono/index.cjs +3 -0
  109. package/dist/hono/index.cjs.map +1 -1
  110. package/dist/hono/index.d.cts +2 -0
  111. package/dist/hono/index.d.ts +2 -0
  112. package/dist/hono/index.js +2 -0
  113. package/dist/hono/index.js.map +1 -1
  114. package/dist/hono/middleware.cjs +6 -2
  115. package/dist/hono/middleware.cjs.map +1 -1
  116. package/dist/hono/middleware.d.cts +2 -0
  117. package/dist/hono/middleware.d.ts +2 -0
  118. package/dist/hono/middleware.js +6 -2
  119. package/dist/hono/middleware.js.map +1 -1
  120. package/dist/hono/utils.d.cts +2 -0
  121. package/dist/hono/utils.d.ts +2 -0
  122. package/dist/koa/index.cjs +3 -0
  123. package/dist/koa/index.cjs.map +1 -1
  124. package/dist/koa/index.d.cts +2 -0
  125. package/dist/koa/index.d.ts +2 -0
  126. package/dist/koa/index.js +2 -0
  127. package/dist/koa/index.js.map +1 -1
  128. package/dist/koa/middleware.cjs +8 -4
  129. package/dist/koa/middleware.cjs.map +1 -1
  130. package/dist/koa/middleware.d.cts +2 -0
  131. package/dist/koa/middleware.d.ts +2 -0
  132. package/dist/koa/middleware.js +8 -4
  133. package/dist/koa/middleware.js.map +1 -1
  134. package/dist/loggers/console.d.cts +4 -0
  135. package/dist/loggers/console.d.ts +4 -0
  136. package/dist/loggers/hapi.d.cts +4 -0
  137. package/dist/loggers/hapi.d.ts +4 -0
  138. package/dist/loggers/index.d.cts +4 -0
  139. package/dist/loggers/index.d.ts +4 -0
  140. package/dist/loggers/nestjs.d.cts +4 -0
  141. package/dist/loggers/nestjs.d.ts +4 -0
  142. package/dist/loggers/pino.d.cts +4 -0
  143. package/dist/loggers/pino.d.ts +4 -0
  144. package/dist/loggers/winston.d.cts +4 -0
  145. package/dist/loggers/winston.d.ts +4 -0
  146. package/dist/nestjs/index.cjs +3 -0
  147. package/dist/nestjs/index.cjs.map +1 -1
  148. package/dist/nestjs/index.d.cts +2 -0
  149. package/dist/nestjs/index.d.ts +2 -0
  150. package/dist/nestjs/index.js +2 -0
  151. package/dist/nestjs/index.js.map +1 -1
  152. package/package.json +5 -2
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/express/middleware.ts"],"sourcesContent":["import type { Express, NextFunction, Request, Response, Router } from \"express\";\nimport { AsyncLocalStorage } from \"node:async_hooks\";\nimport { performance } from \"node:perf_hooks\";\n\nimport { ApitallyClient } from \"../common/client.js\";\nimport { consumerFromStringOrObject } from \"../common/consumerRegistry.js\";\nimport { parseContentLength } from \"../common/headers.js\";\nimport { getPackageVersion } from \"../common/packageVersions.js\";\nimport type { LogRecord } from \"../common/requestLogger.js\";\nimport { convertBody, convertHeaders } from \"../common/requestLogger.js\";\nimport {\n ApitallyConfig,\n ApitallyConsumer,\n StartupData,\n ValidationError,\n} from \"../common/types.js\";\nimport {\n patchConsole,\n patchNestLogger,\n patchPinoLogger,\n patchWinston,\n} from \"../loggers/index.js\";\nimport {\n getEndpoints,\n getRouterInfo,\n parseExpressPath,\n parseExpressPathRegExp,\n} from \"./utils.js\";\n\ndeclare module \"express\" {\n interface Request {\n apitallyConsumer?: ApitallyConsumer | string | null;\n consumerIdentifier?: ApitallyConsumer | string | null; // For backwards compatibility\n }\n}\n\nexport function useApitally(\n app: Express | Router,\n config: ApitallyConfig & { basePath?: string },\n) {\n const client = new ApitallyClient(config);\n const middleware = getMiddleware(app, client);\n app.use(middleware);\n\n const setStartupData = (attempt: number = 1) => {\n const appInfo = getAppInfo(app, config.basePath, config.appVersion);\n if (appInfo.paths.length > 0 || attempt >= 10) {\n client.setStartupData(appInfo);\n client.startSync();\n } else {\n setTimeout(() => setStartupData(attempt + 1), 500);\n }\n };\n setTimeout(() => setStartupData(), 500);\n}\n\nfunction getMiddleware(app: Express | Router, client: ApitallyClient) {\n let errorHandlerConfigured = false;\n const logsContext = new AsyncLocalStorage<LogRecord[]>();\n\n if (client.requestLogger.config.captureLogs) {\n patchConsole(logsContext);\n patchWinston(logsContext);\n patchNestLogger(logsContext);\n }\n\n return async (req: Request, res: Response, next: NextFunction) => {\n if (!client.isEnabled() || req.method.toUpperCase() === \"OPTIONS\") {\n next();\n return;\n }\n\n if (!errorHandlerConfigured) {\n // Add error handling middleware to the bottom of the stack when handling the first request\n app.use((err: Error, req: Request, res: Response, next: NextFunction) => {\n res.locals.serverError = err;\n next(err);\n });\n errorHandlerConfigured = true;\n }\n\n if (client.requestLogger.config.captureLogs && \"log\" in req) {\n await patchPinoLogger((req as any).log, logsContext);\n }\n\n logsContext.run([], () => {\n try {\n const startTime = performance.now();\n const originalSend = res.send;\n res.send = (body) => {\n const contentType = res.get(\"content-type\");\n if (client.requestLogger.isSupportedContentType(contentType)) {\n res.locals.body = body;\n }\n return originalSend.call(res, body);\n };\n\n res.once(\"finish\", () => {\n try {\n const responseTime = performance.now() - startTime;\n const path = getRoutePath(req);\n const consumer = getConsumer(req);\n client.consumerRegistry.addOrUpdateConsumer(consumer);\n\n const requestSize = parseContentLength(req.get(\"content-length\"));\n const responseSize = parseContentLength(res.get(\"content-length\"));\n\n if (path) {\n client.requestCounter.addRequest({\n consumer: consumer?.identifier,\n method: req.method,\n path,\n statusCode: res.statusCode,\n responseTime: responseTime,\n requestSize,\n responseSize,\n });\n\n if (\n (res.statusCode === 400 || res.statusCode === 422) &&\n res.locals.body\n ) {\n let jsonBody: any;\n try {\n jsonBody = JSON.parse(res.locals.body);\n } catch {\n // Ignore\n }\n if (jsonBody) {\n const validationErrors: ValidationError[] = [];\n if (validationErrors.length === 0) {\n validationErrors.push(\n ...extractExpressValidatorErrors(jsonBody),\n );\n }\n if (validationErrors.length === 0) {\n validationErrors.push(...extractCelebrateErrors(jsonBody));\n }\n if (validationErrors.length === 0) {\n validationErrors.push(\n ...extractNestValidationErrors(jsonBody),\n );\n }\n validationErrors.forEach((error) => {\n client.validationErrorCounter.addValidationError({\n consumer: consumer?.identifier,\n method: req.method,\n path,\n ...error,\n });\n });\n }\n }\n\n if (res.statusCode === 500 && res.locals.serverError) {\n const serverError = res.locals.serverError as Error;\n client.serverErrorCounter.addServerError({\n consumer: consumer?.identifier,\n method: req.method,\n path,\n type: serverError.name,\n msg: serverError.message,\n traceback: serverError.stack || \"\",\n });\n }\n }\n\n if (client.requestLogger.enabled) {\n const logs = logsContext.getStore();\n client.requestLogger.logRequest(\n {\n timestamp: Date.now() / 1000,\n method: req.method,\n path,\n url: `${req.protocol}://${req.host}${req.originalUrl}`,\n headers: convertHeaders(req.headers),\n size: requestSize,\n consumer: consumer?.identifier,\n body: convertBody(req.body, req.get(\"content-type\")),\n },\n {\n statusCode: res.statusCode,\n responseTime: responseTime / 1000,\n headers: convertHeaders(res.getHeaders()),\n size: responseSize,\n body: convertBody(res.locals.body, res.get(\"content-type\")),\n },\n res.locals.serverError,\n logs,\n );\n }\n } catch (error) {\n client.logger.error(\n \"Error while logging request in Apitally middleware.\",\n { request: req, response: res, error },\n );\n }\n });\n } catch (error) {\n client.logger.error(\"Error in Apitally middleware.\", {\n request: req,\n response: res,\n error,\n });\n } finally {\n next();\n }\n });\n };\n}\n\nfunction getRoutePath(req: Request) {\n if (!req.route) {\n return;\n }\n if (req.baseUrl) {\n const routerInfo = getRouterInfo(req.app);\n if (routerInfo.stack) {\n const routerPath = getRouterPath(routerInfo.stack, req.baseUrl);\n return req.route.path === \"/\" ? routerPath : routerPath + req.route.path;\n }\n }\n return req.route.path;\n}\n\nfunction getRouterPath(stack: any[], baseUrl: string) {\n const routerPaths: string[] = [];\n while (stack && stack.length > 0) {\n const routerLayer = stack.find(\n (layer) =>\n layer.name === \"router\" &&\n layer.path &&\n (baseUrl.startsWith(layer.path) || layer.regexp?.test(baseUrl)),\n );\n if (routerLayer) {\n if (\n routerLayer.regexp &&\n routerLayer.keys &&\n routerLayer.keys.length > 0\n ) {\n const parsedPath = parseExpressPathRegExp(\n routerLayer.regexp,\n routerLayer.keys,\n );\n routerPaths.push(\"/\" + parsedPath);\n } else if (\n routerLayer.params &&\n Object.keys(routerLayer.params).length > 0\n ) {\n const parsedPath = parseExpressPath(\n routerLayer.path,\n routerLayer.params,\n );\n routerPaths.push(parsedPath);\n } else {\n routerPaths.push(routerLayer.path);\n }\n stack = routerLayer.handle?.stack;\n baseUrl = baseUrl.slice(routerLayer.path.length);\n } else {\n break;\n }\n }\n return routerPaths.filter((path) => path !== \"/\").join(\"\");\n}\n\nexport function setConsumer(\n req: Request,\n consumer: ApitallyConsumer | string | null | undefined,\n) {\n req.apitallyConsumer = consumer || undefined;\n}\n\nfunction getConsumer(req: Request) {\n if (req.apitallyConsumer) {\n return consumerFromStringOrObject(req.apitallyConsumer);\n } else if (req.consumerIdentifier) {\n // For backwards compatibility\n process.emitWarning(\n \"The consumerIdentifier property on the request object is deprecated. Use apitallyConsumer instead.\",\n \"DeprecationWarning\",\n );\n return consumerFromStringOrObject(req.consumerIdentifier);\n }\n return null;\n}\n\nfunction extractExpressValidatorErrors(responseBody: any) {\n try {\n const errors: ValidationError[] = [];\n if (\n responseBody &&\n responseBody.errors &&\n Array.isArray(responseBody.errors)\n ) {\n responseBody.errors.forEach((error: any) => {\n if (error.location && error.path && error.msg && error.type) {\n errors.push({\n loc: `${error.location}.${error.path}`,\n msg: error.msg,\n type: error.type,\n });\n }\n });\n }\n return errors;\n } catch (error) {\n return [];\n }\n}\n\nfunction extractCelebrateErrors(responseBody: any) {\n try {\n const errors: ValidationError[] = [];\n if (responseBody && responseBody.validation) {\n Object.values(responseBody.validation).forEach((error: any) => {\n if (\n error.source &&\n error.keys &&\n Array.isArray(error.keys) &&\n error.message\n ) {\n error.keys.forEach((key: string) => {\n errors.push({\n loc: `${error.source}.${key}`,\n msg: subsetJoiMessage(error.message, key),\n type: \"\",\n });\n });\n }\n });\n }\n return errors;\n } catch (error) {\n return [];\n }\n}\n\nfunction extractNestValidationErrors(responseBody: any) {\n try {\n const errors: ValidationError[] = [];\n if (responseBody && Array.isArray(responseBody.message)) {\n responseBody.message.forEach((message: any) => {\n errors.push({\n loc: \"\",\n msg: message,\n type: \"\",\n });\n });\n }\n return errors;\n } catch (error) {\n return [];\n }\n}\n\nfunction subsetJoiMessage(message: string, key: string) {\n const messageWithKey = message\n .split(\". \")\n .find((message) => message.includes(`\"${key}\"`));\n return messageWithKey ? messageWithKey : message;\n}\n\nfunction getAppInfo(\n app: Express | Router,\n basePath?: string,\n appVersion?: string,\n): StartupData {\n const versions: Array<[string, string]> = [\n [\"nodejs\", process.version.replace(/^v/, \"\")],\n ];\n const expressVersion = getPackageVersion(\"express\");\n const nestjsVersion = getPackageVersion(\"@nestjs/core\");\n const apitallyVersion = getPackageVersion(\"../..\");\n if (expressVersion) {\n versions.push([\"express\", expressVersion]);\n }\n if (nestjsVersion) {\n versions.push([\"nestjs\", nestjsVersion]);\n }\n if (apitallyVersion) {\n versions.push([\"apitally\", apitallyVersion]);\n }\n if (appVersion) {\n versions.push([\"app\", appVersion]);\n }\n return {\n paths: getEndpoints(app, basePath || \"\"),\n versions: Object.fromEntries(versions),\n client: \"js:express\",\n };\n}\n"],"mappings":"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","names":["useApitally","app","config","client","ApitallyClient","middleware","getMiddleware","use","setStartupData","attempt","appInfo","getAppInfo","basePath","appVersion","paths","length","startSync","setTimeout","errorHandlerConfigured","logsContext","AsyncLocalStorage","requestLogger","captureLogs","patchConsole","patchWinston","patchNestLogger","req","res","next","isEnabled","method","toUpperCase","err","locals","serverError","patchPinoLogger","log","run","startTime","performance","now","originalSend","send","body","contentType","get","isSupportedContentType","call","once","responseTime","path","getRoutePath","consumer","getConsumer","consumerRegistry","addOrUpdateConsumer","requestSize","parseContentLength","responseSize","requestCounter","addRequest","identifier","statusCode","jsonBody","JSON","parse","validationErrors","push","extractExpressValidatorErrors","extractCelebrateErrors","extractNestValidationErrors","forEach","error","validationErrorCounter","addValidationError","serverErrorCounter","addServerError","type","name","msg","message","traceback","stack","enabled","logs","getStore","logRequest","timestamp","Date","url","protocol","host","originalUrl","headers","convertHeaders","size","convertBody","getHeaders","logger","request","response","route","baseUrl","routerInfo","getRouterInfo","routerPath","getRouterPath","routerPaths","routerLayer","find","layer","_a","startsWith","regexp","test","keys","parsedPath","parseExpressPathRegExp","params","Object","parseExpressPath","handle","slice","filter","join","setConsumer","apitallyConsumer","undefined","consumerFromStringOrObject","consumerIdentifier","process","emitWarning","responseBody","errors","Array","isArray","location","loc","validation","values","source","key","subsetJoiMessage","messageWithKey","split","includes","versions","version","replace","expressVersion","getPackageVersion","nestjsVersion","apitallyVersion","getEndpoints","fromEntries"]}
1
+ {"version":3,"sources":["../../src/express/middleware.ts"],"sourcesContent":["import type { Express, NextFunction, Request, Response, Router } from \"express\";\nimport { AsyncLocalStorage } from \"node:async_hooks\";\nimport { performance } from \"node:perf_hooks\";\n\nimport { ApitallyClient } from \"../common/client.js\";\nimport { consumerFromStringOrObject } from \"../common/consumerRegistry.js\";\nimport { parseContentLength } from \"../common/headers.js\";\nimport { getPackageVersion } from \"../common/packageVersions.js\";\nimport type { LogRecord } from \"../common/requestLogger.js\";\nimport { convertBody, convertHeaders } from \"../common/requestLogger.js\";\nimport {\n ApitallyConfig,\n ApitallyConsumer,\n StartupData,\n ValidationError,\n} from \"../common/types.js\";\nimport {\n patchConsole,\n patchNestLogger,\n patchPinoLogger,\n patchWinston,\n} from \"../loggers/index.js\";\nimport {\n getEndpoints,\n getRouterInfo,\n parseExpressPath,\n parseExpressPathRegExp,\n} from \"./utils.js\";\n\ndeclare module \"express\" {\n interface Request {\n apitallyConsumer?: ApitallyConsumer | string | null;\n consumerIdentifier?: ApitallyConsumer | string | null; // For backwards compatibility\n }\n}\n\nexport function useApitally(\n app: Express | Router,\n config: ApitallyConfig & { basePath?: string },\n) {\n const client = new ApitallyClient(config);\n const middleware = getMiddleware(app, client);\n app.use(middleware);\n\n const setStartupData = (attempt: number = 1) => {\n const appInfo = getAppInfo(app, config.basePath, config.appVersion);\n if (appInfo.paths.length > 0 || attempt >= 10) {\n client.setStartupData(appInfo);\n client.startSync();\n } else {\n setTimeout(() => setStartupData(attempt + 1), 500);\n }\n };\n setTimeout(() => setStartupData(), 500);\n}\n\nfunction getMiddleware(app: Express | Router, client: ApitallyClient) {\n let errorHandlerConfigured = false;\n const logsContext = new AsyncLocalStorage<LogRecord[]>();\n\n if (client.requestLogger.config.captureLogs) {\n patchConsole(logsContext);\n patchWinston(logsContext);\n patchNestLogger(logsContext);\n }\n\n return async (req: Request, res: Response, next: NextFunction) => {\n if (!client.isEnabled() || req.method.toUpperCase() === \"OPTIONS\") {\n next();\n return;\n }\n\n if (!errorHandlerConfigured) {\n // Add error handling middleware to the bottom of the stack when handling the first request\n app.use((err: Error, req: Request, res: Response, next: NextFunction) => {\n res.locals.serverError = err;\n next(err);\n });\n errorHandlerConfigured = true;\n }\n\n if (client.requestLogger.config.captureLogs && \"log\" in req) {\n await patchPinoLogger((req as any).log, logsContext);\n }\n\n logsContext.run([], () => {\n try {\n const startTime = performance.now();\n\n const originalSend = res.send;\n res.send = (body) => {\n const contentType = res.get(\"content-type\");\n if (client.requestLogger.isSupportedContentType(contentType)) {\n res.locals.body = body;\n }\n return originalSend.call(res, body);\n };\n\n const spanHandle = client.spanCollector.startSpan();\n spanHandle.runInContext(() => {\n res.once(\"finish\", () => {\n try {\n const responseTime = performance.now() - startTime;\n const path = getRoutePath(req);\n\n spanHandle.setName(`${req.method} ${path}`);\n const spans = spanHandle.end();\n const traceId = spanHandle.traceId;\n\n const consumer = getConsumer(req);\n client.consumerRegistry.addOrUpdateConsumer(consumer);\n\n const requestSize = parseContentLength(req.get(\"content-length\"));\n const responseSize = parseContentLength(\n res.get(\"content-length\"),\n );\n\n if (path) {\n client.requestCounter.addRequest({\n consumer: consumer?.identifier,\n method: req.method,\n path,\n statusCode: res.statusCode,\n responseTime: responseTime,\n requestSize,\n responseSize,\n });\n\n if (\n (res.statusCode === 400 || res.statusCode === 422) &&\n res.locals.body\n ) {\n let jsonBody: any;\n try {\n jsonBody = JSON.parse(res.locals.body);\n } catch {\n // Ignore\n }\n if (jsonBody) {\n const validationErrors: ValidationError[] = [];\n if (validationErrors.length === 0) {\n validationErrors.push(\n ...extractExpressValidatorErrors(jsonBody),\n );\n }\n if (validationErrors.length === 0) {\n validationErrors.push(\n ...extractCelebrateErrors(jsonBody),\n );\n }\n if (validationErrors.length === 0) {\n validationErrors.push(\n ...extractNestValidationErrors(jsonBody),\n );\n }\n validationErrors.forEach((error) => {\n client.validationErrorCounter.addValidationError({\n consumer: consumer?.identifier,\n method: req.method,\n path,\n ...error,\n });\n });\n }\n }\n\n if (res.statusCode === 500 && res.locals.serverError) {\n const serverError = res.locals.serverError as Error;\n client.serverErrorCounter.addServerError({\n consumer: consumer?.identifier,\n method: req.method,\n path,\n type: serverError.name,\n msg: serverError.message,\n traceback: serverError.stack || \"\",\n });\n }\n }\n\n if (client.requestLogger.enabled) {\n const logs = logsContext.getStore();\n client.requestLogger.logRequest(\n {\n timestamp: Date.now() / 1000,\n method: req.method,\n path,\n url: `${req.protocol}://${req.host}${req.originalUrl}`,\n headers: convertHeaders(req.headers),\n size: requestSize,\n consumer: consumer?.identifier,\n body: convertBody(req.body, req.get(\"content-type\")),\n },\n {\n statusCode: res.statusCode,\n responseTime: responseTime / 1000,\n headers: convertHeaders(res.getHeaders()),\n size: responseSize,\n body: convertBody(res.locals.body, res.get(\"content-type\")),\n },\n res.locals.serverError,\n logs,\n spans,\n traceId,\n );\n }\n } catch (error) {\n client.logger.error(\n \"Error while logging request in Apitally middleware\",\n { request: req, response: res, error },\n );\n }\n });\n\n res.once(\"close\", () => {\n spanHandle.end();\n });\n\n next();\n });\n } catch (error) {\n client.logger.error(\"Error in Apitally middleware\", {\n request: req,\n response: res,\n error,\n });\n }\n });\n };\n}\n\nfunction getRoutePath(req: Request) {\n if (!req.route) {\n return;\n }\n if (req.baseUrl) {\n const routerInfo = getRouterInfo(req.app);\n if (routerInfo.stack) {\n const routerPath = getRouterPath(routerInfo.stack, req.baseUrl);\n return req.route.path === \"/\" ? routerPath : routerPath + req.route.path;\n }\n }\n return req.route.path;\n}\n\nfunction getRouterPath(stack: any[], baseUrl: string) {\n const routerPaths: string[] = [];\n while (stack && stack.length > 0) {\n const routerLayer = stack.find(\n (layer) =>\n layer.name === \"router\" &&\n layer.path &&\n (baseUrl.startsWith(layer.path) || layer.regexp?.test(baseUrl)),\n );\n if (routerLayer) {\n if (\n routerLayer.regexp &&\n routerLayer.keys &&\n routerLayer.keys.length > 0\n ) {\n const parsedPath = parseExpressPathRegExp(\n routerLayer.regexp,\n routerLayer.keys,\n );\n routerPaths.push(\"/\" + parsedPath);\n } else if (\n routerLayer.params &&\n Object.keys(routerLayer.params).length > 0\n ) {\n const parsedPath = parseExpressPath(\n routerLayer.path,\n routerLayer.params,\n );\n routerPaths.push(parsedPath);\n } else {\n routerPaths.push(routerLayer.path);\n }\n stack = routerLayer.handle?.stack;\n baseUrl = baseUrl.slice(routerLayer.path.length);\n } else {\n break;\n }\n }\n return routerPaths.filter((path) => path !== \"/\").join(\"\");\n}\n\nexport function setConsumer(\n req: Request,\n consumer: ApitallyConsumer | string | null | undefined,\n) {\n req.apitallyConsumer = consumer || undefined;\n}\n\nfunction getConsumer(req: Request) {\n if (req.apitallyConsumer) {\n return consumerFromStringOrObject(req.apitallyConsumer);\n } else if (req.consumerIdentifier) {\n // For backwards compatibility\n process.emitWarning(\n \"The consumerIdentifier property on the request object is deprecated. Use apitallyConsumer instead.\",\n \"DeprecationWarning\",\n );\n return consumerFromStringOrObject(req.consumerIdentifier);\n }\n return null;\n}\n\nfunction extractExpressValidatorErrors(responseBody: any) {\n try {\n const errors: ValidationError[] = [];\n if (\n responseBody &&\n responseBody.errors &&\n Array.isArray(responseBody.errors)\n ) {\n responseBody.errors.forEach((error: any) => {\n if (error.location && error.path && error.msg && error.type) {\n errors.push({\n loc: `${error.location}.${error.path}`,\n msg: error.msg,\n type: error.type,\n });\n }\n });\n }\n return errors;\n } catch (error) {\n return [];\n }\n}\n\nfunction extractCelebrateErrors(responseBody: any) {\n try {\n const errors: ValidationError[] = [];\n if (responseBody && responseBody.validation) {\n Object.values(responseBody.validation).forEach((error: any) => {\n if (\n error.source &&\n error.keys &&\n Array.isArray(error.keys) &&\n error.message\n ) {\n error.keys.forEach((key: string) => {\n errors.push({\n loc: `${error.source}.${key}`,\n msg: subsetJoiMessage(error.message, key),\n type: \"\",\n });\n });\n }\n });\n }\n return errors;\n } catch (error) {\n return [];\n }\n}\n\nfunction extractNestValidationErrors(responseBody: any) {\n try {\n const errors: ValidationError[] = [];\n if (responseBody && Array.isArray(responseBody.message)) {\n responseBody.message.forEach((message: any) => {\n errors.push({\n loc: \"\",\n msg: message,\n type: \"\",\n });\n });\n }\n return errors;\n } catch (error) {\n return [];\n }\n}\n\nfunction subsetJoiMessage(message: string, key: string) {\n const messageWithKey = message\n .split(\". \")\n .find((message) => message.includes(`\"${key}\"`));\n return messageWithKey ? messageWithKey : message;\n}\n\nfunction getAppInfo(\n app: Express | Router,\n basePath?: string,\n appVersion?: string,\n): StartupData {\n const versions: Array<[string, string]> = [\n [\"nodejs\", process.version.replace(/^v/, \"\")],\n ];\n const expressVersion = getPackageVersion(\"express\");\n const nestjsVersion = getPackageVersion(\"@nestjs/core\");\n const apitallyVersion = getPackageVersion(\"../..\");\n if (expressVersion) {\n versions.push([\"express\", expressVersion]);\n }\n if (nestjsVersion) {\n versions.push([\"nestjs\", nestjsVersion]);\n }\n if (apitallyVersion) {\n versions.push([\"apitally\", apitallyVersion]);\n }\n if (appVersion) {\n versions.push([\"app\", appVersion]);\n }\n return {\n paths: getEndpoints(app, basePath || \"\"),\n versions: Object.fromEntries(versions),\n client: \"js:express\",\n };\n}\n"],"mappings":"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","names":["useApitally","app","config","client","ApitallyClient","middleware","getMiddleware","use","setStartupData","attempt","appInfo","getAppInfo","basePath","appVersion","paths","length","startSync","setTimeout","errorHandlerConfigured","logsContext","AsyncLocalStorage","requestLogger","captureLogs","patchConsole","patchWinston","patchNestLogger","req","res","next","isEnabled","method","toUpperCase","err","locals","serverError","patchPinoLogger","log","run","startTime","performance","now","originalSend","send","body","contentType","get","isSupportedContentType","call","spanHandle","spanCollector","startSpan","runInContext","once","responseTime","path","getRoutePath","setName","spans","end","traceId","consumer","getConsumer","consumerRegistry","addOrUpdateConsumer","requestSize","parseContentLength","responseSize","requestCounter","addRequest","identifier","statusCode","jsonBody","JSON","parse","validationErrors","push","extractExpressValidatorErrors","extractCelebrateErrors","extractNestValidationErrors","forEach","error","validationErrorCounter","addValidationError","serverErrorCounter","addServerError","type","name","msg","message","traceback","stack","enabled","logs","getStore","logRequest","timestamp","Date","url","protocol","host","originalUrl","headers","convertHeaders","size","convertBody","getHeaders","logger","request","response","route","baseUrl","routerInfo","getRouterInfo","routerPath","getRouterPath","routerPaths","routerLayer","find","layer","_a","startsWith","regexp","test","keys","parsedPath","parseExpressPathRegExp","params","Object","parseExpressPath","handle","slice","filter","join","setConsumer","apitallyConsumer","undefined","consumerFromStringOrObject","consumerIdentifier","process","emitWarning","responseBody","errors","Array","isArray","location","loc","validation","values","source","key","subsetJoiMessage","messageWithKey","split","includes","versions","version","replace","expressVersion","getPackageVersion","nestjsVersion","apitallyVersion","getEndpoints","fromEntries"]}
@@ -1,5 +1,7 @@
1
+ import '../common/spanCollector.cjs';
1
2
  import { ApitallyConsumer, ApitallyConfig } from '../common/types.cjs';
2
3
  import { Request, Express, Router } from 'express';
4
+ import '@opentelemetry/sdk-trace-base';
3
5
  import '../common/logging.cjs';
4
6
  import 'winston';
5
7
  import '../common/requestLogger.cjs';
@@ -1,5 +1,7 @@
1
+ import '../common/spanCollector.js';
1
2
  import { ApitallyConsumer, ApitallyConfig } from '../common/types.js';
2
3
  import { Request, Express, Router } from 'express';
4
+ import '@opentelemetry/sdk-trace-base';
3
5
  import '../common/logging.js';
4
6
  import 'winston';
5
7
  import '../common/requestLogger.js';
@@ -59,98 +59,106 @@ function getMiddleware(app, client) {
59
59
  }
60
60
  return originalSend.call(res, body);
61
61
  };
62
- res.once("finish", () => {
63
- try {
64
- const responseTime = performance.now() - startTime;
65
- const path = getRoutePath(req);
66
- const consumer = getConsumer(req);
67
- client.consumerRegistry.addOrUpdateConsumer(consumer);
68
- const requestSize = parseContentLength(req.get("content-length"));
69
- const responseSize = parseContentLength(res.get("content-length"));
70
- if (path) {
71
- client.requestCounter.addRequest({
72
- consumer: consumer == null ? void 0 : consumer.identifier,
73
- method: req.method,
74
- path,
75
- statusCode: res.statusCode,
76
- responseTime,
77
- requestSize,
78
- responseSize
79
- });
80
- if ((res.statusCode === 400 || res.statusCode === 422) && res.locals.body) {
81
- let jsonBody;
82
- try {
83
- jsonBody = JSON.parse(res.locals.body);
84
- } catch {
85
- }
86
- if (jsonBody) {
87
- const validationErrors = [];
88
- if (validationErrors.length === 0) {
89
- validationErrors.push(...extractExpressValidatorErrors(jsonBody));
90
- }
91
- if (validationErrors.length === 0) {
92
- validationErrors.push(...extractCelebrateErrors(jsonBody));
93
- }
94
- if (validationErrors.length === 0) {
95
- validationErrors.push(...extractNestValidationErrors(jsonBody));
62
+ const spanHandle = client.spanCollector.startSpan();
63
+ spanHandle.runInContext(() => {
64
+ res.once("finish", () => {
65
+ try {
66
+ const responseTime = performance.now() - startTime;
67
+ const path = getRoutePath(req);
68
+ spanHandle.setName(`${req.method} ${path}`);
69
+ const spans = spanHandle.end();
70
+ const traceId = spanHandle.traceId;
71
+ const consumer = getConsumer(req);
72
+ client.consumerRegistry.addOrUpdateConsumer(consumer);
73
+ const requestSize = parseContentLength(req.get("content-length"));
74
+ const responseSize = parseContentLength(res.get("content-length"));
75
+ if (path) {
76
+ client.requestCounter.addRequest({
77
+ consumer: consumer == null ? void 0 : consumer.identifier,
78
+ method: req.method,
79
+ path,
80
+ statusCode: res.statusCode,
81
+ responseTime,
82
+ requestSize,
83
+ responseSize
84
+ });
85
+ if ((res.statusCode === 400 || res.statusCode === 422) && res.locals.body) {
86
+ let jsonBody;
87
+ try {
88
+ jsonBody = JSON.parse(res.locals.body);
89
+ } catch {
96
90
  }
97
- validationErrors.forEach((error) => {
98
- client.validationErrorCounter.addValidationError({
99
- consumer: consumer == null ? void 0 : consumer.identifier,
100
- method: req.method,
101
- path,
102
- ...error
91
+ if (jsonBody) {
92
+ const validationErrors = [];
93
+ if (validationErrors.length === 0) {
94
+ validationErrors.push(...extractExpressValidatorErrors(jsonBody));
95
+ }
96
+ if (validationErrors.length === 0) {
97
+ validationErrors.push(...extractCelebrateErrors(jsonBody));
98
+ }
99
+ if (validationErrors.length === 0) {
100
+ validationErrors.push(...extractNestValidationErrors(jsonBody));
101
+ }
102
+ validationErrors.forEach((error) => {
103
+ client.validationErrorCounter.addValidationError({
104
+ consumer: consumer == null ? void 0 : consumer.identifier,
105
+ method: req.method,
106
+ path,
107
+ ...error
108
+ });
103
109
  });
110
+ }
111
+ }
112
+ if (res.statusCode === 500 && res.locals.serverError) {
113
+ const serverError = res.locals.serverError;
114
+ client.serverErrorCounter.addServerError({
115
+ consumer: consumer == null ? void 0 : consumer.identifier,
116
+ method: req.method,
117
+ path,
118
+ type: serverError.name,
119
+ msg: serverError.message,
120
+ traceback: serverError.stack || ""
104
121
  });
105
122
  }
106
123
  }
107
- if (res.statusCode === 500 && res.locals.serverError) {
108
- const serverError = res.locals.serverError;
109
- client.serverErrorCounter.addServerError({
110
- consumer: consumer == null ? void 0 : consumer.identifier,
124
+ if (client.requestLogger.enabled) {
125
+ const logs = logsContext.getStore();
126
+ client.requestLogger.logRequest({
127
+ timestamp: Date.now() / 1e3,
111
128
  method: req.method,
112
129
  path,
113
- type: serverError.name,
114
- msg: serverError.message,
115
- traceback: serverError.stack || ""
116
- });
130
+ url: `${req.protocol}://${req.host}${req.originalUrl}`,
131
+ headers: convertHeaders(req.headers),
132
+ size: requestSize,
133
+ consumer: consumer == null ? void 0 : consumer.identifier,
134
+ body: convertBody(req.body, req.get("content-type"))
135
+ }, {
136
+ statusCode: res.statusCode,
137
+ responseTime: responseTime / 1e3,
138
+ headers: convertHeaders(res.getHeaders()),
139
+ size: responseSize,
140
+ body: convertBody(res.locals.body, res.get("content-type"))
141
+ }, res.locals.serverError, logs, spans, traceId);
117
142
  }
143
+ } catch (error) {
144
+ client.logger.error("Error while logging request in Apitally middleware", {
145
+ request: req,
146
+ response: res,
147
+ error
148
+ });
118
149
  }
119
- if (client.requestLogger.enabled) {
120
- const logs = logsContext.getStore();
121
- client.requestLogger.logRequest({
122
- timestamp: Date.now() / 1e3,
123
- method: req.method,
124
- path,
125
- url: `${req.protocol}://${req.host}${req.originalUrl}`,
126
- headers: convertHeaders(req.headers),
127
- size: requestSize,
128
- consumer: consumer == null ? void 0 : consumer.identifier,
129
- body: convertBody(req.body, req.get("content-type"))
130
- }, {
131
- statusCode: res.statusCode,
132
- responseTime: responseTime / 1e3,
133
- headers: convertHeaders(res.getHeaders()),
134
- size: responseSize,
135
- body: convertBody(res.locals.body, res.get("content-type"))
136
- }, res.locals.serverError, logs);
137
- }
138
- } catch (error) {
139
- client.logger.error("Error while logging request in Apitally middleware.", {
140
- request: req,
141
- response: res,
142
- error
143
- });
144
- }
150
+ });
151
+ res.once("close", () => {
152
+ spanHandle.end();
153
+ });
154
+ next();
145
155
  });
146
156
  } catch (error) {
147
- client.logger.error("Error in Apitally middleware.", {
157
+ client.logger.error("Error in Apitally middleware", {
148
158
  request: req,
149
159
  response: res,
150
160
  error
151
161
  });
152
- } finally {
153
- next();
154
162
  }
155
163
  });
156
164
  };
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/express/middleware.ts"],"sourcesContent":["import type { Express, NextFunction, Request, Response, Router } from \"express\";\nimport { AsyncLocalStorage } from \"node:async_hooks\";\nimport { performance } from \"node:perf_hooks\";\n\nimport { ApitallyClient } from \"../common/client.js\";\nimport { consumerFromStringOrObject } from \"../common/consumerRegistry.js\";\nimport { parseContentLength } from \"../common/headers.js\";\nimport { getPackageVersion } from \"../common/packageVersions.js\";\nimport type { LogRecord } from \"../common/requestLogger.js\";\nimport { convertBody, convertHeaders } from \"../common/requestLogger.js\";\nimport {\n ApitallyConfig,\n ApitallyConsumer,\n StartupData,\n ValidationError,\n} from \"../common/types.js\";\nimport {\n patchConsole,\n patchNestLogger,\n patchPinoLogger,\n patchWinston,\n} from \"../loggers/index.js\";\nimport {\n getEndpoints,\n getRouterInfo,\n parseExpressPath,\n parseExpressPathRegExp,\n} from \"./utils.js\";\n\ndeclare module \"express\" {\n interface Request {\n apitallyConsumer?: ApitallyConsumer | string | null;\n consumerIdentifier?: ApitallyConsumer | string | null; // For backwards compatibility\n }\n}\n\nexport function useApitally(\n app: Express | Router,\n config: ApitallyConfig & { basePath?: string },\n) {\n const client = new ApitallyClient(config);\n const middleware = getMiddleware(app, client);\n app.use(middleware);\n\n const setStartupData = (attempt: number = 1) => {\n const appInfo = getAppInfo(app, config.basePath, config.appVersion);\n if (appInfo.paths.length > 0 || attempt >= 10) {\n client.setStartupData(appInfo);\n client.startSync();\n } else {\n setTimeout(() => setStartupData(attempt + 1), 500);\n }\n };\n setTimeout(() => setStartupData(), 500);\n}\n\nfunction getMiddleware(app: Express | Router, client: ApitallyClient) {\n let errorHandlerConfigured = false;\n const logsContext = new AsyncLocalStorage<LogRecord[]>();\n\n if (client.requestLogger.config.captureLogs) {\n patchConsole(logsContext);\n patchWinston(logsContext);\n patchNestLogger(logsContext);\n }\n\n return async (req: Request, res: Response, next: NextFunction) => {\n if (!client.isEnabled() || req.method.toUpperCase() === \"OPTIONS\") {\n next();\n return;\n }\n\n if (!errorHandlerConfigured) {\n // Add error handling middleware to the bottom of the stack when handling the first request\n app.use((err: Error, req: Request, res: Response, next: NextFunction) => {\n res.locals.serverError = err;\n next(err);\n });\n errorHandlerConfigured = true;\n }\n\n if (client.requestLogger.config.captureLogs && \"log\" in req) {\n await patchPinoLogger((req as any).log, logsContext);\n }\n\n logsContext.run([], () => {\n try {\n const startTime = performance.now();\n const originalSend = res.send;\n res.send = (body) => {\n const contentType = res.get(\"content-type\");\n if (client.requestLogger.isSupportedContentType(contentType)) {\n res.locals.body = body;\n }\n return originalSend.call(res, body);\n };\n\n res.once(\"finish\", () => {\n try {\n const responseTime = performance.now() - startTime;\n const path = getRoutePath(req);\n const consumer = getConsumer(req);\n client.consumerRegistry.addOrUpdateConsumer(consumer);\n\n const requestSize = parseContentLength(req.get(\"content-length\"));\n const responseSize = parseContentLength(res.get(\"content-length\"));\n\n if (path) {\n client.requestCounter.addRequest({\n consumer: consumer?.identifier,\n method: req.method,\n path,\n statusCode: res.statusCode,\n responseTime: responseTime,\n requestSize,\n responseSize,\n });\n\n if (\n (res.statusCode === 400 || res.statusCode === 422) &&\n res.locals.body\n ) {\n let jsonBody: any;\n try {\n jsonBody = JSON.parse(res.locals.body);\n } catch {\n // Ignore\n }\n if (jsonBody) {\n const validationErrors: ValidationError[] = [];\n if (validationErrors.length === 0) {\n validationErrors.push(\n ...extractExpressValidatorErrors(jsonBody),\n );\n }\n if (validationErrors.length === 0) {\n validationErrors.push(...extractCelebrateErrors(jsonBody));\n }\n if (validationErrors.length === 0) {\n validationErrors.push(\n ...extractNestValidationErrors(jsonBody),\n );\n }\n validationErrors.forEach((error) => {\n client.validationErrorCounter.addValidationError({\n consumer: consumer?.identifier,\n method: req.method,\n path,\n ...error,\n });\n });\n }\n }\n\n if (res.statusCode === 500 && res.locals.serverError) {\n const serverError = res.locals.serverError as Error;\n client.serverErrorCounter.addServerError({\n consumer: consumer?.identifier,\n method: req.method,\n path,\n type: serverError.name,\n msg: serverError.message,\n traceback: serverError.stack || \"\",\n });\n }\n }\n\n if (client.requestLogger.enabled) {\n const logs = logsContext.getStore();\n client.requestLogger.logRequest(\n {\n timestamp: Date.now() / 1000,\n method: req.method,\n path,\n url: `${req.protocol}://${req.host}${req.originalUrl}`,\n headers: convertHeaders(req.headers),\n size: requestSize,\n consumer: consumer?.identifier,\n body: convertBody(req.body, req.get(\"content-type\")),\n },\n {\n statusCode: res.statusCode,\n responseTime: responseTime / 1000,\n headers: convertHeaders(res.getHeaders()),\n size: responseSize,\n body: convertBody(res.locals.body, res.get(\"content-type\")),\n },\n res.locals.serverError,\n logs,\n );\n }\n } catch (error) {\n client.logger.error(\n \"Error while logging request in Apitally middleware.\",\n { request: req, response: res, error },\n );\n }\n });\n } catch (error) {\n client.logger.error(\"Error in Apitally middleware.\", {\n request: req,\n response: res,\n error,\n });\n } finally {\n next();\n }\n });\n };\n}\n\nfunction getRoutePath(req: Request) {\n if (!req.route) {\n return;\n }\n if (req.baseUrl) {\n const routerInfo = getRouterInfo(req.app);\n if (routerInfo.stack) {\n const routerPath = getRouterPath(routerInfo.stack, req.baseUrl);\n return req.route.path === \"/\" ? routerPath : routerPath + req.route.path;\n }\n }\n return req.route.path;\n}\n\nfunction getRouterPath(stack: any[], baseUrl: string) {\n const routerPaths: string[] = [];\n while (stack && stack.length > 0) {\n const routerLayer = stack.find(\n (layer) =>\n layer.name === \"router\" &&\n layer.path &&\n (baseUrl.startsWith(layer.path) || layer.regexp?.test(baseUrl)),\n );\n if (routerLayer) {\n if (\n routerLayer.regexp &&\n routerLayer.keys &&\n routerLayer.keys.length > 0\n ) {\n const parsedPath = parseExpressPathRegExp(\n routerLayer.regexp,\n routerLayer.keys,\n );\n routerPaths.push(\"/\" + parsedPath);\n } else if (\n routerLayer.params &&\n Object.keys(routerLayer.params).length > 0\n ) {\n const parsedPath = parseExpressPath(\n routerLayer.path,\n routerLayer.params,\n );\n routerPaths.push(parsedPath);\n } else {\n routerPaths.push(routerLayer.path);\n }\n stack = routerLayer.handle?.stack;\n baseUrl = baseUrl.slice(routerLayer.path.length);\n } else {\n break;\n }\n }\n return routerPaths.filter((path) => path !== \"/\").join(\"\");\n}\n\nexport function setConsumer(\n req: Request,\n consumer: ApitallyConsumer | string | null | undefined,\n) {\n req.apitallyConsumer = consumer || undefined;\n}\n\nfunction getConsumer(req: Request) {\n if (req.apitallyConsumer) {\n return consumerFromStringOrObject(req.apitallyConsumer);\n } else if (req.consumerIdentifier) {\n // For backwards compatibility\n process.emitWarning(\n \"The consumerIdentifier property on the request object is deprecated. Use apitallyConsumer instead.\",\n \"DeprecationWarning\",\n );\n return consumerFromStringOrObject(req.consumerIdentifier);\n }\n return null;\n}\n\nfunction extractExpressValidatorErrors(responseBody: any) {\n try {\n const errors: ValidationError[] = [];\n if (\n responseBody &&\n responseBody.errors &&\n Array.isArray(responseBody.errors)\n ) {\n responseBody.errors.forEach((error: any) => {\n if (error.location && error.path && error.msg && error.type) {\n errors.push({\n loc: `${error.location}.${error.path}`,\n msg: error.msg,\n type: error.type,\n });\n }\n });\n }\n return errors;\n } catch (error) {\n return [];\n }\n}\n\nfunction extractCelebrateErrors(responseBody: any) {\n try {\n const errors: ValidationError[] = [];\n if (responseBody && responseBody.validation) {\n Object.values(responseBody.validation).forEach((error: any) => {\n if (\n error.source &&\n error.keys &&\n Array.isArray(error.keys) &&\n error.message\n ) {\n error.keys.forEach((key: string) => {\n errors.push({\n loc: `${error.source}.${key}`,\n msg: subsetJoiMessage(error.message, key),\n type: \"\",\n });\n });\n }\n });\n }\n return errors;\n } catch (error) {\n return [];\n }\n}\n\nfunction extractNestValidationErrors(responseBody: any) {\n try {\n const errors: ValidationError[] = [];\n if (responseBody && Array.isArray(responseBody.message)) {\n responseBody.message.forEach((message: any) => {\n errors.push({\n loc: \"\",\n msg: message,\n type: \"\",\n });\n });\n }\n return errors;\n } catch (error) {\n return [];\n }\n}\n\nfunction subsetJoiMessage(message: string, key: string) {\n const messageWithKey = message\n .split(\". \")\n .find((message) => message.includes(`\"${key}\"`));\n return messageWithKey ? messageWithKey : message;\n}\n\nfunction getAppInfo(\n app: Express | Router,\n basePath?: string,\n appVersion?: string,\n): StartupData {\n const versions: Array<[string, string]> = [\n [\"nodejs\", process.version.replace(/^v/, \"\")],\n ];\n const expressVersion = getPackageVersion(\"express\");\n const nestjsVersion = getPackageVersion(\"@nestjs/core\");\n const apitallyVersion = getPackageVersion(\"../..\");\n if (expressVersion) {\n versions.push([\"express\", expressVersion]);\n }\n if (nestjsVersion) {\n versions.push([\"nestjs\", nestjsVersion]);\n }\n if (apitallyVersion) {\n versions.push([\"apitally\", apitallyVersion]);\n }\n if (appVersion) {\n versions.push([\"app\", appVersion]);\n }\n return {\n paths: getEndpoints(app, basePath || \"\"),\n versions: Object.fromEntries(versions),\n client: \"js:express\",\n };\n}\n"],"mappings":"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","names":["AsyncLocalStorage","performance","ApitallyClient","consumerFromStringOrObject","parseContentLength","getPackageVersion","convertBody","convertHeaders","patchConsole","patchNestLogger","patchPinoLogger","patchWinston","getEndpoints","getRouterInfo","parseExpressPath","parseExpressPathRegExp","useApitally","app","config","client","middleware","getMiddleware","use","setStartupData","attempt","appInfo","getAppInfo","basePath","appVersion","paths","length","startSync","setTimeout","errorHandlerConfigured","logsContext","requestLogger","captureLogs","req","res","next","isEnabled","method","toUpperCase","err","locals","serverError","log","run","startTime","now","originalSend","send","body","contentType","get","isSupportedContentType","call","once","responseTime","path","getRoutePath","consumer","getConsumer","consumerRegistry","addOrUpdateConsumer","requestSize","responseSize","requestCounter","addRequest","identifier","statusCode","jsonBody","JSON","parse","validationErrors","push","extractExpressValidatorErrors","extractCelebrateErrors","extractNestValidationErrors","forEach","error","validationErrorCounter","addValidationError","serverErrorCounter","addServerError","type","name","msg","message","traceback","stack","enabled","logs","getStore","logRequest","timestamp","Date","url","protocol","host","originalUrl","headers","size","getHeaders","logger","request","response","route","baseUrl","routerInfo","routerPath","getRouterPath","routerPaths","routerLayer","find","layer","_a","startsWith","regexp","test","keys","parsedPath","params","Object","handle","slice","filter","join","setConsumer","apitallyConsumer","undefined","consumerIdentifier","process","emitWarning","responseBody","errors","Array","isArray","location","loc","validation","values","source","key","subsetJoiMessage","messageWithKey","split","includes","versions","version","replace","expressVersion","nestjsVersion","apitallyVersion","fromEntries"]}
1
+ {"version":3,"sources":["../../src/express/middleware.ts"],"sourcesContent":["import type { Express, NextFunction, Request, Response, Router } from \"express\";\nimport { AsyncLocalStorage } from \"node:async_hooks\";\nimport { performance } from \"node:perf_hooks\";\n\nimport { ApitallyClient } from \"../common/client.js\";\nimport { consumerFromStringOrObject } from \"../common/consumerRegistry.js\";\nimport { parseContentLength } from \"../common/headers.js\";\nimport { getPackageVersion } from \"../common/packageVersions.js\";\nimport type { LogRecord } from \"../common/requestLogger.js\";\nimport { convertBody, convertHeaders } from \"../common/requestLogger.js\";\nimport {\n ApitallyConfig,\n ApitallyConsumer,\n StartupData,\n ValidationError,\n} from \"../common/types.js\";\nimport {\n patchConsole,\n patchNestLogger,\n patchPinoLogger,\n patchWinston,\n} from \"../loggers/index.js\";\nimport {\n getEndpoints,\n getRouterInfo,\n parseExpressPath,\n parseExpressPathRegExp,\n} from \"./utils.js\";\n\ndeclare module \"express\" {\n interface Request {\n apitallyConsumer?: ApitallyConsumer | string | null;\n consumerIdentifier?: ApitallyConsumer | string | null; // For backwards compatibility\n }\n}\n\nexport function useApitally(\n app: Express | Router,\n config: ApitallyConfig & { basePath?: string },\n) {\n const client = new ApitallyClient(config);\n const middleware = getMiddleware(app, client);\n app.use(middleware);\n\n const setStartupData = (attempt: number = 1) => {\n const appInfo = getAppInfo(app, config.basePath, config.appVersion);\n if (appInfo.paths.length > 0 || attempt >= 10) {\n client.setStartupData(appInfo);\n client.startSync();\n } else {\n setTimeout(() => setStartupData(attempt + 1), 500);\n }\n };\n setTimeout(() => setStartupData(), 500);\n}\n\nfunction getMiddleware(app: Express | Router, client: ApitallyClient) {\n let errorHandlerConfigured = false;\n const logsContext = new AsyncLocalStorage<LogRecord[]>();\n\n if (client.requestLogger.config.captureLogs) {\n patchConsole(logsContext);\n patchWinston(logsContext);\n patchNestLogger(logsContext);\n }\n\n return async (req: Request, res: Response, next: NextFunction) => {\n if (!client.isEnabled() || req.method.toUpperCase() === \"OPTIONS\") {\n next();\n return;\n }\n\n if (!errorHandlerConfigured) {\n // Add error handling middleware to the bottom of the stack when handling the first request\n app.use((err: Error, req: Request, res: Response, next: NextFunction) => {\n res.locals.serverError = err;\n next(err);\n });\n errorHandlerConfigured = true;\n }\n\n if (client.requestLogger.config.captureLogs && \"log\" in req) {\n await patchPinoLogger((req as any).log, logsContext);\n }\n\n logsContext.run([], () => {\n try {\n const startTime = performance.now();\n\n const originalSend = res.send;\n res.send = (body) => {\n const contentType = res.get(\"content-type\");\n if (client.requestLogger.isSupportedContentType(contentType)) {\n res.locals.body = body;\n }\n return originalSend.call(res, body);\n };\n\n const spanHandle = client.spanCollector.startSpan();\n spanHandle.runInContext(() => {\n res.once(\"finish\", () => {\n try {\n const responseTime = performance.now() - startTime;\n const path = getRoutePath(req);\n\n spanHandle.setName(`${req.method} ${path}`);\n const spans = spanHandle.end();\n const traceId = spanHandle.traceId;\n\n const consumer = getConsumer(req);\n client.consumerRegistry.addOrUpdateConsumer(consumer);\n\n const requestSize = parseContentLength(req.get(\"content-length\"));\n const responseSize = parseContentLength(\n res.get(\"content-length\"),\n );\n\n if (path) {\n client.requestCounter.addRequest({\n consumer: consumer?.identifier,\n method: req.method,\n path,\n statusCode: res.statusCode,\n responseTime: responseTime,\n requestSize,\n responseSize,\n });\n\n if (\n (res.statusCode === 400 || res.statusCode === 422) &&\n res.locals.body\n ) {\n let jsonBody: any;\n try {\n jsonBody = JSON.parse(res.locals.body);\n } catch {\n // Ignore\n }\n if (jsonBody) {\n const validationErrors: ValidationError[] = [];\n if (validationErrors.length === 0) {\n validationErrors.push(\n ...extractExpressValidatorErrors(jsonBody),\n );\n }\n if (validationErrors.length === 0) {\n validationErrors.push(\n ...extractCelebrateErrors(jsonBody),\n );\n }\n if (validationErrors.length === 0) {\n validationErrors.push(\n ...extractNestValidationErrors(jsonBody),\n );\n }\n validationErrors.forEach((error) => {\n client.validationErrorCounter.addValidationError({\n consumer: consumer?.identifier,\n method: req.method,\n path,\n ...error,\n });\n });\n }\n }\n\n if (res.statusCode === 500 && res.locals.serverError) {\n const serverError = res.locals.serverError as Error;\n client.serverErrorCounter.addServerError({\n consumer: consumer?.identifier,\n method: req.method,\n path,\n type: serverError.name,\n msg: serverError.message,\n traceback: serverError.stack || \"\",\n });\n }\n }\n\n if (client.requestLogger.enabled) {\n const logs = logsContext.getStore();\n client.requestLogger.logRequest(\n {\n timestamp: Date.now() / 1000,\n method: req.method,\n path,\n url: `${req.protocol}://${req.host}${req.originalUrl}`,\n headers: convertHeaders(req.headers),\n size: requestSize,\n consumer: consumer?.identifier,\n body: convertBody(req.body, req.get(\"content-type\")),\n },\n {\n statusCode: res.statusCode,\n responseTime: responseTime / 1000,\n headers: convertHeaders(res.getHeaders()),\n size: responseSize,\n body: convertBody(res.locals.body, res.get(\"content-type\")),\n },\n res.locals.serverError,\n logs,\n spans,\n traceId,\n );\n }\n } catch (error) {\n client.logger.error(\n \"Error while logging request in Apitally middleware\",\n { request: req, response: res, error },\n );\n }\n });\n\n res.once(\"close\", () => {\n spanHandle.end();\n });\n\n next();\n });\n } catch (error) {\n client.logger.error(\"Error in Apitally middleware\", {\n request: req,\n response: res,\n error,\n });\n }\n });\n };\n}\n\nfunction getRoutePath(req: Request) {\n if (!req.route) {\n return;\n }\n if (req.baseUrl) {\n const routerInfo = getRouterInfo(req.app);\n if (routerInfo.stack) {\n const routerPath = getRouterPath(routerInfo.stack, req.baseUrl);\n return req.route.path === \"/\" ? routerPath : routerPath + req.route.path;\n }\n }\n return req.route.path;\n}\n\nfunction getRouterPath(stack: any[], baseUrl: string) {\n const routerPaths: string[] = [];\n while (stack && stack.length > 0) {\n const routerLayer = stack.find(\n (layer) =>\n layer.name === \"router\" &&\n layer.path &&\n (baseUrl.startsWith(layer.path) || layer.regexp?.test(baseUrl)),\n );\n if (routerLayer) {\n if (\n routerLayer.regexp &&\n routerLayer.keys &&\n routerLayer.keys.length > 0\n ) {\n const parsedPath = parseExpressPathRegExp(\n routerLayer.regexp,\n routerLayer.keys,\n );\n routerPaths.push(\"/\" + parsedPath);\n } else if (\n routerLayer.params &&\n Object.keys(routerLayer.params).length > 0\n ) {\n const parsedPath = parseExpressPath(\n routerLayer.path,\n routerLayer.params,\n );\n routerPaths.push(parsedPath);\n } else {\n routerPaths.push(routerLayer.path);\n }\n stack = routerLayer.handle?.stack;\n baseUrl = baseUrl.slice(routerLayer.path.length);\n } else {\n break;\n }\n }\n return routerPaths.filter((path) => path !== \"/\").join(\"\");\n}\n\nexport function setConsumer(\n req: Request,\n consumer: ApitallyConsumer | string | null | undefined,\n) {\n req.apitallyConsumer = consumer || undefined;\n}\n\nfunction getConsumer(req: Request) {\n if (req.apitallyConsumer) {\n return consumerFromStringOrObject(req.apitallyConsumer);\n } else if (req.consumerIdentifier) {\n // For backwards compatibility\n process.emitWarning(\n \"The consumerIdentifier property on the request object is deprecated. Use apitallyConsumer instead.\",\n \"DeprecationWarning\",\n );\n return consumerFromStringOrObject(req.consumerIdentifier);\n }\n return null;\n}\n\nfunction extractExpressValidatorErrors(responseBody: any) {\n try {\n const errors: ValidationError[] = [];\n if (\n responseBody &&\n responseBody.errors &&\n Array.isArray(responseBody.errors)\n ) {\n responseBody.errors.forEach((error: any) => {\n if (error.location && error.path && error.msg && error.type) {\n errors.push({\n loc: `${error.location}.${error.path}`,\n msg: error.msg,\n type: error.type,\n });\n }\n });\n }\n return errors;\n } catch (error) {\n return [];\n }\n}\n\nfunction extractCelebrateErrors(responseBody: any) {\n try {\n const errors: ValidationError[] = [];\n if (responseBody && responseBody.validation) {\n Object.values(responseBody.validation).forEach((error: any) => {\n if (\n error.source &&\n error.keys &&\n Array.isArray(error.keys) &&\n error.message\n ) {\n error.keys.forEach((key: string) => {\n errors.push({\n loc: `${error.source}.${key}`,\n msg: subsetJoiMessage(error.message, key),\n type: \"\",\n });\n });\n }\n });\n }\n return errors;\n } catch (error) {\n return [];\n }\n}\n\nfunction extractNestValidationErrors(responseBody: any) {\n try {\n const errors: ValidationError[] = [];\n if (responseBody && Array.isArray(responseBody.message)) {\n responseBody.message.forEach((message: any) => {\n errors.push({\n loc: \"\",\n msg: message,\n type: \"\",\n });\n });\n }\n return errors;\n } catch (error) {\n return [];\n }\n}\n\nfunction subsetJoiMessage(message: string, key: string) {\n const messageWithKey = message\n .split(\". \")\n .find((message) => message.includes(`\"${key}\"`));\n return messageWithKey ? messageWithKey : message;\n}\n\nfunction getAppInfo(\n app: Express | Router,\n basePath?: string,\n appVersion?: string,\n): StartupData {\n const versions: Array<[string, string]> = [\n [\"nodejs\", process.version.replace(/^v/, \"\")],\n ];\n const expressVersion = getPackageVersion(\"express\");\n const nestjsVersion = getPackageVersion(\"@nestjs/core\");\n const apitallyVersion = getPackageVersion(\"../..\");\n if (expressVersion) {\n versions.push([\"express\", expressVersion]);\n }\n if (nestjsVersion) {\n versions.push([\"nestjs\", nestjsVersion]);\n }\n if (apitallyVersion) {\n versions.push([\"apitally\", apitallyVersion]);\n }\n if (appVersion) {\n versions.push([\"app\", appVersion]);\n }\n return {\n paths: getEndpoints(app, basePath || \"\"),\n versions: Object.fromEntries(versions),\n client: \"js:express\",\n };\n}\n"],"mappings":"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","names":["AsyncLocalStorage","performance","ApitallyClient","consumerFromStringOrObject","parseContentLength","getPackageVersion","convertBody","convertHeaders","patchConsole","patchNestLogger","patchPinoLogger","patchWinston","getEndpoints","getRouterInfo","parseExpressPath","parseExpressPathRegExp","useApitally","app","config","client","middleware","getMiddleware","use","setStartupData","attempt","appInfo","getAppInfo","basePath","appVersion","paths","length","startSync","setTimeout","errorHandlerConfigured","logsContext","requestLogger","captureLogs","req","res","next","isEnabled","method","toUpperCase","err","locals","serverError","log","run","startTime","now","originalSend","send","body","contentType","get","isSupportedContentType","call","spanHandle","spanCollector","startSpan","runInContext","once","responseTime","path","getRoutePath","setName","spans","end","traceId","consumer","getConsumer","consumerRegistry","addOrUpdateConsumer","requestSize","responseSize","requestCounter","addRequest","identifier","statusCode","jsonBody","JSON","parse","validationErrors","push","extractExpressValidatorErrors","extractCelebrateErrors","extractNestValidationErrors","forEach","error","validationErrorCounter","addValidationError","serverErrorCounter","addServerError","type","name","msg","message","traceback","stack","enabled","logs","getStore","logRequest","timestamp","Date","url","protocol","host","originalUrl","headers","size","getHeaders","logger","request","response","route","baseUrl","routerInfo","routerPath","getRouterPath","routerPaths","routerLayer","find","layer","_a","startsWith","regexp","test","keys","parsedPath","params","Object","handle","slice","filter","join","setConsumer","apitallyConsumer","undefined","consumerIdentifier","process","emitWarning","responseBody","errors","Array","isArray","location","loc","validation","values","source","key","subsetJoiMessage","messageWithKey","split","includes","versions","version","replace","expressVersion","nestjsVersion","apitallyVersion","fromEntries"]}
@@ -28,13 +28,16 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
28
28
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
29
  var fastify_exports = {};
30
30
  __export(fastify_exports, {
31
+ ApitallySpanProcessor: () => import_spanCollector.ApitallySpanProcessor,
31
32
  apitallyPlugin: () => import_plugin.default,
32
33
  setConsumer: () => import_plugin.setConsumer
33
34
  });
34
35
  module.exports = __toCommonJS(fastify_exports);
36
+ var import_spanCollector = require("../common/spanCollector.js");
35
37
  var import_plugin = __toESM(require("./plugin.js"), 1);
36
38
  // Annotate the CommonJS export names for ESM import in node:
37
39
  0 && (module.exports = {
40
+ ApitallySpanProcessor,
38
41
  apitallyPlugin,
39
42
  setConsumer
40
43
  });
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/fastify/index.ts"],"sourcesContent":["export type { ApitallyConfig, ApitallyConsumer } from \"../common/types.js\";\nexport { default as apitallyPlugin, setConsumer } from \"./plugin.js\";\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AACA;;;;;;oBAAuD;","names":[]}
1
+ {"version":3,"sources":["../../src/fastify/index.ts"],"sourcesContent":["export { ApitallySpanProcessor } from \"../common/spanCollector.js\";\nexport type { ApitallyConfig, ApitallyConsumer } from \"../common/types.js\";\nexport { default as apitallyPlugin, setConsumer } from \"./plugin.js\";\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;;;;;;;2BAAsC;AAEtC,oBAAuD;","names":[]}
@@ -1,5 +1,7 @@
1
+ export { ApitallySpanProcessor } from '../common/spanCollector.cjs';
1
2
  export { ApitallyConfig, ApitallyConsumer } from '../common/types.cjs';
2
3
  export { default as apitallyPlugin, setConsumer } from './plugin.cjs';
4
+ import '@opentelemetry/sdk-trace-base';
3
5
  import '../common/logging.cjs';
4
6
  import 'winston';
5
7
  import '../common/requestLogger.cjs';
@@ -1,5 +1,7 @@
1
+ export { ApitallySpanProcessor } from '../common/spanCollector.js';
1
2
  export { ApitallyConfig, ApitallyConsumer } from '../common/types.js';
2
3
  export { default as apitallyPlugin, setConsumer } from './plugin.js';
4
+ import '@opentelemetry/sdk-trace-base';
3
5
  import '../common/logging.js';
4
6
  import 'winston';
5
7
  import '../common/requestLogger.js';
@@ -1,5 +1,7 @@
1
+ import { ApitallySpanProcessor } from "../common/spanCollector.js";
1
2
  import { default as default2, setConsumer } from "./plugin.js";
2
3
  export {
4
+ ApitallySpanProcessor,
3
5
  default2 as apitallyPlugin,
4
6
  setConsumer
5
7
  };
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/fastify/index.ts"],"sourcesContent":["export type { ApitallyConfig, ApitallyConsumer } from \"../common/types.js\";\nexport { default as apitallyPlugin, setConsumer } from \"./plugin.js\";\n"],"mappings":"AACA,SAAoBA,WAAXC,UAA2BC,mBAAmB;","names":["apitallyPlugin","default","setConsumer"]}
1
+ {"version":3,"sources":["../../src/fastify/index.ts"],"sourcesContent":["export { ApitallySpanProcessor } from \"../common/spanCollector.js\";\nexport type { ApitallyConfig, ApitallyConsumer } from \"../common/types.js\";\nexport { default as apitallyPlugin, setConsumer } from \"./plugin.js\";\n"],"mappings":"AAAA,SAASA,6BAA6B;AAEtC,SAAoBC,WAAXC,UAA2BC,mBAAmB;","names":["ApitallySpanProcessor","apitallyPlugin","default","setConsumer"]}
@@ -43,7 +43,8 @@ var import_packageVersions = require("../common/packageVersions.js");
43
43
  var import_requestLogger = require("../common/requestLogger.js");
44
44
  var import_loggers = require("../loggers/index.js");
45
45
  const LOGS_SYMBOL = /* @__PURE__ */ Symbol("apitally.logs");
46
- const ASYNC_RESOURCE_SYMBOL = /* @__PURE__ */ Symbol("apitally.logsContextAsyncResource");
46
+ const ASYNC_RESOURCE_SYMBOL = /* @__PURE__ */ Symbol("apitally.asyncResource");
47
+ const SPAN_HANDLE_SYMBOL = /* @__PURE__ */ Symbol("apitally.spanHandle");
47
48
  const apitallyPlugin = /* @__PURE__ */ __name(async (fastify, config) => {
48
49
  const client = new import_client.ApitallyClient(config);
49
50
  const routes = [];
@@ -85,9 +86,13 @@ const apitallyPlugin = /* @__PURE__ */ __name(async (fastify, config) => {
85
86
  const logs = [];
86
87
  request[LOGS_SYMBOL] = logs;
87
88
  logsContext.run(logs, () => {
88
- const asyncResource = new import_node_async_hooks.AsyncResource("ApitallyLogsContext");
89
- request[ASYNC_RESOURCE_SYMBOL] = asyncResource;
90
- asyncResource.runInAsyncScope(done, request.raw);
89
+ const spanHandle = client.spanCollector.startSpan();
90
+ request[SPAN_HANDLE_SYMBOL] = spanHandle;
91
+ spanHandle.runInContext(() => {
92
+ const asyncResource = new import_node_async_hooks.AsyncResource("ApitallyContext");
93
+ request[ASYNC_RESOURCE_SYMBOL] = asyncResource;
94
+ asyncResource.runInAsyncScope(done, request.raw);
95
+ });
91
96
  });
92
97
  } else {
93
98
  done();
@@ -162,6 +167,10 @@ const apitallyPlugin = /* @__PURE__ */ __name(async (fastify, config) => {
162
167
  traceback: reply.serverError.stack || ""
163
168
  });
164
169
  }
170
+ const spanHandle = request[SPAN_HANDLE_SYMBOL];
171
+ spanHandle == null ? void 0 : spanHandle.setName(`${request.method} ${path}`);
172
+ const spans = spanHandle == null ? void 0 : spanHandle.end();
173
+ const traceId = spanHandle == null ? void 0 : spanHandle.traceId;
165
174
  if (client.requestLogger.enabled) {
166
175
  const logs = request[LOGS_SYMBOL];
167
176
  client.requestLogger.logRequest({
@@ -179,7 +188,7 @@ const apitallyPlugin = /* @__PURE__ */ __name(async (fastify, config) => {
179
188
  headers: (0, import_requestLogger.convertHeaders)(reply.getHeaders()),
180
189
  size: responseSize,
181
190
  body: (0, import_requestLogger.convertBody)(reply.payload, (_a = reply.getHeader("content-type")) == null ? void 0 : _a.toString())
182
- }, reply.serverError, logs);
191
+ }, reply.serverError, logs, spans, traceId);
183
192
  }
184
193
  }
185
194
  done();