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
@@ -39,6 +39,7 @@ const RESPONSE_PROMISE_SYMBOL = /* @__PURE__ */ Symbol("apitally.responsePromise
39
39
  const ERROR_SYMBOL = /* @__PURE__ */ Symbol("apitally.error");
40
40
  const CLIENT_SYMBOL = /* @__PURE__ */ Symbol("apitally.client");
41
41
  const REQUEST_SYMBOL = /* @__PURE__ */ Symbol("apitally.request");
42
+ const SPAN_HANDLE_SYMBOL = /* @__PURE__ */ Symbol("apitally.spanHandle");
42
43
  function apitallyPlugin(config) {
43
44
  const client = new import_client.ApitallyClient(config);
44
45
  const logsContext = new import_node_async_hooks.AsyncLocalStorage();
@@ -106,6 +107,9 @@ function apitallyPlugin(config) {
106
107
  request[START_TIME_SYMBOL] = import_node_perf_hooks.performance.now();
107
108
  set[REQUEST_SYMBOL] = request;
108
109
  logsContext.enterWith([]);
110
+ const spanHandle = client.spanCollector.startSpan();
111
+ request[SPAN_HANDLE_SYMBOL] = spanHandle;
112
+ spanHandle.enterContext();
109
113
  if (client.requestLogger.enabled && client.requestLogger.config.logRequestBody) {
110
114
  const contentType = request.headers.get("content-type");
111
115
  const requestSize = (0, import_headers.parseContentLength)(request.headers.get("content-length")) ?? 0;
@@ -122,6 +126,10 @@ function apitallyPlugin(config) {
122
126
  }
123
127
  const startTime = request[START_TIME_SYMBOL];
124
128
  const responseTime = startTime ? import_node_perf_hooks.performance.now() - startTime : 0;
129
+ const spanHandle = request[SPAN_HANDLE_SYMBOL];
130
+ spanHandle == null ? void 0 : spanHandle.setName(`${request.method} ${route}`);
131
+ const spans = spanHandle == null ? void 0 : spanHandle.end();
132
+ const traceId = spanHandle == null ? void 0 : spanHandle.traceId;
125
133
  const requestBody = request[REQUEST_BODY_SYMBOL];
126
134
  const requestSize = (0, import_headers.parseContentLength)(request.headers.get("content-length")) ?? (requestBody == null ? void 0 : requestBody.length);
127
135
  let responsePromise = request[RESPONSE_PROMISE_SYMBOL];
@@ -183,7 +191,7 @@ function apitallyPlugin(config) {
183
191
  headers: (0, import_requestLogger.convertHeaders)(responseHeaders),
184
192
  size: responseSize,
185
193
  body: capturedResponse.body
186
- }, error, logs);
194
+ }, error, logs, spans, traceId);
187
195
  }
188
196
  });
189
197
  if ((statusCode === 400 || statusCode === 422) && error instanceof import_elysia.ValidationError) {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/elysia/plugin.ts"],"sourcesContent":["import { Context, Elysia, StatusMap, ValidationError } from \"elysia\";\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 type { LogRecord } from \"../common/requestLogger.js\";\nimport { convertHeaders } from \"../common/requestLogger.js\";\nimport { CapturedResponse, captureResponse } from \"../common/response.js\";\nimport { ApitallyConfig, ApitallyConsumer } from \"../common/types.js\";\nimport { patchConsole, patchWinston } from \"../loggers/index.js\";\nimport { getAppInfo } from \"./utils.js\";\n\nconst START_TIME_SYMBOL = Symbol(\"apitally.startTime\");\nconst REQUEST_BODY_SYMBOL = Symbol(\"apitally.requestBody\");\nconst RESPONSE_SYMBOL = Symbol(\"apitally.response\");\nconst RESPONSE_PROMISE_SYMBOL = Symbol(\"apitally.responsePromise\");\nconst ERROR_SYMBOL = Symbol(\"apitally.error\");\nconst CLIENT_SYMBOL = Symbol(\"apitally.client\");\nconst REQUEST_SYMBOL = Symbol(\"apitally.request\");\n\ndeclare global {\n interface Request {\n [START_TIME_SYMBOL]?: number;\n [REQUEST_BODY_SYMBOL]?: Buffer;\n [RESPONSE_SYMBOL]?: Response;\n [RESPONSE_PROMISE_SYMBOL]?: Promise<CapturedResponse>;\n [ERROR_SYMBOL]?: Readonly<Error>;\n [CLIENT_SYMBOL]?: ApitallyClient;\n }\n}\n\ntype ContextSet = Context[\"set\"] & {\n [REQUEST_SYMBOL]?: Request;\n};\n\ninterface ApitallyContext {\n consumer?: ApitallyConsumer | string;\n}\n\nexport default function apitallyPlugin(config: ApitallyConfig) {\n const client = new ApitallyClient(config);\n const logsContext = new AsyncLocalStorage<LogRecord[]>();\n\n if (client.requestLogger.enabled && client.requestLogger.config.captureLogs) {\n patchConsole(logsContext);\n patchWinston(logsContext);\n }\n\n return (app: Elysia) => {\n const handler = app[\"~adapter\"].handler;\n\n if (!handler.mapResponse.name.startsWith(\"wrapped\")) {\n const originalMapResponse = handler.mapResponse;\n const originalMapCompactResponse = handler.mapCompactResponse;\n const originalMapEarlyResponse = handler.mapEarlyResponse;\n\n const captureMappedResponse = (\n originalResponse: unknown,\n mappedResponse: unknown,\n set?: ContextSet,\n ) => {\n const request = set?.[REQUEST_SYMBOL];\n if (\n request instanceof Request &&\n mappedResponse instanceof Response &&\n !(RESPONSE_SYMBOL in request) &&\n CLIENT_SYMBOL in request\n ) {\n if (typeof originalResponse === \"string\") {\n // Preserve the response body value as Blob if the original response is a string,\n // so that Bun adds a Content-Type header.\n const responseBody = Buffer.from(originalResponse as string);\n request[RESPONSE_SYMBOL] = mappedResponse;\n request[RESPONSE_PROMISE_SYMBOL] = Promise.resolve({\n body: responseBody,\n size: responseBody.length,\n completed: true,\n });\n } else {\n // Otherwise capture the response using streaming\n const client = request[CLIENT_SYMBOL]!;\n const [newResponse, responsePromise] = captureResponse(\n mappedResponse,\n {\n captureBody:\n client.requestLogger.enabled &&\n client.requestLogger.config.logResponseBody,\n maxBodySize: client.requestLogger.maxBodySize,\n },\n );\n request[RESPONSE_SYMBOL] = newResponse;\n request[RESPONSE_PROMISE_SYMBOL] = responsePromise;\n return newResponse;\n }\n }\n return mappedResponse;\n };\n\n handler.mapResponse = function wrappedMapResponse(\n response: unknown,\n set: ContextSet,\n ) {\n const mappedResponse = originalMapResponse(response, set);\n const newResponse = captureMappedResponse(\n response,\n mappedResponse,\n set,\n );\n return newResponse;\n };\n handler.mapCompactResponse = function wrappedMapCompactResponse(\n response: unknown,\n ) {\n const mappedResponse = originalMapCompactResponse(response);\n const newResponse = captureMappedResponse(\n response,\n mappedResponse,\n undefined,\n );\n return newResponse;\n };\n handler.mapEarlyResponse = function wrappedMapEarlyResponse(\n response: unknown,\n set: ContextSet,\n ) {\n const mappedResponse = originalMapEarlyResponse(response, set);\n const newResponse = captureMappedResponse(\n response,\n mappedResponse,\n set,\n );\n return newResponse;\n };\n }\n\n return app\n .decorate(\"apitally\", {} as ApitallyContext)\n .onStart(() => {\n const appInfo = getAppInfo(app, config.appVersion);\n client.setStartupData(appInfo);\n client.startSync();\n })\n .onStop(async () => {\n await client.handleShutdown();\n })\n .onRequest(async ({ request, set }) => {\n if (!client.isEnabled() || request.method.toUpperCase() === \"OPTIONS\") {\n return;\n }\n\n request[CLIENT_SYMBOL] = client;\n request[START_TIME_SYMBOL] = performance.now();\n (set as ContextSet)[REQUEST_SYMBOL] = request;\n logsContext.enterWith([]);\n\n // Capture request body\n if (\n client.requestLogger.enabled &&\n client.requestLogger.config.logRequestBody\n ) {\n const contentType = request.headers.get(\"content-type\");\n const requestSize =\n parseContentLength(request.headers.get(\"content-length\")) ?? 0;\n\n if (\n client.requestLogger.isSupportedContentType(contentType) &&\n requestSize <= client.requestLogger.maxBodySize\n ) {\n try {\n request[REQUEST_BODY_SYMBOL] = Buffer.from(\n await request.clone().arrayBuffer(),\n );\n } catch (error) {\n // ignore\n }\n }\n }\n })\n .onAfterResponse(async ({ request, set, route, apitally }) => {\n if (!client.isEnabled() || request.method.toUpperCase() === \"OPTIONS\") {\n return;\n }\n\n const startTime = request[START_TIME_SYMBOL];\n const responseTime = startTime ? performance.now() - startTime : 0;\n\n const requestBody = request[REQUEST_BODY_SYMBOL];\n const requestSize =\n parseContentLength(request.headers.get(\"content-length\")) ??\n requestBody?.length;\n\n let responsePromise = request[RESPONSE_PROMISE_SYMBOL];\n let response = request[RESPONSE_SYMBOL];\n const error = request[ERROR_SYMBOL];\n\n if (\n !response &&\n error &&\n \"toResponse\" in error &&\n typeof error.toResponse === \"function\"\n ) {\n // Convert error to response\n try {\n response = error.toResponse() as Response;\n const errorResponseBody = Buffer.from(await response.arrayBuffer());\n responsePromise = Promise.resolve({\n body: errorResponseBody,\n size: errorResponseBody.length,\n completed: true,\n });\n } catch (error) {\n // ignore\n }\n }\n\n const statusCode = response?.status ?? getStatusCode(set) ?? 200;\n\n if (!response) {\n // Create empty fake response for errors without the toResponse method\n response = new Response(null, {\n status: statusCode,\n statusText: \"\",\n headers: new Headers(),\n });\n responsePromise = Promise.resolve({\n body: undefined,\n size: 0,\n completed: true,\n });\n }\n\n const consumer = apitally.consumer\n ? consumerFromStringOrObject(apitally.consumer)\n : null;\n client.consumerRegistry.addOrUpdateConsumer(consumer);\n\n // Log request when response has been fully captured\n responsePromise?.then(async (capturedResponse) => {\n const responseHeaders = response?.headers ?? set.headers;\n const responseSize = capturedResponse.completed\n ? capturedResponse.size\n : undefined;\n\n client.requestCounter.addRequest({\n consumer: consumer?.identifier,\n method: request.method,\n path: route,\n statusCode,\n responseTime,\n requestSize,\n responseSize,\n });\n\n if (client.requestLogger.enabled) {\n const logs = logsContext.getStore();\n client.requestLogger.logRequest(\n {\n timestamp: (Date.now() - responseTime) / 1000,\n method: request.method,\n path: route,\n url: request.url,\n headers: convertHeaders(\n Object.fromEntries(request.headers.entries()),\n ),\n size: requestSize,\n consumer: consumer?.identifier,\n body: requestBody,\n },\n {\n statusCode,\n responseTime: responseTime / 1000,\n headers: convertHeaders(responseHeaders),\n size: responseSize,\n body: capturedResponse.body,\n },\n error,\n logs,\n );\n }\n });\n\n // Handle validation errors\n if (\n (statusCode === 400 || statusCode === 422) &&\n error instanceof ValidationError\n ) {\n try {\n const parsedMessage = JSON.parse(error.message);\n client.validationErrorCounter.addValidationError({\n consumer: consumer?.identifier,\n method: request.method,\n path: route,\n loc:\n (parsedMessage.on ?? \"\") + \".\" + (parsedMessage.property ?? \"\"),\n msg: parsedMessage.message,\n type: \"\",\n });\n } catch (error) {\n // ignore\n }\n }\n\n // Handle server errors\n if (statusCode === 500 && error) {\n client.serverErrorCounter.addServerError({\n consumer: consumer?.identifier,\n method: request.method,\n path: route,\n type: error.name,\n msg: error.message,\n traceback: error.stack || \"\",\n });\n }\n })\n .onError(({ request, error }) => {\n if (client.isEnabled() && error instanceof Error) {\n request[ERROR_SYMBOL] = error;\n }\n });\n };\n}\n\nfunction getStatusCode(set: Context[\"set\"]) {\n if (typeof set.status === \"number\") {\n return set.status;\n } else if (typeof set.status === \"string\") {\n return StatusMap[set.status];\n }\n}\n"],"mappings":"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","names":["START_TIME_SYMBOL","Symbol","REQUEST_BODY_SYMBOL","RESPONSE_SYMBOL","RESPONSE_PROMISE_SYMBOL","ERROR_SYMBOL","CLIENT_SYMBOL","REQUEST_SYMBOL","config","client","ApitallyClient","logsContext","AsyncLocalStorage","requestLogger","enabled","captureLogs","patchConsole","patchWinston","app","handler","mapResponse","name","startsWith","originalMapResponse","originalMapCompactResponse","mapCompactResponse","originalMapEarlyResponse","mapEarlyResponse","captureMappedResponse","originalResponse","mappedResponse","set","request","Request","Response","responseBody","Buffer","from","Promise","resolve","body","size","length","completed","newResponse","responsePromise","captureResponse","captureBody","logResponseBody","maxBodySize","wrappedMapResponse","response","wrappedMapCompactResponse","undefined","wrappedMapEarlyResponse","decorate","onStart","appInfo","getAppInfo","appVersion","setStartupData","startSync","onStop","handleShutdown","onRequest","isEnabled","method","toUpperCase","performance","now","enterWith","logRequestBody","contentType","headers","get","requestSize","parseContentLength","isSupportedContentType","clone","arrayBuffer","error","onAfterResponse","route","apitally","startTime","responseTime","requestBody","toResponse","errorResponseBody","statusCode","status","getStatusCode","statusText","Headers","consumer","consumerFromStringOrObject","consumerRegistry","addOrUpdateConsumer","then","capturedResponse","responseHeaders","responseSize","requestCounter","addRequest","identifier","path","logs","getStore","logRequest","timestamp","Date","url","convertHeaders","Object","fromEntries","entries","ValidationError","parsedMessage","JSON","parse","message","validationErrorCounter","addValidationError","loc","on","property","msg","type","serverErrorCounter","addServerError","traceback","stack","onError","Error","apitallyPlugin","StatusMap"]}
1
+ {"version":3,"sources":["../../src/elysia/plugin.ts"],"sourcesContent":["import { Context, Elysia, StatusMap, ValidationError } from \"elysia\";\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 type { LogRecord } from \"../common/requestLogger.js\";\nimport { convertHeaders } from \"../common/requestLogger.js\";\nimport { CapturedResponse, captureResponse } from \"../common/response.js\";\nimport type { SpanHandle } from \"../common/spanCollector.js\";\nimport { ApitallyConfig, ApitallyConsumer } from \"../common/types.js\";\nimport { patchConsole, patchWinston } from \"../loggers/index.js\";\nimport { getAppInfo } from \"./utils.js\";\n\nconst START_TIME_SYMBOL = Symbol(\"apitally.startTime\");\nconst REQUEST_BODY_SYMBOL = Symbol(\"apitally.requestBody\");\nconst RESPONSE_SYMBOL = Symbol(\"apitally.response\");\nconst RESPONSE_PROMISE_SYMBOL = Symbol(\"apitally.responsePromise\");\nconst ERROR_SYMBOL = Symbol(\"apitally.error\");\nconst CLIENT_SYMBOL = Symbol(\"apitally.client\");\nconst REQUEST_SYMBOL = Symbol(\"apitally.request\");\nconst SPAN_HANDLE_SYMBOL = Symbol(\"apitally.spanHandle\");\n\ndeclare global {\n interface Request {\n [START_TIME_SYMBOL]?: number;\n [REQUEST_BODY_SYMBOL]?: Buffer;\n [RESPONSE_SYMBOL]?: Response;\n [RESPONSE_PROMISE_SYMBOL]?: Promise<CapturedResponse>;\n [ERROR_SYMBOL]?: Readonly<Error>;\n [CLIENT_SYMBOL]?: ApitallyClient;\n [SPAN_HANDLE_SYMBOL]?: SpanHandle;\n }\n}\n\ntype ContextSet = Context[\"set\"] & {\n [REQUEST_SYMBOL]?: Request;\n};\n\ninterface ApitallyContext {\n consumer?: ApitallyConsumer | string;\n}\n\nexport default function apitallyPlugin(config: ApitallyConfig) {\n const client = new ApitallyClient(config);\n const logsContext = new AsyncLocalStorage<LogRecord[]>();\n\n if (client.requestLogger.enabled && client.requestLogger.config.captureLogs) {\n patchConsole(logsContext);\n patchWinston(logsContext);\n }\n\n return (app: Elysia) => {\n const handler = app[\"~adapter\"].handler;\n\n if (!handler.mapResponse.name.startsWith(\"wrapped\")) {\n const originalMapResponse = handler.mapResponse;\n const originalMapCompactResponse = handler.mapCompactResponse;\n const originalMapEarlyResponse = handler.mapEarlyResponse;\n\n const captureMappedResponse = (\n originalResponse: unknown,\n mappedResponse: unknown,\n set?: ContextSet,\n ) => {\n const request = set?.[REQUEST_SYMBOL];\n if (\n request instanceof Request &&\n mappedResponse instanceof Response &&\n !(RESPONSE_SYMBOL in request) &&\n CLIENT_SYMBOL in request\n ) {\n if (typeof originalResponse === \"string\") {\n // Preserve the response body value as Blob if the original response is a string,\n // so that Bun adds a Content-Type header.\n const responseBody = Buffer.from(originalResponse as string);\n request[RESPONSE_SYMBOL] = mappedResponse;\n request[RESPONSE_PROMISE_SYMBOL] = Promise.resolve({\n body: responseBody,\n size: responseBody.length,\n completed: true,\n });\n } else {\n // Otherwise capture the response using streaming\n const client = request[CLIENT_SYMBOL]!;\n const [newResponse, responsePromise] = captureResponse(\n mappedResponse,\n {\n captureBody:\n client.requestLogger.enabled &&\n client.requestLogger.config.logResponseBody,\n maxBodySize: client.requestLogger.maxBodySize,\n },\n );\n request[RESPONSE_SYMBOL] = newResponse;\n request[RESPONSE_PROMISE_SYMBOL] = responsePromise;\n return newResponse;\n }\n }\n return mappedResponse;\n };\n\n handler.mapResponse = function wrappedMapResponse(\n response: unknown,\n set: ContextSet,\n ) {\n const mappedResponse = originalMapResponse(response, set);\n const newResponse = captureMappedResponse(\n response,\n mappedResponse,\n set,\n );\n return newResponse;\n };\n handler.mapCompactResponse = function wrappedMapCompactResponse(\n response: unknown,\n ) {\n const mappedResponse = originalMapCompactResponse(response);\n const newResponse = captureMappedResponse(\n response,\n mappedResponse,\n undefined,\n );\n return newResponse;\n };\n handler.mapEarlyResponse = function wrappedMapEarlyResponse(\n response: unknown,\n set: ContextSet,\n ) {\n const mappedResponse = originalMapEarlyResponse(response, set);\n const newResponse = captureMappedResponse(\n response,\n mappedResponse,\n set,\n );\n return newResponse;\n };\n }\n\n return app\n .decorate(\"apitally\", {} as ApitallyContext)\n .onStart(() => {\n const appInfo = getAppInfo(app, config.appVersion);\n client.setStartupData(appInfo);\n client.startSync();\n })\n .onStop(async () => {\n await client.handleShutdown();\n })\n .onRequest(async ({ request, set }) => {\n if (!client.isEnabled() || request.method.toUpperCase() === \"OPTIONS\") {\n return;\n }\n\n request[CLIENT_SYMBOL] = client;\n request[START_TIME_SYMBOL] = performance.now();\n (set as ContextSet)[REQUEST_SYMBOL] = request;\n logsContext.enterWith([]);\n\n // Start span and enter the context for subsequent handlers\n const spanHandle = client.spanCollector.startSpan();\n request[SPAN_HANDLE_SYMBOL] = spanHandle;\n spanHandle.enterContext();\n\n // Capture request body\n if (\n client.requestLogger.enabled &&\n client.requestLogger.config.logRequestBody\n ) {\n const contentType = request.headers.get(\"content-type\");\n const requestSize =\n parseContentLength(request.headers.get(\"content-length\")) ?? 0;\n\n if (\n client.requestLogger.isSupportedContentType(contentType) &&\n requestSize <= client.requestLogger.maxBodySize\n ) {\n try {\n request[REQUEST_BODY_SYMBOL] = Buffer.from(\n await request.clone().arrayBuffer(),\n );\n } catch (error) {\n // ignore\n }\n }\n }\n })\n .onAfterResponse(async ({ request, set, route, apitally }) => {\n if (!client.isEnabled() || request.method.toUpperCase() === \"OPTIONS\") {\n return;\n }\n\n const startTime = request[START_TIME_SYMBOL];\n const responseTime = startTime ? performance.now() - startTime : 0;\n\n const spanHandle = request[SPAN_HANDLE_SYMBOL];\n spanHandle?.setName(`${request.method} ${route}`);\n const spans = spanHandle?.end();\n const traceId = spanHandle?.traceId;\n\n const requestBody = request[REQUEST_BODY_SYMBOL];\n const requestSize =\n parseContentLength(request.headers.get(\"content-length\")) ??\n requestBody?.length;\n\n let responsePromise = request[RESPONSE_PROMISE_SYMBOL];\n let response = request[RESPONSE_SYMBOL];\n const error = request[ERROR_SYMBOL];\n\n if (\n !response &&\n error &&\n \"toResponse\" in error &&\n typeof error.toResponse === \"function\"\n ) {\n // Convert error to response\n try {\n response = error.toResponse() as Response;\n const errorResponseBody = Buffer.from(await response.arrayBuffer());\n responsePromise = Promise.resolve({\n body: errorResponseBody,\n size: errorResponseBody.length,\n completed: true,\n });\n } catch (error) {\n // ignore\n }\n }\n\n const statusCode = response?.status ?? getStatusCode(set) ?? 200;\n\n if (!response) {\n // Create empty fake response for errors without the toResponse method\n response = new Response(null, {\n status: statusCode,\n statusText: \"\",\n headers: new Headers(),\n });\n responsePromise = Promise.resolve({\n body: undefined,\n size: 0,\n completed: true,\n });\n }\n\n const consumer = apitally.consumer\n ? consumerFromStringOrObject(apitally.consumer)\n : null;\n client.consumerRegistry.addOrUpdateConsumer(consumer);\n\n // Log request when response has been fully captured\n responsePromise?.then(async (capturedResponse) => {\n const responseHeaders = response?.headers ?? set.headers;\n const responseSize = capturedResponse.completed\n ? capturedResponse.size\n : undefined;\n\n client.requestCounter.addRequest({\n consumer: consumer?.identifier,\n method: request.method,\n path: route,\n statusCode,\n responseTime,\n requestSize,\n responseSize,\n });\n\n if (client.requestLogger.enabled) {\n const logs = logsContext.getStore();\n client.requestLogger.logRequest(\n {\n timestamp: (Date.now() - responseTime) / 1000,\n method: request.method,\n path: route,\n url: request.url,\n headers: convertHeaders(\n Object.fromEntries(request.headers.entries()),\n ),\n size: requestSize,\n consumer: consumer?.identifier,\n body: requestBody,\n },\n {\n statusCode,\n responseTime: responseTime / 1000,\n headers: convertHeaders(responseHeaders),\n size: responseSize,\n body: capturedResponse.body,\n },\n error,\n logs,\n spans,\n traceId,\n );\n }\n });\n\n // Handle validation errors\n if (\n (statusCode === 400 || statusCode === 422) &&\n error instanceof ValidationError\n ) {\n try {\n const parsedMessage = JSON.parse(error.message);\n client.validationErrorCounter.addValidationError({\n consumer: consumer?.identifier,\n method: request.method,\n path: route,\n loc:\n (parsedMessage.on ?? \"\") + \".\" + (parsedMessage.property ?? \"\"),\n msg: parsedMessage.message,\n type: \"\",\n });\n } catch (error) {\n // ignore\n }\n }\n\n // Handle server errors\n if (statusCode === 500 && error) {\n client.serverErrorCounter.addServerError({\n consumer: consumer?.identifier,\n method: request.method,\n path: route,\n type: error.name,\n msg: error.message,\n traceback: error.stack || \"\",\n });\n }\n })\n .onError(({ request, error }) => {\n if (client.isEnabled() && error instanceof Error) {\n request[ERROR_SYMBOL] = error;\n }\n });\n };\n}\n\nfunction getStatusCode(set: Context[\"set\"]) {\n if (typeof set.status === \"number\") {\n return set.status;\n } else if (typeof set.status === \"string\") {\n return StatusMap[set.status];\n }\n}\n"],"mappings":"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","names":["START_TIME_SYMBOL","Symbol","REQUEST_BODY_SYMBOL","RESPONSE_SYMBOL","RESPONSE_PROMISE_SYMBOL","ERROR_SYMBOL","CLIENT_SYMBOL","REQUEST_SYMBOL","SPAN_HANDLE_SYMBOL","config","client","ApitallyClient","logsContext","AsyncLocalStorage","requestLogger","enabled","captureLogs","patchConsole","patchWinston","app","handler","mapResponse","name","startsWith","originalMapResponse","originalMapCompactResponse","mapCompactResponse","originalMapEarlyResponse","mapEarlyResponse","captureMappedResponse","originalResponse","mappedResponse","set","request","Request","Response","responseBody","Buffer","from","Promise","resolve","body","size","length","completed","newResponse","responsePromise","captureResponse","captureBody","logResponseBody","maxBodySize","wrappedMapResponse","response","wrappedMapCompactResponse","undefined","wrappedMapEarlyResponse","decorate","onStart","appInfo","getAppInfo","appVersion","setStartupData","startSync","onStop","handleShutdown","onRequest","isEnabled","method","toUpperCase","performance","now","enterWith","spanHandle","spanCollector","startSpan","enterContext","logRequestBody","contentType","headers","get","requestSize","parseContentLength","isSupportedContentType","clone","arrayBuffer","error","onAfterResponse","route","apitally","startTime","responseTime","setName","spans","end","traceId","requestBody","toResponse","errorResponseBody","statusCode","status","getStatusCode","statusText","Headers","consumer","consumerFromStringOrObject","consumerRegistry","addOrUpdateConsumer","then","capturedResponse","responseHeaders","responseSize","requestCounter","addRequest","identifier","path","logs","getStore","logRequest","timestamp","Date","url","convertHeaders","Object","fromEntries","entries","ValidationError","parsedMessage","JSON","parse","message","validationErrorCounter","addValidationError","loc","on","property","msg","type","serverErrorCounter","addServerError","traceback","stack","onError","Error","apitallyPlugin","StatusMap"]}
@@ -1,7 +1,9 @@
1
+ import { SpanHandle } from '../common/spanCollector.cjs';
1
2
  import { ApitallyConfig, ApitallyConsumer } from '../common/types.cjs';
2
3
  import { Elysia } from 'elysia';
3
4
  import { ApitallyClient } from '../common/client.cjs';
4
5
  import { CapturedResponse } from '../common/response.cjs';
6
+ import '@opentelemetry/sdk-trace-base';
5
7
  import '../common/logging.cjs';
6
8
  import 'winston';
7
9
  import '../common/requestLogger.cjs';
@@ -19,6 +21,7 @@ declare const RESPONSE_SYMBOL: unique symbol;
19
21
  declare const RESPONSE_PROMISE_SYMBOL: unique symbol;
20
22
  declare const ERROR_SYMBOL: unique symbol;
21
23
  declare const CLIENT_SYMBOL: unique symbol;
24
+ declare const SPAN_HANDLE_SYMBOL: unique symbol;
22
25
  declare global {
23
26
  interface Request {
24
27
  [START_TIME_SYMBOL]?: number;
@@ -27,6 +30,7 @@ declare global {
27
30
  [RESPONSE_PROMISE_SYMBOL]?: Promise<CapturedResponse>;
28
31
  [ERROR_SYMBOL]?: Readonly<Error>;
29
32
  [CLIENT_SYMBOL]?: ApitallyClient;
33
+ [SPAN_HANDLE_SYMBOL]?: SpanHandle;
30
34
  }
31
35
  }
32
36
  interface ApitallyContext {
@@ -1,7 +1,9 @@
1
+ import { SpanHandle } from '../common/spanCollector.js';
1
2
  import { ApitallyConfig, ApitallyConsumer } from '../common/types.js';
2
3
  import { Elysia } from 'elysia';
3
4
  import { ApitallyClient } from '../common/client.js';
4
5
  import { CapturedResponse } from '../common/response.js';
6
+ import '@opentelemetry/sdk-trace-base';
5
7
  import '../common/logging.js';
6
8
  import 'winston';
7
9
  import '../common/requestLogger.js';
@@ -19,6 +21,7 @@ declare const RESPONSE_SYMBOL: unique symbol;
19
21
  declare const RESPONSE_PROMISE_SYMBOL: unique symbol;
20
22
  declare const ERROR_SYMBOL: unique symbol;
21
23
  declare const CLIENT_SYMBOL: unique symbol;
24
+ declare const SPAN_HANDLE_SYMBOL: unique symbol;
22
25
  declare global {
23
26
  interface Request {
24
27
  [START_TIME_SYMBOL]?: number;
@@ -27,6 +30,7 @@ declare global {
27
30
  [RESPONSE_PROMISE_SYMBOL]?: Promise<CapturedResponse>;
28
31
  [ERROR_SYMBOL]?: Readonly<Error>;
29
32
  [CLIENT_SYMBOL]?: ApitallyClient;
33
+ [SPAN_HANDLE_SYMBOL]?: SpanHandle;
30
34
  }
31
35
  }
32
36
  interface ApitallyContext {
@@ -17,6 +17,7 @@ const RESPONSE_PROMISE_SYMBOL = /* @__PURE__ */ Symbol("apitally.responsePromise
17
17
  const ERROR_SYMBOL = /* @__PURE__ */ Symbol("apitally.error");
18
18
  const CLIENT_SYMBOL = /* @__PURE__ */ Symbol("apitally.client");
19
19
  const REQUEST_SYMBOL = /* @__PURE__ */ Symbol("apitally.request");
20
+ const SPAN_HANDLE_SYMBOL = /* @__PURE__ */ Symbol("apitally.spanHandle");
20
21
  function apitallyPlugin(config) {
21
22
  const client = new ApitallyClient(config);
22
23
  const logsContext = new AsyncLocalStorage();
@@ -84,6 +85,9 @@ function apitallyPlugin(config) {
84
85
  request[START_TIME_SYMBOL] = performance.now();
85
86
  set[REQUEST_SYMBOL] = request;
86
87
  logsContext.enterWith([]);
88
+ const spanHandle = client.spanCollector.startSpan();
89
+ request[SPAN_HANDLE_SYMBOL] = spanHandle;
90
+ spanHandle.enterContext();
87
91
  if (client.requestLogger.enabled && client.requestLogger.config.logRequestBody) {
88
92
  const contentType = request.headers.get("content-type");
89
93
  const requestSize = parseContentLength(request.headers.get("content-length")) ?? 0;
@@ -100,6 +104,10 @@ function apitallyPlugin(config) {
100
104
  }
101
105
  const startTime = request[START_TIME_SYMBOL];
102
106
  const responseTime = startTime ? performance.now() - startTime : 0;
107
+ const spanHandle = request[SPAN_HANDLE_SYMBOL];
108
+ spanHandle == null ? void 0 : spanHandle.setName(`${request.method} ${route}`);
109
+ const spans = spanHandle == null ? void 0 : spanHandle.end();
110
+ const traceId = spanHandle == null ? void 0 : spanHandle.traceId;
103
111
  const requestBody = request[REQUEST_BODY_SYMBOL];
104
112
  const requestSize = parseContentLength(request.headers.get("content-length")) ?? (requestBody == null ? void 0 : requestBody.length);
105
113
  let responsePromise = request[RESPONSE_PROMISE_SYMBOL];
@@ -161,7 +169,7 @@ function apitallyPlugin(config) {
161
169
  headers: convertHeaders(responseHeaders),
162
170
  size: responseSize,
163
171
  body: capturedResponse.body
164
- }, error, logs);
172
+ }, error, logs, spans, traceId);
165
173
  }
166
174
  });
167
175
  if ((statusCode === 400 || statusCode === 422) && error instanceof ValidationError) {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/elysia/plugin.ts"],"sourcesContent":["import { Context, Elysia, StatusMap, ValidationError } from \"elysia\";\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 type { LogRecord } from \"../common/requestLogger.js\";\nimport { convertHeaders } from \"../common/requestLogger.js\";\nimport { CapturedResponse, captureResponse } from \"../common/response.js\";\nimport { ApitallyConfig, ApitallyConsumer } from \"../common/types.js\";\nimport { patchConsole, patchWinston } from \"../loggers/index.js\";\nimport { getAppInfo } from \"./utils.js\";\n\nconst START_TIME_SYMBOL = Symbol(\"apitally.startTime\");\nconst REQUEST_BODY_SYMBOL = Symbol(\"apitally.requestBody\");\nconst RESPONSE_SYMBOL = Symbol(\"apitally.response\");\nconst RESPONSE_PROMISE_SYMBOL = Symbol(\"apitally.responsePromise\");\nconst ERROR_SYMBOL = Symbol(\"apitally.error\");\nconst CLIENT_SYMBOL = Symbol(\"apitally.client\");\nconst REQUEST_SYMBOL = Symbol(\"apitally.request\");\n\ndeclare global {\n interface Request {\n [START_TIME_SYMBOL]?: number;\n [REQUEST_BODY_SYMBOL]?: Buffer;\n [RESPONSE_SYMBOL]?: Response;\n [RESPONSE_PROMISE_SYMBOL]?: Promise<CapturedResponse>;\n [ERROR_SYMBOL]?: Readonly<Error>;\n [CLIENT_SYMBOL]?: ApitallyClient;\n }\n}\n\ntype ContextSet = Context[\"set\"] & {\n [REQUEST_SYMBOL]?: Request;\n};\n\ninterface ApitallyContext {\n consumer?: ApitallyConsumer | string;\n}\n\nexport default function apitallyPlugin(config: ApitallyConfig) {\n const client = new ApitallyClient(config);\n const logsContext = new AsyncLocalStorage<LogRecord[]>();\n\n if (client.requestLogger.enabled && client.requestLogger.config.captureLogs) {\n patchConsole(logsContext);\n patchWinston(logsContext);\n }\n\n return (app: Elysia) => {\n const handler = app[\"~adapter\"].handler;\n\n if (!handler.mapResponse.name.startsWith(\"wrapped\")) {\n const originalMapResponse = handler.mapResponse;\n const originalMapCompactResponse = handler.mapCompactResponse;\n const originalMapEarlyResponse = handler.mapEarlyResponse;\n\n const captureMappedResponse = (\n originalResponse: unknown,\n mappedResponse: unknown,\n set?: ContextSet,\n ) => {\n const request = set?.[REQUEST_SYMBOL];\n if (\n request instanceof Request &&\n mappedResponse instanceof Response &&\n !(RESPONSE_SYMBOL in request) &&\n CLIENT_SYMBOL in request\n ) {\n if (typeof originalResponse === \"string\") {\n // Preserve the response body value as Blob if the original response is a string,\n // so that Bun adds a Content-Type header.\n const responseBody = Buffer.from(originalResponse as string);\n request[RESPONSE_SYMBOL] = mappedResponse;\n request[RESPONSE_PROMISE_SYMBOL] = Promise.resolve({\n body: responseBody,\n size: responseBody.length,\n completed: true,\n });\n } else {\n // Otherwise capture the response using streaming\n const client = request[CLIENT_SYMBOL]!;\n const [newResponse, responsePromise] = captureResponse(\n mappedResponse,\n {\n captureBody:\n client.requestLogger.enabled &&\n client.requestLogger.config.logResponseBody,\n maxBodySize: client.requestLogger.maxBodySize,\n },\n );\n request[RESPONSE_SYMBOL] = newResponse;\n request[RESPONSE_PROMISE_SYMBOL] = responsePromise;\n return newResponse;\n }\n }\n return mappedResponse;\n };\n\n handler.mapResponse = function wrappedMapResponse(\n response: unknown,\n set: ContextSet,\n ) {\n const mappedResponse = originalMapResponse(response, set);\n const newResponse = captureMappedResponse(\n response,\n mappedResponse,\n set,\n );\n return newResponse;\n };\n handler.mapCompactResponse = function wrappedMapCompactResponse(\n response: unknown,\n ) {\n const mappedResponse = originalMapCompactResponse(response);\n const newResponse = captureMappedResponse(\n response,\n mappedResponse,\n undefined,\n );\n return newResponse;\n };\n handler.mapEarlyResponse = function wrappedMapEarlyResponse(\n response: unknown,\n set: ContextSet,\n ) {\n const mappedResponse = originalMapEarlyResponse(response, set);\n const newResponse = captureMappedResponse(\n response,\n mappedResponse,\n set,\n );\n return newResponse;\n };\n }\n\n return app\n .decorate(\"apitally\", {} as ApitallyContext)\n .onStart(() => {\n const appInfo = getAppInfo(app, config.appVersion);\n client.setStartupData(appInfo);\n client.startSync();\n })\n .onStop(async () => {\n await client.handleShutdown();\n })\n .onRequest(async ({ request, set }) => {\n if (!client.isEnabled() || request.method.toUpperCase() === \"OPTIONS\") {\n return;\n }\n\n request[CLIENT_SYMBOL] = client;\n request[START_TIME_SYMBOL] = performance.now();\n (set as ContextSet)[REQUEST_SYMBOL] = request;\n logsContext.enterWith([]);\n\n // Capture request body\n if (\n client.requestLogger.enabled &&\n client.requestLogger.config.logRequestBody\n ) {\n const contentType = request.headers.get(\"content-type\");\n const requestSize =\n parseContentLength(request.headers.get(\"content-length\")) ?? 0;\n\n if (\n client.requestLogger.isSupportedContentType(contentType) &&\n requestSize <= client.requestLogger.maxBodySize\n ) {\n try {\n request[REQUEST_BODY_SYMBOL] = Buffer.from(\n await request.clone().arrayBuffer(),\n );\n } catch (error) {\n // ignore\n }\n }\n }\n })\n .onAfterResponse(async ({ request, set, route, apitally }) => {\n if (!client.isEnabled() || request.method.toUpperCase() === \"OPTIONS\") {\n return;\n }\n\n const startTime = request[START_TIME_SYMBOL];\n const responseTime = startTime ? performance.now() - startTime : 0;\n\n const requestBody = request[REQUEST_BODY_SYMBOL];\n const requestSize =\n parseContentLength(request.headers.get(\"content-length\")) ??\n requestBody?.length;\n\n let responsePromise = request[RESPONSE_PROMISE_SYMBOL];\n let response = request[RESPONSE_SYMBOL];\n const error = request[ERROR_SYMBOL];\n\n if (\n !response &&\n error &&\n \"toResponse\" in error &&\n typeof error.toResponse === \"function\"\n ) {\n // Convert error to response\n try {\n response = error.toResponse() as Response;\n const errorResponseBody = Buffer.from(await response.arrayBuffer());\n responsePromise = Promise.resolve({\n body: errorResponseBody,\n size: errorResponseBody.length,\n completed: true,\n });\n } catch (error) {\n // ignore\n }\n }\n\n const statusCode = response?.status ?? getStatusCode(set) ?? 200;\n\n if (!response) {\n // Create empty fake response for errors without the toResponse method\n response = new Response(null, {\n status: statusCode,\n statusText: \"\",\n headers: new Headers(),\n });\n responsePromise = Promise.resolve({\n body: undefined,\n size: 0,\n completed: true,\n });\n }\n\n const consumer = apitally.consumer\n ? consumerFromStringOrObject(apitally.consumer)\n : null;\n client.consumerRegistry.addOrUpdateConsumer(consumer);\n\n // Log request when response has been fully captured\n responsePromise?.then(async (capturedResponse) => {\n const responseHeaders = response?.headers ?? set.headers;\n const responseSize = capturedResponse.completed\n ? capturedResponse.size\n : undefined;\n\n client.requestCounter.addRequest({\n consumer: consumer?.identifier,\n method: request.method,\n path: route,\n statusCode,\n responseTime,\n requestSize,\n responseSize,\n });\n\n if (client.requestLogger.enabled) {\n const logs = logsContext.getStore();\n client.requestLogger.logRequest(\n {\n timestamp: (Date.now() - responseTime) / 1000,\n method: request.method,\n path: route,\n url: request.url,\n headers: convertHeaders(\n Object.fromEntries(request.headers.entries()),\n ),\n size: requestSize,\n consumer: consumer?.identifier,\n body: requestBody,\n },\n {\n statusCode,\n responseTime: responseTime / 1000,\n headers: convertHeaders(responseHeaders),\n size: responseSize,\n body: capturedResponse.body,\n },\n error,\n logs,\n );\n }\n });\n\n // Handle validation errors\n if (\n (statusCode === 400 || statusCode === 422) &&\n error instanceof ValidationError\n ) {\n try {\n const parsedMessage = JSON.parse(error.message);\n client.validationErrorCounter.addValidationError({\n consumer: consumer?.identifier,\n method: request.method,\n path: route,\n loc:\n (parsedMessage.on ?? \"\") + \".\" + (parsedMessage.property ?? \"\"),\n msg: parsedMessage.message,\n type: \"\",\n });\n } catch (error) {\n // ignore\n }\n }\n\n // Handle server errors\n if (statusCode === 500 && error) {\n client.serverErrorCounter.addServerError({\n consumer: consumer?.identifier,\n method: request.method,\n path: route,\n type: error.name,\n msg: error.message,\n traceback: error.stack || \"\",\n });\n }\n })\n .onError(({ request, error }) => {\n if (client.isEnabled() && error instanceof Error) {\n request[ERROR_SYMBOL] = error;\n }\n });\n };\n}\n\nfunction getStatusCode(set: Context[\"set\"]) {\n if (typeof set.status === \"number\") {\n return set.status;\n } else if (typeof set.status === \"string\") {\n return StatusMap[set.status];\n }\n}\n"],"mappings":"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","names":["StatusMap","ValidationError","AsyncLocalStorage","performance","ApitallyClient","consumerFromStringOrObject","parseContentLength","convertHeaders","captureResponse","patchConsole","patchWinston","getAppInfo","START_TIME_SYMBOL","Symbol","REQUEST_BODY_SYMBOL","RESPONSE_SYMBOL","RESPONSE_PROMISE_SYMBOL","ERROR_SYMBOL","CLIENT_SYMBOL","REQUEST_SYMBOL","config","client","logsContext","requestLogger","enabled","captureLogs","app","handler","mapResponse","name","startsWith","originalMapResponse","originalMapCompactResponse","mapCompactResponse","originalMapEarlyResponse","mapEarlyResponse","captureMappedResponse","originalResponse","mappedResponse","set","request","Request","Response","responseBody","Buffer","from","Promise","resolve","body","size","length","completed","newResponse","responsePromise","captureBody","logResponseBody","maxBodySize","wrappedMapResponse","response","wrappedMapCompactResponse","undefined","wrappedMapEarlyResponse","decorate","onStart","appInfo","appVersion","setStartupData","startSync","onStop","handleShutdown","onRequest","isEnabled","method","toUpperCase","now","enterWith","logRequestBody","contentType","headers","get","requestSize","isSupportedContentType","clone","arrayBuffer","error","onAfterResponse","route","apitally","startTime","responseTime","requestBody","toResponse","errorResponseBody","statusCode","status","getStatusCode","statusText","Headers","consumer","consumerRegistry","addOrUpdateConsumer","then","capturedResponse","responseHeaders","responseSize","requestCounter","addRequest","identifier","path","logs","getStore","logRequest","timestamp","Date","url","Object","fromEntries","entries","parsedMessage","JSON","parse","message","validationErrorCounter","addValidationError","loc","on","property","msg","type","serverErrorCounter","addServerError","traceback","stack","onError","Error","apitallyPlugin"]}
1
+ {"version":3,"sources":["../../src/elysia/plugin.ts"],"sourcesContent":["import { Context, Elysia, StatusMap, ValidationError } from \"elysia\";\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 type { LogRecord } from \"../common/requestLogger.js\";\nimport { convertHeaders } from \"../common/requestLogger.js\";\nimport { CapturedResponse, captureResponse } from \"../common/response.js\";\nimport type { SpanHandle } from \"../common/spanCollector.js\";\nimport { ApitallyConfig, ApitallyConsumer } from \"../common/types.js\";\nimport { patchConsole, patchWinston } from \"../loggers/index.js\";\nimport { getAppInfo } from \"./utils.js\";\n\nconst START_TIME_SYMBOL = Symbol(\"apitally.startTime\");\nconst REQUEST_BODY_SYMBOL = Symbol(\"apitally.requestBody\");\nconst RESPONSE_SYMBOL = Symbol(\"apitally.response\");\nconst RESPONSE_PROMISE_SYMBOL = Symbol(\"apitally.responsePromise\");\nconst ERROR_SYMBOL = Symbol(\"apitally.error\");\nconst CLIENT_SYMBOL = Symbol(\"apitally.client\");\nconst REQUEST_SYMBOL = Symbol(\"apitally.request\");\nconst SPAN_HANDLE_SYMBOL = Symbol(\"apitally.spanHandle\");\n\ndeclare global {\n interface Request {\n [START_TIME_SYMBOL]?: number;\n [REQUEST_BODY_SYMBOL]?: Buffer;\n [RESPONSE_SYMBOL]?: Response;\n [RESPONSE_PROMISE_SYMBOL]?: Promise<CapturedResponse>;\n [ERROR_SYMBOL]?: Readonly<Error>;\n [CLIENT_SYMBOL]?: ApitallyClient;\n [SPAN_HANDLE_SYMBOL]?: SpanHandle;\n }\n}\n\ntype ContextSet = Context[\"set\"] & {\n [REQUEST_SYMBOL]?: Request;\n};\n\ninterface ApitallyContext {\n consumer?: ApitallyConsumer | string;\n}\n\nexport default function apitallyPlugin(config: ApitallyConfig) {\n const client = new ApitallyClient(config);\n const logsContext = new AsyncLocalStorage<LogRecord[]>();\n\n if (client.requestLogger.enabled && client.requestLogger.config.captureLogs) {\n patchConsole(logsContext);\n patchWinston(logsContext);\n }\n\n return (app: Elysia) => {\n const handler = app[\"~adapter\"].handler;\n\n if (!handler.mapResponse.name.startsWith(\"wrapped\")) {\n const originalMapResponse = handler.mapResponse;\n const originalMapCompactResponse = handler.mapCompactResponse;\n const originalMapEarlyResponse = handler.mapEarlyResponse;\n\n const captureMappedResponse = (\n originalResponse: unknown,\n mappedResponse: unknown,\n set?: ContextSet,\n ) => {\n const request = set?.[REQUEST_SYMBOL];\n if (\n request instanceof Request &&\n mappedResponse instanceof Response &&\n !(RESPONSE_SYMBOL in request) &&\n CLIENT_SYMBOL in request\n ) {\n if (typeof originalResponse === \"string\") {\n // Preserve the response body value as Blob if the original response is a string,\n // so that Bun adds a Content-Type header.\n const responseBody = Buffer.from(originalResponse as string);\n request[RESPONSE_SYMBOL] = mappedResponse;\n request[RESPONSE_PROMISE_SYMBOL] = Promise.resolve({\n body: responseBody,\n size: responseBody.length,\n completed: true,\n });\n } else {\n // Otherwise capture the response using streaming\n const client = request[CLIENT_SYMBOL]!;\n const [newResponse, responsePromise] = captureResponse(\n mappedResponse,\n {\n captureBody:\n client.requestLogger.enabled &&\n client.requestLogger.config.logResponseBody,\n maxBodySize: client.requestLogger.maxBodySize,\n },\n );\n request[RESPONSE_SYMBOL] = newResponse;\n request[RESPONSE_PROMISE_SYMBOL] = responsePromise;\n return newResponse;\n }\n }\n return mappedResponse;\n };\n\n handler.mapResponse = function wrappedMapResponse(\n response: unknown,\n set: ContextSet,\n ) {\n const mappedResponse = originalMapResponse(response, set);\n const newResponse = captureMappedResponse(\n response,\n mappedResponse,\n set,\n );\n return newResponse;\n };\n handler.mapCompactResponse = function wrappedMapCompactResponse(\n response: unknown,\n ) {\n const mappedResponse = originalMapCompactResponse(response);\n const newResponse = captureMappedResponse(\n response,\n mappedResponse,\n undefined,\n );\n return newResponse;\n };\n handler.mapEarlyResponse = function wrappedMapEarlyResponse(\n response: unknown,\n set: ContextSet,\n ) {\n const mappedResponse = originalMapEarlyResponse(response, set);\n const newResponse = captureMappedResponse(\n response,\n mappedResponse,\n set,\n );\n return newResponse;\n };\n }\n\n return app\n .decorate(\"apitally\", {} as ApitallyContext)\n .onStart(() => {\n const appInfo = getAppInfo(app, config.appVersion);\n client.setStartupData(appInfo);\n client.startSync();\n })\n .onStop(async () => {\n await client.handleShutdown();\n })\n .onRequest(async ({ request, set }) => {\n if (!client.isEnabled() || request.method.toUpperCase() === \"OPTIONS\") {\n return;\n }\n\n request[CLIENT_SYMBOL] = client;\n request[START_TIME_SYMBOL] = performance.now();\n (set as ContextSet)[REQUEST_SYMBOL] = request;\n logsContext.enterWith([]);\n\n // Start span and enter the context for subsequent handlers\n const spanHandle = client.spanCollector.startSpan();\n request[SPAN_HANDLE_SYMBOL] = spanHandle;\n spanHandle.enterContext();\n\n // Capture request body\n if (\n client.requestLogger.enabled &&\n client.requestLogger.config.logRequestBody\n ) {\n const contentType = request.headers.get(\"content-type\");\n const requestSize =\n parseContentLength(request.headers.get(\"content-length\")) ?? 0;\n\n if (\n client.requestLogger.isSupportedContentType(contentType) &&\n requestSize <= client.requestLogger.maxBodySize\n ) {\n try {\n request[REQUEST_BODY_SYMBOL] = Buffer.from(\n await request.clone().arrayBuffer(),\n );\n } catch (error) {\n // ignore\n }\n }\n }\n })\n .onAfterResponse(async ({ request, set, route, apitally }) => {\n if (!client.isEnabled() || request.method.toUpperCase() === \"OPTIONS\") {\n return;\n }\n\n const startTime = request[START_TIME_SYMBOL];\n const responseTime = startTime ? performance.now() - startTime : 0;\n\n const spanHandle = request[SPAN_HANDLE_SYMBOL];\n spanHandle?.setName(`${request.method} ${route}`);\n const spans = spanHandle?.end();\n const traceId = spanHandle?.traceId;\n\n const requestBody = request[REQUEST_BODY_SYMBOL];\n const requestSize =\n parseContentLength(request.headers.get(\"content-length\")) ??\n requestBody?.length;\n\n let responsePromise = request[RESPONSE_PROMISE_SYMBOL];\n let response = request[RESPONSE_SYMBOL];\n const error = request[ERROR_SYMBOL];\n\n if (\n !response &&\n error &&\n \"toResponse\" in error &&\n typeof error.toResponse === \"function\"\n ) {\n // Convert error to response\n try {\n response = error.toResponse() as Response;\n const errorResponseBody = Buffer.from(await response.arrayBuffer());\n responsePromise = Promise.resolve({\n body: errorResponseBody,\n size: errorResponseBody.length,\n completed: true,\n });\n } catch (error) {\n // ignore\n }\n }\n\n const statusCode = response?.status ?? getStatusCode(set) ?? 200;\n\n if (!response) {\n // Create empty fake response for errors without the toResponse method\n response = new Response(null, {\n status: statusCode,\n statusText: \"\",\n headers: new Headers(),\n });\n responsePromise = Promise.resolve({\n body: undefined,\n size: 0,\n completed: true,\n });\n }\n\n const consumer = apitally.consumer\n ? consumerFromStringOrObject(apitally.consumer)\n : null;\n client.consumerRegistry.addOrUpdateConsumer(consumer);\n\n // Log request when response has been fully captured\n responsePromise?.then(async (capturedResponse) => {\n const responseHeaders = response?.headers ?? set.headers;\n const responseSize = capturedResponse.completed\n ? capturedResponse.size\n : undefined;\n\n client.requestCounter.addRequest({\n consumer: consumer?.identifier,\n method: request.method,\n path: route,\n statusCode,\n responseTime,\n requestSize,\n responseSize,\n });\n\n if (client.requestLogger.enabled) {\n const logs = logsContext.getStore();\n client.requestLogger.logRequest(\n {\n timestamp: (Date.now() - responseTime) / 1000,\n method: request.method,\n path: route,\n url: request.url,\n headers: convertHeaders(\n Object.fromEntries(request.headers.entries()),\n ),\n size: requestSize,\n consumer: consumer?.identifier,\n body: requestBody,\n },\n {\n statusCode,\n responseTime: responseTime / 1000,\n headers: convertHeaders(responseHeaders),\n size: responseSize,\n body: capturedResponse.body,\n },\n error,\n logs,\n spans,\n traceId,\n );\n }\n });\n\n // Handle validation errors\n if (\n (statusCode === 400 || statusCode === 422) &&\n error instanceof ValidationError\n ) {\n try {\n const parsedMessage = JSON.parse(error.message);\n client.validationErrorCounter.addValidationError({\n consumer: consumer?.identifier,\n method: request.method,\n path: route,\n loc:\n (parsedMessage.on ?? \"\") + \".\" + (parsedMessage.property ?? \"\"),\n msg: parsedMessage.message,\n type: \"\",\n });\n } catch (error) {\n // ignore\n }\n }\n\n // Handle server errors\n if (statusCode === 500 && error) {\n client.serverErrorCounter.addServerError({\n consumer: consumer?.identifier,\n method: request.method,\n path: route,\n type: error.name,\n msg: error.message,\n traceback: error.stack || \"\",\n });\n }\n })\n .onError(({ request, error }) => {\n if (client.isEnabled() && error instanceof Error) {\n request[ERROR_SYMBOL] = error;\n }\n });\n };\n}\n\nfunction getStatusCode(set: Context[\"set\"]) {\n if (typeof set.status === \"number\") {\n return set.status;\n } else if (typeof set.status === \"string\") {\n return StatusMap[set.status];\n }\n}\n"],"mappings":"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","names":["StatusMap","ValidationError","AsyncLocalStorage","performance","ApitallyClient","consumerFromStringOrObject","parseContentLength","convertHeaders","captureResponse","patchConsole","patchWinston","getAppInfo","START_TIME_SYMBOL","Symbol","REQUEST_BODY_SYMBOL","RESPONSE_SYMBOL","RESPONSE_PROMISE_SYMBOL","ERROR_SYMBOL","CLIENT_SYMBOL","REQUEST_SYMBOL","SPAN_HANDLE_SYMBOL","config","client","logsContext","requestLogger","enabled","captureLogs","app","handler","mapResponse","name","startsWith","originalMapResponse","originalMapCompactResponse","mapCompactResponse","originalMapEarlyResponse","mapEarlyResponse","captureMappedResponse","originalResponse","mappedResponse","set","request","Request","Response","responseBody","Buffer","from","Promise","resolve","body","size","length","completed","newResponse","responsePromise","captureBody","logResponseBody","maxBodySize","wrappedMapResponse","response","wrappedMapCompactResponse","undefined","wrappedMapEarlyResponse","decorate","onStart","appInfo","appVersion","setStartupData","startSync","onStop","handleShutdown","onRequest","isEnabled","method","toUpperCase","now","enterWith","spanHandle","spanCollector","startSpan","enterContext","logRequestBody","contentType","headers","get","requestSize","isSupportedContentType","clone","arrayBuffer","error","onAfterResponse","route","apitally","startTime","responseTime","setName","spans","end","traceId","requestBody","toResponse","errorResponseBody","statusCode","status","getStatusCode","statusText","Headers","consumer","consumerRegistry","addOrUpdateConsumer","then","capturedResponse","responseHeaders","responseSize","requestCounter","addRequest","identifier","path","logs","getStore","logRequest","timestamp","Date","url","Object","fromEntries","entries","parsedMessage","JSON","parse","message","validationErrorCounter","addValidationError","loc","on","property","msg","type","serverErrorCounter","addServerError","traceback","stack","onError","Error","apitallyPlugin"]}
@@ -5,6 +5,8 @@ import 'winston';
5
5
  import '../common/requestLogger.cjs';
6
6
  import 'node:buffer';
7
7
  import 'node:http';
8
+ import '../common/spanCollector.cjs';
9
+ import '@opentelemetry/sdk-trace-base';
8
10
  import '../common/tempGzipFile.cjs';
9
11
 
10
12
  declare function getAppInfo(app: Elysia, appVersion?: string): StartupData;
@@ -5,6 +5,8 @@ import 'winston';
5
5
  import '../common/requestLogger.js';
6
6
  import 'node:buffer';
7
7
  import 'node:http';
8
+ import '../common/spanCollector.js';
9
+ import '@opentelemetry/sdk-trace-base';
8
10
  import '../common/tempGzipFile.js';
9
11
 
10
12
  declare function getAppInfo(app: Elysia, appVersion?: string): StartupData;
@@ -18,13 +18,16 @@ var __copyProps = (to, from, except, desc) => {
18
18
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
19
  var express_exports = {};
20
20
  __export(express_exports, {
21
+ ApitallySpanProcessor: () => import_spanCollector.ApitallySpanProcessor,
21
22
  setConsumer: () => import_middleware.setConsumer,
22
23
  useApitally: () => import_middleware.useApitally
23
24
  });
24
25
  module.exports = __toCommonJS(express_exports);
26
+ var import_spanCollector = require("../common/spanCollector.js");
25
27
  var import_middleware = require("./middleware.js");
26
28
  // Annotate the CommonJS export names for ESM import in node:
27
29
  0 && (module.exports = {
30
+ ApitallySpanProcessor,
28
31
  setConsumer,
29
32
  useApitally
30
33
  });
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/express/index.ts"],"sourcesContent":["export type { ApitallyConfig, ApitallyConsumer } from \"../common/types.js\";\nexport { setConsumer, useApitally } from \"./middleware.js\";\n"],"mappings":";;;;;;;;;;;;;;;;;;AACA;;;;;;wBAAyC;","names":[]}
1
+ {"version":3,"sources":["../../src/express/index.ts"],"sourcesContent":["export { ApitallySpanProcessor } from \"../common/spanCollector.js\";\nexport type { ApitallyConfig, ApitallyConsumer } from \"../common/types.js\";\nexport { setConsumer, useApitally } from \"./middleware.js\";\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;;;;;;;2BAAsC;AAEtC,wBAAyC;","names":[]}
@@ -1,5 +1,7 @@
1
+ export { ApitallySpanProcessor } from '../common/spanCollector.cjs';
1
2
  export { ApitallyConfig, ApitallyConsumer } from '../common/types.cjs';
2
3
  export { setConsumer, useApitally } from './middleware.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 { setConsumer, useApitally } from './middleware.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 { setConsumer, useApitally } from "./middleware.js";
2
3
  export {
4
+ ApitallySpanProcessor,
3
5
  setConsumer,
4
6
  useApitally
5
7
  };
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/express/index.ts"],"sourcesContent":["export type { ApitallyConfig, ApitallyConsumer } from \"../common/types.js\";\nexport { setConsumer, useApitally } from \"./middleware.js\";\n"],"mappings":"AACA,SAASA,aAAaC,mBAAmB;","names":["setConsumer","useApitally"]}
1
+ {"version":3,"sources":["../../src/express/index.ts"],"sourcesContent":["export { ApitallySpanProcessor } from \"../common/spanCollector.js\";\nexport type { ApitallyConfig, ApitallyConsumer } from \"../common/types.js\";\nexport { setConsumer, useApitally } from \"./middleware.js\";\n"],"mappings":"AAAA,SAASA,6BAA6B;AAEtC,SAASC,aAAaC,mBAAmB;","names":["ApitallySpanProcessor","setConsumer","useApitally"]}
@@ -82,98 +82,106 @@ function getMiddleware(app, client) {
82
82
  }
83
83
  return originalSend.call(res, body);
84
84
  };
85
- res.once("finish", () => {
86
- try {
87
- const responseTime = import_node_perf_hooks.performance.now() - startTime;
88
- const path = getRoutePath(req);
89
- const consumer = getConsumer(req);
90
- client.consumerRegistry.addOrUpdateConsumer(consumer);
91
- const requestSize = (0, import_headers.parseContentLength)(req.get("content-length"));
92
- const responseSize = (0, import_headers.parseContentLength)(res.get("content-length"));
93
- if (path) {
94
- client.requestCounter.addRequest({
95
- consumer: consumer == null ? void 0 : consumer.identifier,
96
- method: req.method,
97
- path,
98
- statusCode: res.statusCode,
99
- responseTime,
100
- requestSize,
101
- responseSize
102
- });
103
- if ((res.statusCode === 400 || res.statusCode === 422) && res.locals.body) {
104
- let jsonBody;
105
- try {
106
- jsonBody = JSON.parse(res.locals.body);
107
- } catch {
108
- }
109
- if (jsonBody) {
110
- const validationErrors = [];
111
- if (validationErrors.length === 0) {
112
- validationErrors.push(...extractExpressValidatorErrors(jsonBody));
113
- }
114
- if (validationErrors.length === 0) {
115
- validationErrors.push(...extractCelebrateErrors(jsonBody));
116
- }
117
- if (validationErrors.length === 0) {
118
- validationErrors.push(...extractNestValidationErrors(jsonBody));
85
+ const spanHandle = client.spanCollector.startSpan();
86
+ spanHandle.runInContext(() => {
87
+ res.once("finish", () => {
88
+ try {
89
+ const responseTime = import_node_perf_hooks.performance.now() - startTime;
90
+ const path = getRoutePath(req);
91
+ spanHandle.setName(`${req.method} ${path}`);
92
+ const spans = spanHandle.end();
93
+ const traceId = spanHandle.traceId;
94
+ const consumer = getConsumer(req);
95
+ client.consumerRegistry.addOrUpdateConsumer(consumer);
96
+ const requestSize = (0, import_headers.parseContentLength)(req.get("content-length"));
97
+ const responseSize = (0, import_headers.parseContentLength)(res.get("content-length"));
98
+ if (path) {
99
+ client.requestCounter.addRequest({
100
+ consumer: consumer == null ? void 0 : consumer.identifier,
101
+ method: req.method,
102
+ path,
103
+ statusCode: res.statusCode,
104
+ responseTime,
105
+ requestSize,
106
+ responseSize
107
+ });
108
+ if ((res.statusCode === 400 || res.statusCode === 422) && res.locals.body) {
109
+ let jsonBody;
110
+ try {
111
+ jsonBody = JSON.parse(res.locals.body);
112
+ } catch {
119
113
  }
120
- validationErrors.forEach((error) => {
121
- client.validationErrorCounter.addValidationError({
122
- consumer: consumer == null ? void 0 : consumer.identifier,
123
- method: req.method,
124
- path,
125
- ...error
114
+ if (jsonBody) {
115
+ const validationErrors = [];
116
+ if (validationErrors.length === 0) {
117
+ validationErrors.push(...extractExpressValidatorErrors(jsonBody));
118
+ }
119
+ if (validationErrors.length === 0) {
120
+ validationErrors.push(...extractCelebrateErrors(jsonBody));
121
+ }
122
+ if (validationErrors.length === 0) {
123
+ validationErrors.push(...extractNestValidationErrors(jsonBody));
124
+ }
125
+ validationErrors.forEach((error) => {
126
+ client.validationErrorCounter.addValidationError({
127
+ consumer: consumer == null ? void 0 : consumer.identifier,
128
+ method: req.method,
129
+ path,
130
+ ...error
131
+ });
126
132
  });
133
+ }
134
+ }
135
+ if (res.statusCode === 500 && res.locals.serverError) {
136
+ const serverError = res.locals.serverError;
137
+ client.serverErrorCounter.addServerError({
138
+ consumer: consumer == null ? void 0 : consumer.identifier,
139
+ method: req.method,
140
+ path,
141
+ type: serverError.name,
142
+ msg: serverError.message,
143
+ traceback: serverError.stack || ""
127
144
  });
128
145
  }
129
146
  }
130
- if (res.statusCode === 500 && res.locals.serverError) {
131
- const serverError = res.locals.serverError;
132
- client.serverErrorCounter.addServerError({
133
- consumer: consumer == null ? void 0 : consumer.identifier,
147
+ if (client.requestLogger.enabled) {
148
+ const logs = logsContext.getStore();
149
+ client.requestLogger.logRequest({
150
+ timestamp: Date.now() / 1e3,
134
151
  method: req.method,
135
152
  path,
136
- type: serverError.name,
137
- msg: serverError.message,
138
- traceback: serverError.stack || ""
139
- });
153
+ url: `${req.protocol}://${req.host}${req.originalUrl}`,
154
+ headers: (0, import_requestLogger.convertHeaders)(req.headers),
155
+ size: requestSize,
156
+ consumer: consumer == null ? void 0 : consumer.identifier,
157
+ body: (0, import_requestLogger.convertBody)(req.body, req.get("content-type"))
158
+ }, {
159
+ statusCode: res.statusCode,
160
+ responseTime: responseTime / 1e3,
161
+ headers: (0, import_requestLogger.convertHeaders)(res.getHeaders()),
162
+ size: responseSize,
163
+ body: (0, import_requestLogger.convertBody)(res.locals.body, res.get("content-type"))
164
+ }, res.locals.serverError, logs, spans, traceId);
140
165
  }
166
+ } catch (error) {
167
+ client.logger.error("Error while logging request in Apitally middleware", {
168
+ request: req,
169
+ response: res,
170
+ error
171
+ });
141
172
  }
142
- if (client.requestLogger.enabled) {
143
- const logs = logsContext.getStore();
144
- client.requestLogger.logRequest({
145
- timestamp: Date.now() / 1e3,
146
- method: req.method,
147
- path,
148
- url: `${req.protocol}://${req.host}${req.originalUrl}`,
149
- headers: (0, import_requestLogger.convertHeaders)(req.headers),
150
- size: requestSize,
151
- consumer: consumer == null ? void 0 : consumer.identifier,
152
- body: (0, import_requestLogger.convertBody)(req.body, req.get("content-type"))
153
- }, {
154
- statusCode: res.statusCode,
155
- responseTime: responseTime / 1e3,
156
- headers: (0, import_requestLogger.convertHeaders)(res.getHeaders()),
157
- size: responseSize,
158
- body: (0, import_requestLogger.convertBody)(res.locals.body, res.get("content-type"))
159
- }, res.locals.serverError, logs);
160
- }
161
- } catch (error) {
162
- client.logger.error("Error while logging request in Apitally middleware.", {
163
- request: req,
164
- response: res,
165
- error
166
- });
167
- }
173
+ });
174
+ res.once("close", () => {
175
+ spanHandle.end();
176
+ });
177
+ next();
168
178
  });
169
179
  } catch (error) {
170
- client.logger.error("Error in Apitally middleware.", {
180
+ client.logger.error("Error in Apitally middleware", {
171
181
  request: req,
172
182
  response: res,
173
183
  error
174
184
  });
175
- } finally {
176
- next();
177
185
  }
178
186
  });
179
187
  };