apitally 0.24.0 → 0.25.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (163) hide show
  1. package/dist/adonisjs/configure.cjs +1 -1
  2. package/dist/adonisjs/index.cjs +4 -1
  3. package/dist/adonisjs/index.cjs.map +1 -1
  4. package/dist/adonisjs/index.d.cts +2 -0
  5. package/dist/adonisjs/index.d.ts +2 -0
  6. package/dist/adonisjs/index.js +2 -0
  7. package/dist/adonisjs/index.js.map +1 -1
  8. package/dist/adonisjs/middleware.cjs +9 -5
  9. package/dist/adonisjs/middleware.cjs.map +1 -1
  10. package/dist/adonisjs/middleware.d.cts +2 -0
  11. package/dist/adonisjs/middleware.d.ts +2 -0
  12. package/dist/adonisjs/middleware.js +6 -2
  13. package/dist/adonisjs/middleware.js.map +1 -1
  14. package/dist/adonisjs/provider.cjs +3 -3
  15. package/dist/adonisjs/provider.d.cts +2 -0
  16. package/dist/adonisjs/provider.d.ts +2 -0
  17. package/dist/adonisjs/stubs/config/apitally.stub +1 -0
  18. package/dist/common/client.cjs +13 -9
  19. package/dist/common/client.cjs.map +1 -1
  20. package/dist/common/client.d.cts +3 -0
  21. package/dist/common/client.d.ts +3 -0
  22. package/dist/common/client.js +4 -0
  23. package/dist/common/client.js.map +1 -1
  24. package/dist/common/consumerRegistry.d.cts +2 -0
  25. package/dist/common/consumerRegistry.d.ts +2 -0
  26. package/dist/common/requestCounter.d.cts +2 -0
  27. package/dist/common/requestCounter.d.ts +2 -0
  28. package/dist/common/requestLogger.cjs +14 -5
  29. package/dist/common/requestLogger.cjs.map +1 -1
  30. package/dist/common/requestLogger.d.cts +6 -1
  31. package/dist/common/requestLogger.d.ts +6 -1
  32. package/dist/common/requestLogger.js +11 -2
  33. package/dist/common/requestLogger.js.map +1 -1
  34. package/dist/common/serverErrorCounter.cjs +1 -1
  35. package/dist/common/serverErrorCounter.d.cts +2 -0
  36. package/dist/common/serverErrorCounter.d.ts +2 -0
  37. package/dist/common/spanCollector.cjs +217 -0
  38. package/dist/common/spanCollector.cjs.map +1 -0
  39. package/dist/common/spanCollector.d.cts +50 -0
  40. package/dist/common/spanCollector.d.ts +50 -0
  41. package/dist/common/spanCollector.js +194 -0
  42. package/dist/common/spanCollector.js.map +1 -0
  43. package/dist/common/types.d.cts +2 -0
  44. package/dist/common/types.d.ts +2 -0
  45. package/dist/common/validationErrorCounter.d.cts +2 -0
  46. package/dist/common/validationErrorCounter.d.ts +2 -0
  47. package/dist/elysia/index.cjs +4 -1
  48. package/dist/elysia/index.cjs.map +1 -1
  49. package/dist/elysia/index.d.cts +2 -0
  50. package/dist/elysia/index.d.ts +2 -0
  51. package/dist/elysia/index.js +2 -0
  52. package/dist/elysia/index.js.map +1 -1
  53. package/dist/elysia/plugin.cjs +16 -8
  54. package/dist/elysia/plugin.cjs.map +1 -1
  55. package/dist/elysia/plugin.d.cts +4 -0
  56. package/dist/elysia/plugin.d.ts +4 -0
  57. package/dist/elysia/plugin.js +9 -1
  58. package/dist/elysia/plugin.js.map +1 -1
  59. package/dist/elysia/utils.cjs +1 -1
  60. package/dist/elysia/utils.d.cts +2 -0
  61. package/dist/elysia/utils.d.ts +2 -0
  62. package/dist/express/index.cjs +4 -1
  63. package/dist/express/index.cjs.map +1 -1
  64. package/dist/express/index.d.cts +2 -0
  65. package/dist/express/index.d.ts +2 -0
  66. package/dist/express/index.js +2 -0
  67. package/dist/express/index.js.map +1 -1
  68. package/dist/express/middleware.cjs +92 -84
  69. package/dist/express/middleware.cjs.map +1 -1
  70. package/dist/express/middleware.d.cts +2 -0
  71. package/dist/express/middleware.d.ts +2 -0
  72. package/dist/express/middleware.js +85 -77
  73. package/dist/express/middleware.js.map +1 -1
  74. package/dist/fastify/index.cjs +4 -1
  75. package/dist/fastify/index.cjs.map +1 -1
  76. package/dist/fastify/index.d.cts +2 -0
  77. package/dist/fastify/index.d.ts +2 -0
  78. package/dist/fastify/index.js +2 -0
  79. package/dist/fastify/index.js.map +1 -1
  80. package/dist/fastify/plugin.cjs +20 -11
  81. package/dist/fastify/plugin.cjs.map +1 -1
  82. package/dist/fastify/plugin.d.cts +4 -0
  83. package/dist/fastify/plugin.d.ts +4 -0
  84. package/dist/fastify/plugin.js +14 -5
  85. package/dist/fastify/plugin.js.map +1 -1
  86. package/dist/h3/index.cjs +4 -1
  87. package/dist/h3/index.cjs.map +1 -1
  88. package/dist/h3/index.d.cts +2 -0
  89. package/dist/h3/index.d.ts +2 -0
  90. package/dist/h3/index.js +2 -0
  91. package/dist/h3/index.js.map +1 -1
  92. package/dist/h3/plugin.cjs +20 -12
  93. package/dist/h3/plugin.cjs.map +1 -1
  94. package/dist/h3/plugin.d.cts +4 -0
  95. package/dist/h3/plugin.d.ts +4 -0
  96. package/dist/h3/plugin.js +13 -5
  97. package/dist/h3/plugin.js.map +1 -1
  98. package/dist/h3/utils.cjs +1 -1
  99. package/dist/hapi/index.cjs +4 -1
  100. package/dist/hapi/index.cjs.map +1 -1
  101. package/dist/hapi/index.d.cts +2 -0
  102. package/dist/hapi/index.d.ts +2 -0
  103. package/dist/hapi/index.js +2 -0
  104. package/dist/hapi/index.js.map +1 -1
  105. package/dist/hapi/plugin.cjs +15 -8
  106. package/dist/hapi/plugin.cjs.map +1 -1
  107. package/dist/hapi/plugin.d.cts +4 -0
  108. package/dist/hapi/plugin.d.ts +4 -0
  109. package/dist/hapi/plugin.js +10 -3
  110. package/dist/hapi/plugin.js.map +1 -1
  111. package/dist/hapi/utils.cjs +1 -1
  112. package/dist/hapi/utils.d.cts +2 -0
  113. package/dist/hapi/utils.d.ts +2 -0
  114. package/dist/hono/index.cjs +4 -1
  115. package/dist/hono/index.cjs.map +1 -1
  116. package/dist/hono/index.d.cts +2 -0
  117. package/dist/hono/index.d.ts +2 -0
  118. package/dist/hono/index.js +2 -0
  119. package/dist/hono/index.js.map +1 -1
  120. package/dist/hono/middleware.cjs +13 -9
  121. package/dist/hono/middleware.cjs.map +1 -1
  122. package/dist/hono/middleware.d.cts +2 -0
  123. package/dist/hono/middleware.d.ts +2 -0
  124. package/dist/hono/middleware.js +6 -2
  125. package/dist/hono/middleware.js.map +1 -1
  126. package/dist/hono/utils.cjs +1 -1
  127. package/dist/hono/utils.d.cts +2 -0
  128. package/dist/hono/utils.d.ts +2 -0
  129. package/dist/koa/index.cjs +4 -1
  130. package/dist/koa/index.cjs.map +1 -1
  131. package/dist/koa/index.d.cts +2 -0
  132. package/dist/koa/index.d.ts +2 -0
  133. package/dist/koa/index.js +2 -0
  134. package/dist/koa/index.js.map +1 -1
  135. package/dist/koa/middleware.cjs +13 -9
  136. package/dist/koa/middleware.cjs.map +1 -1
  137. package/dist/koa/middleware.d.cts +2 -0
  138. package/dist/koa/middleware.d.ts +2 -0
  139. package/dist/koa/middleware.js +8 -4
  140. package/dist/koa/middleware.js.map +1 -1
  141. package/dist/loggers/console.d.cts +4 -0
  142. package/dist/loggers/console.d.ts +4 -0
  143. package/dist/loggers/hapi.d.cts +4 -0
  144. package/dist/loggers/hapi.d.ts +4 -0
  145. package/dist/loggers/index.cjs +5 -5
  146. package/dist/loggers/index.d.cts +4 -0
  147. package/dist/loggers/index.d.ts +4 -0
  148. package/dist/loggers/nestjs.cjs +1 -1
  149. package/dist/loggers/nestjs.d.cts +4 -0
  150. package/dist/loggers/nestjs.d.ts +4 -0
  151. package/dist/loggers/pino.cjs +1 -1
  152. package/dist/loggers/pino.d.cts +4 -0
  153. package/dist/loggers/pino.d.ts +4 -0
  154. package/dist/loggers/winston.cjs +1 -1
  155. package/dist/loggers/winston.d.cts +4 -0
  156. package/dist/loggers/winston.d.ts +4 -0
  157. package/dist/nestjs/index.cjs +3 -0
  158. package/dist/nestjs/index.cjs.map +1 -1
  159. package/dist/nestjs/index.d.cts +2 -0
  160. package/dist/nestjs/index.d.ts +2 -0
  161. package/dist/nestjs/index.js +2 -0
  162. package/dist/nestjs/index.js.map +1 -1
  163. package/package.json +6 -2
@@ -26,15 +26,16 @@ module.exports = __toCommonJS(plugin_exports);
26
26
  var import_h3 = require("h3");
27
27
  var import_node_async_hooks = require("node:async_hooks");
28
28
  var import_node_perf_hooks = require("node:perf_hooks");
29
- var import_client = require("../common/client.js");
30
- var import_consumerRegistry = require("../common/consumerRegistry.js");
31
- var import_headers = require("../common/headers.js");
32
- var import_requestLogger = require("../common/requestLogger.js");
33
- var import_response = require("../common/response.js");
34
- var import_loggers = require("../loggers/index.js");
35
- var import_utils = require("./utils.js");
29
+ var import_client = require('../common/client.cjs');
30
+ var import_consumerRegistry = require('../common/consumerRegistry.cjs');
31
+ var import_headers = require('../common/headers.cjs');
32
+ var import_requestLogger = require('../common/requestLogger.cjs');
33
+ var import_response = require('../common/response.cjs');
34
+ var import_loggers = require('../loggers/index.cjs');
35
+ var import_utils = require('./utils.cjs');
36
36
  const REQUEST_TIMESTAMP_SYMBOL = /* @__PURE__ */ Symbol("apitally.requestTimestamp");
37
37
  const REQUEST_BODY_SYMBOL = /* @__PURE__ */ Symbol("apitally.requestBody");
38
+ const SPAN_HANDLE_SYMBOL = /* @__PURE__ */ Symbol("apitally.spanHandle");
38
39
  const jsonHeaders = new Headers({
39
40
  "content-type": "application/json;charset=UTF-8"
40
41
  });
@@ -56,7 +57,7 @@ const apitallyPlugin = (0, import_h3.definePlugin)((app, config) => {
56
57
  (0, import_loggers.patchWinston)(logsContext);
57
58
  }
58
59
  const handleResponse = /* @__PURE__ */ __name(async (event, response, error) => {
59
- var _a, _b;
60
+ var _a;
60
61
  if (event.req.method.toUpperCase() === "OPTIONS") {
61
62
  return response;
62
63
  }
@@ -77,6 +78,10 @@ const apitallyPlugin = (0, import_h3.definePlugin)((app, config) => {
77
78
  });
78
79
  responsePromise.then(async (capturedResponse) => {
79
80
  const responseTime = startTime ? import_node_perf_hooks.performance.now() - startTime : 0;
81
+ const spanHandle = event.context[SPAN_HANDLE_SYMBOL];
82
+ spanHandle == null ? void 0 : spanHandle.setName(`${event.req.method} ${path}`);
83
+ const spans = spanHandle == null ? void 0 : spanHandle.end();
84
+ const traceId = spanHandle == null ? void 0 : spanHandle.traceId;
80
85
  const responseSize = capturedResponse.completed ? capturedResponse.size : void 0;
81
86
  const requestSize = (0, import_headers.parseContentLength)(event.req.headers.get("content-length"));
82
87
  if (path) {
@@ -107,12 +112,12 @@ const apitallyPlugin = (0, import_h3.definePlugin)((app, config) => {
107
112
  headers: (0, import_requestLogger.convertHeaders)(Object.fromEntries(response.headers.entries())),
108
113
  size: responseSize,
109
114
  body: capturedResponse.body
110
- }, (error == null ? void 0 : error.cause) instanceof Error ? error.cause : void 0, logs);
115
+ }, (error == null ? void 0 : error.cause) instanceof Error ? error.cause : void 0, logs, spans, traceId);
111
116
  }
112
117
  });
113
- if (path && (error == null ? void 0 : error.status) === 400 && error.data && error.data.name === "ZodError") {
114
- const zodError = error.data;
115
- (_b = zodError.issues) == null ? void 0 : _b.forEach((issue) => {
118
+ if (path && (error == null ? void 0 : error.status) === 400 && error.data && Array.isArray(error.data.issues)) {
119
+ const issues = error.data.issues;
120
+ issues.forEach((issue) => {
116
121
  client.validationErrorCounter.addValidationError({
117
122
  consumer: consumer == null ? void 0 : consumer.identifier,
118
123
  method: event.req.method,
@@ -138,6 +143,9 @@ const apitallyPlugin = (0, import_h3.definePlugin)((app, config) => {
138
143
  app.use((0, import_h3.onRequest)(async (event) => {
139
144
  logsContext.enterWith([]);
140
145
  event.context[REQUEST_TIMESTAMP_SYMBOL] = import_node_perf_hooks.performance.now();
146
+ const spanHandle = client.spanCollector.startSpan();
147
+ event.context[SPAN_HANDLE_SYMBOL] = spanHandle;
148
+ spanHandle.enterContext();
141
149
  const requestContentType = event.req.headers.get("content-type");
142
150
  const requestSize = (0, import_headers.parseContentLength)(event.req.headers.get("content-length")) ?? 0;
143
151
  if (client.requestLogger.enabled && client.requestLogger.config.logRequestBody && client.requestLogger.isSupportedContentType(requestContentType) && requestSize <= client.requestLogger.maxBodySize) {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/h3/plugin.ts"],"sourcesContent":["import type { H3Event, HTTPError } from \"h3\";\nimport { definePlugin, onError, onRequest, onResponse } from \"h3\";\nimport { AsyncLocalStorage } from \"node:async_hooks\";\nimport { performance } from \"node:perf_hooks\";\nimport type { ZodError } from \"zod\";\n\nimport { ApitallyClient } from \"../common/client.js\";\nimport { consumerFromStringOrObject } from \"../common/consumerRegistry.js\";\nimport { mergeHeaders, parseContentLength } from \"../common/headers.js\";\nimport type { LogRecord } from \"../common/requestLogger.js\";\nimport { convertHeaders } from \"../common/requestLogger.js\";\nimport { 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 REQUEST_TIMESTAMP_SYMBOL = Symbol(\"apitally.requestTimestamp\");\nconst REQUEST_BODY_SYMBOL = Symbol(\"apitally.requestBody\");\n\ndeclare module \"h3\" {\n interface H3EventContext {\n apitallyConsumer?: ApitallyConsumer | string;\n\n [REQUEST_TIMESTAMP_SYMBOL]?: number;\n [REQUEST_BODY_SYMBOL]?: Buffer;\n }\n}\n\nconst jsonHeaders = new Headers({\n \"content-type\": \"application/json;charset=UTF-8\",\n});\n\nexport const apitallyPlugin = definePlugin<ApitallyConfig>((app, config) => {\n const client = new ApitallyClient(config);\n const logsContext = new AsyncLocalStorage<LogRecord[]>();\n\n const setStartupData = (attempt: number = 1) => {\n const appInfo = getAppInfo(app, 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 if (client.requestLogger.config.captureLogs) {\n patchConsole(logsContext);\n patchWinston(logsContext);\n }\n\n const handleResponse = async (\n event: H3Event,\n response?: Response,\n error?: HTTPError,\n ) => {\n if (event.req.method.toUpperCase() === \"OPTIONS\") {\n return response;\n }\n\n const startTime = event.context[REQUEST_TIMESTAMP_SYMBOL];\n const path = event.context.matchedRoute?.route;\n\n const consumer = getConsumer(event);\n client.consumerRegistry.addOrUpdateConsumer(consumer);\n\n if (!response) {\n response = new Response(null, {\n status: error?.status || 500,\n statusText: error?.statusText || \"Internal Server Error\",\n headers: error?.headers\n ? mergeHeaders(jsonHeaders, error.headers)\n : jsonHeaders,\n });\n }\n\n const [newResponse, responsePromise] = captureResponse(response, {\n captureBody:\n client.requestLogger.enabled &&\n client.requestLogger.config.logResponseBody,\n maxBodySize: client.requestLogger.maxBodySize,\n });\n\n responsePromise.then(async (capturedResponse) => {\n const responseTime = startTime ? performance.now() - startTime : 0;\n const responseSize = capturedResponse.completed\n ? capturedResponse.size\n : undefined;\n const requestSize = parseContentLength(\n event.req.headers.get(\"content-length\"),\n );\n\n if (path) {\n client.requestCounter.addRequest({\n consumer: consumer?.identifier,\n method: event.req.method,\n path,\n statusCode: response.status,\n responseTime,\n requestSize,\n responseSize,\n });\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: event.req.method,\n path,\n url: event.req.url,\n headers: convertHeaders(\n Object.fromEntries(event.req.headers.entries()),\n ),\n size: requestSize,\n consumer: consumer?.identifier,\n body: event.context[REQUEST_BODY_SYMBOL],\n },\n {\n statusCode: response.status,\n responseTime: responseTime / 1000,\n headers: convertHeaders(\n Object.fromEntries(response.headers.entries()),\n ),\n size: responseSize,\n body: capturedResponse.body,\n },\n error?.cause instanceof Error ? error.cause : undefined,\n logs,\n );\n }\n });\n\n if (\n path &&\n error?.status === 400 &&\n error.data &&\n (error.data as any).name === \"ZodError\"\n ) {\n const zodError = error.data as ZodError;\n zodError.issues?.forEach((issue) => {\n client.validationErrorCounter.addValidationError({\n consumer: consumer?.identifier,\n method: event.req.method,\n path,\n loc: issue.path.join(\".\"),\n msg: issue.message,\n type: issue.code,\n });\n });\n }\n\n if (path && error?.status === 500 && error.cause instanceof Error) {\n client.serverErrorCounter.addServerError({\n consumer: consumer?.identifier,\n method: event.req.method,\n path,\n type: error.cause.name,\n msg: error.cause.message,\n traceback: error.cause.stack || \"\",\n });\n }\n\n return newResponse;\n };\n\n app\n .use(\n onRequest(async (event) => {\n logsContext.enterWith([]);\n event.context[REQUEST_TIMESTAMP_SYMBOL] = performance.now();\n const requestContentType = event.req.headers.get(\"content-type\");\n const requestSize =\n parseContentLength(event.req.headers.get(\"content-length\")) ?? 0;\n\n if (\n client.requestLogger.enabled &&\n client.requestLogger.config.logRequestBody &&\n client.requestLogger.isSupportedContentType(requestContentType) &&\n requestSize <= client.requestLogger.maxBodySize\n ) {\n const clonedRequest = event.req.clone();\n const requestBody = Buffer.from(await clonedRequest.arrayBuffer());\n event.context[REQUEST_BODY_SYMBOL] = requestBody;\n }\n }),\n )\n .use(\n onResponse((response, event) => {\n if (client.isEnabled()) {\n return handleResponse(event, response, undefined);\n }\n }),\n )\n .use(\n onError((error, event) => {\n if (client.isEnabled()) {\n handleResponse(event, undefined, error);\n }\n }),\n );\n});\n\nexport function setConsumer(\n event: H3Event,\n consumer: ApitallyConsumer | string | null | undefined,\n) {\n event.context.apitallyConsumer = consumer || undefined;\n}\n\nfunction getConsumer(event: H3Event) {\n const consumer = event.context.apitallyConsumer;\n if (consumer) {\n return consumerFromStringOrObject(consumer);\n }\n return null;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AACA;;;;;;AAAA,gBAA6D;AAC7D,8BAAkC;AAClC,6BAA4B;AAG5B,oBAA+B;AAC/B,8BAA2C;AAC3C,qBAAiD;AAEjD,2BAA+B;AAC/B,sBAAgC;AAEhC,qBAA2C;AAC3C,mBAA2B;AAE3B,MAAMA,2BAA2BC,uBAAO,2BAAA;AACxC,MAAMC,sBAAsBD,uBAAO,sBAAA;AAWnC,MAAME,cAAc,IAAIC,QAAQ;EAC9B,gBAAgB;AAClB,CAAA;AAEO,MAAMC,qBAAiBC,wBAA6B,CAACC,KAAKC,WAAAA;AAC/D,QAAMC,SAAS,IAAIC,6BAAeF,MAAAA;AAClC,QAAMG,cAAc,IAAIC,0CAAAA;AAExB,QAAMC,iBAAiB,wBAACC,UAAkB,MAAC;AACzC,UAAMC,cAAUC,yBAAWT,KAAKC,OAAOS,UAAU;AACjD,QAAIF,QAAQG,MAAMC,SAAS,KAAKL,WAAW,IAAI;AAC7CL,aAAOI,eAAeE,OAAAA;AACtBN,aAAOW,UAAS;IAClB,OAAO;AACLC,iBAAW,MAAMR,eAAeC,UAAU,CAAA,GAAI,GAAA;IAChD;EACF,GARuB;AASvBO,aAAW,MAAMR,eAAAA,GAAkB,GAAA;AAEnC,MAAIJ,OAAOa,cAAcd,OAAOe,aAAa;AAC3CC,qCAAab,WAAAA;AACbc,qCAAad,WAAAA;EACf;AAEA,QAAMe,iBAAiB,8BACrBC,OACAC,UACAC,UAAAA;AAtDJ;AAwDI,QAAIF,MAAMG,IAAIC,OAAOC,YAAW,MAAO,WAAW;AAChD,aAAOJ;IACT;AAEA,UAAMK,YAAYN,MAAMO,QAAQlC,wBAAAA;AAChC,UAAMmC,QAAOR,WAAMO,QAAQE,iBAAdT,mBAA4BU;AAEzC,UAAMC,WAAWC,YAAYZ,KAAAA;AAC7BlB,WAAO+B,iBAAiBC,oBAAoBH,QAAAA;AAE5C,QAAI,CAACV,UAAU;AACbA,iBAAW,IAAIc,SAAS,MAAM;QAC5BC,SAAQd,+BAAOc,WAAU;QACzBC,aAAYf,+BAAOe,eAAc;QACjCC,UAAShB,+BAAOgB,eACZC,6BAAa3C,aAAa0B,MAAMgB,OAAO,IACvC1C;MACN,CAAA;IACF;AAEA,UAAM,CAAC4C,aAAaC,eAAAA,QAAmBC,iCAAgBrB,UAAU;MAC/DsB,aACEzC,OAAOa,cAAc6B,WACrB1C,OAAOa,cAAcd,OAAO4C;MAC9BC,aAAa5C,OAAOa,cAAc+B;IACpC,CAAA;AAEAL,oBAAgBM,KAAK,OAAOC,qBAAAA;AAC1B,YAAMC,eAAevB,YAAYwB,mCAAYC,IAAG,IAAKzB,YAAY;AACjE,YAAM0B,eAAeJ,iBAAiBK,YAClCL,iBAAiBM,OACjBC;AACJ,YAAMC,kBAAcC,mCAClBrC,MAAMG,IAAIe,QAAQoB,IAAI,gBAAA,CAAA;AAGxB,UAAI9B,MAAM;AACR1B,eAAOyD,eAAeC,WAAW;UAC/B7B,UAAUA,qCAAU8B;UACpBrC,QAAQJ,MAAMG,IAAIC;UAClBI;UACAkC,YAAYzC,SAASe;UACrBa;UACAO;UACAJ;QACF,CAAA;MACF;AAEA,UAAIlD,OAAOa,cAAc6B,SAAS;AAChC,cAAMmB,OAAO3D,YAAY4D,SAAQ;AACjC9D,eAAOa,cAAckD,WACnB;UACEC,YAAYC,KAAKhB,IAAG,IAAKF,gBAAgB;UACzCzB,QAAQJ,MAAMG,IAAIC;UAClBI;UACAwC,KAAKhD,MAAMG,IAAI6C;UACf9B,aAAS+B,qCACPC,OAAOC,YAAYnD,MAAMG,IAAIe,QAAQkC,QAAO,CAAA,CAAA;UAE9ClB,MAAME;UACNzB,UAAUA,qCAAU8B;UACpBY,MAAMrD,MAAMO,QAAQhC,mBAAAA;QACtB,GACA;UACEmE,YAAYzC,SAASe;UACrBa,cAAcA,eAAe;UAC7BX,aAAS+B,qCACPC,OAAOC,YAAYlD,SAASiB,QAAQkC,QAAO,CAAA,CAAA;UAE7ClB,MAAMF;UACNqB,MAAMzB,iBAAiByB;QACzB,IACAnD,+BAAOoD,kBAAiBC,QAAQrD,MAAMoD,QAAQnB,QAC9CQ,IAAAA;MAEJ;IACF,CAAA;AAEA,QACEnC,SACAN,+BAAOc,YAAW,OAClBd,MAAMsD,QACLtD,MAAMsD,KAAaC,SAAS,YAC7B;AACA,YAAMC,WAAWxD,MAAMsD;AACvBE,qBAASC,WAATD,mBAAiBE,QAAQ,CAACC,UAAAA;AACxB/E,eAAOgF,uBAAuBC,mBAAmB;UAC/CpD,UAAUA,qCAAU8B;UACpBrC,QAAQJ,MAAMG,IAAIC;UAClBI;UACAwD,KAAKH,MAAMrD,KAAKyD,KAAK,GAAA;UACrBC,KAAKL,MAAMM;UACXC,MAAMP,MAAMQ;QACd,CAAA;MACF;IACF;AAEA,QAAI7D,SAAQN,+BAAOc,YAAW,OAAOd,MAAMoD,iBAAiBC,OAAO;AACjEzE,aAAOwF,mBAAmBC,eAAe;QACvC5D,UAAUA,qCAAU8B;QACpBrC,QAAQJ,MAAMG,IAAIC;QAClBI;QACA4D,MAAMlE,MAAMoD,MAAMG;QAClBS,KAAKhE,MAAMoD,MAAMa;QACjBK,WAAWtE,MAAMoD,MAAMmB,SAAS;MAClC,CAAA;IACF;AAEA,WAAOrD;EACT,GAlHuB;AAoHvBxC,MACG8F,QACCC,qBAAU,OAAO3E,UAAAA;AACfhB,gBAAY4F,UAAU,CAAA,CAAE;AACxB5E,UAAMO,QAAQlC,wBAAAA,IAA4ByD,mCAAYC,IAAG;AACzD,UAAM8C,qBAAqB7E,MAAMG,IAAIe,QAAQoB,IAAI,cAAA;AACjD,UAAMF,kBACJC,mCAAmBrC,MAAMG,IAAIe,QAAQoB,IAAI,gBAAA,CAAA,KAAsB;AAEjE,QACExD,OAAOa,cAAc6B,WACrB1C,OAAOa,cAAcd,OAAOiG,kBAC5BhG,OAAOa,cAAcoF,uBAAuBF,kBAAAA,KAC5CzC,eAAetD,OAAOa,cAAc+B,aACpC;AACA,YAAMsD,gBAAgBhF,MAAMG,IAAI8E,MAAK;AACrC,YAAMC,cAAcC,OAAOC,KAAK,MAAMJ,cAAcK,YAAW,CAAA;AAC/DrF,YAAMO,QAAQhC,mBAAAA,IAAuB2G;IACvC;EACF,CAAA,CAAA,EAEDR,QACCY,sBAAW,CAACrF,UAAUD,UAAAA;AACpB,QAAIlB,OAAOyG,UAAS,GAAI;AACtB,aAAOxF,eAAeC,OAAOC,UAAUkC,MAAAA;IACzC;EACF,CAAA,CAAA,EAEDuC,QACCc,mBAAQ,CAACtF,OAAOF,UAAAA;AACd,QAAIlB,OAAOyG,UAAS,GAAI;AACtBxF,qBAAeC,OAAOmC,QAAWjC,KAAAA;IACnC;EACF,CAAA,CAAA;AAEN,CAAA;AAEO,SAASuF,YACdzF,OACAW,UAAsD;AAEtDX,QAAMO,QAAQmF,mBAAmB/E,YAAYwB;AAC/C;AALgBsD;AAOhB,SAAS7E,YAAYZ,OAAc;AACjC,QAAMW,WAAWX,MAAMO,QAAQmF;AAC/B,MAAI/E,UAAU;AACZ,eAAOgF,oDAA2BhF,QAAAA;EACpC;AACA,SAAO;AACT;AANSC;","names":["REQUEST_TIMESTAMP_SYMBOL","Symbol","REQUEST_BODY_SYMBOL","jsonHeaders","Headers","apitallyPlugin","definePlugin","app","config","client","ApitallyClient","logsContext","AsyncLocalStorage","setStartupData","attempt","appInfo","getAppInfo","appVersion","paths","length","startSync","setTimeout","requestLogger","captureLogs","patchConsole","patchWinston","handleResponse","event","response","error","req","method","toUpperCase","startTime","context","path","matchedRoute","route","consumer","getConsumer","consumerRegistry","addOrUpdateConsumer","Response","status","statusText","headers","mergeHeaders","newResponse","responsePromise","captureResponse","captureBody","enabled","logResponseBody","maxBodySize","then","capturedResponse","responseTime","performance","now","responseSize","completed","size","undefined","requestSize","parseContentLength","get","requestCounter","addRequest","identifier","statusCode","logs","getStore","logRequest","timestamp","Date","url","convertHeaders","Object","fromEntries","entries","body","cause","Error","data","name","zodError","issues","forEach","issue","validationErrorCounter","addValidationError","loc","join","msg","message","type","code","serverErrorCounter","addServerError","traceback","stack","use","onRequest","enterWith","requestContentType","logRequestBody","isSupportedContentType","clonedRequest","clone","requestBody","Buffer","from","arrayBuffer","onResponse","isEnabled","onError","setConsumer","apitallyConsumer","consumerFromStringOrObject"]}
1
+ {"version":3,"sources":["../../src/h3/plugin.ts"],"sourcesContent":["import type { H3Event, HTTPError } from \"h3\";\nimport { definePlugin, onError, onRequest, onResponse } from \"h3\";\nimport { AsyncLocalStorage } from \"node:async_hooks\";\nimport { performance } from \"node:perf_hooks\";\nimport type { ZodError } from \"zod\";\n\nimport { ApitallyClient } from \"../common/client.js\";\nimport { consumerFromStringOrObject } from \"../common/consumerRegistry.js\";\nimport { mergeHeaders, parseContentLength } from \"../common/headers.js\";\nimport type { LogRecord } from \"../common/requestLogger.js\";\nimport { convertHeaders } from \"../common/requestLogger.js\";\nimport { 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 REQUEST_TIMESTAMP_SYMBOL = Symbol(\"apitally.requestTimestamp\");\nconst REQUEST_BODY_SYMBOL = Symbol(\"apitally.requestBody\");\nconst SPAN_HANDLE_SYMBOL = Symbol(\"apitally.spanHandle\");\n\ndeclare module \"h3\" {\n interface H3EventContext {\n apitallyConsumer?: ApitallyConsumer | string;\n\n [REQUEST_TIMESTAMP_SYMBOL]?: number;\n [REQUEST_BODY_SYMBOL]?: Buffer;\n [SPAN_HANDLE_SYMBOL]?: SpanHandle;\n }\n}\n\nconst jsonHeaders = new Headers({\n \"content-type\": \"application/json;charset=UTF-8\",\n});\n\nexport const apitallyPlugin = definePlugin<ApitallyConfig>((app, config) => {\n const client = new ApitallyClient(config);\n const logsContext = new AsyncLocalStorage<LogRecord[]>();\n\n const setStartupData = (attempt: number = 1) => {\n const appInfo = getAppInfo(app, 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 if (client.requestLogger.config.captureLogs) {\n patchConsole(logsContext);\n patchWinston(logsContext);\n }\n\n const handleResponse = async (\n event: H3Event,\n response?: Response,\n error?: HTTPError,\n ) => {\n if (event.req.method.toUpperCase() === \"OPTIONS\") {\n return response;\n }\n\n const startTime = event.context[REQUEST_TIMESTAMP_SYMBOL];\n const path = event.context.matchedRoute?.route;\n\n const consumer = getConsumer(event);\n client.consumerRegistry.addOrUpdateConsumer(consumer);\n\n if (!response) {\n response = new Response(null, {\n status: error?.status || 500,\n statusText: error?.statusText || \"Internal Server Error\",\n headers: error?.headers\n ? mergeHeaders(jsonHeaders, error.headers)\n : jsonHeaders,\n });\n }\n\n const [newResponse, responsePromise] = captureResponse(response, {\n captureBody:\n client.requestLogger.enabled &&\n client.requestLogger.config.logResponseBody,\n maxBodySize: client.requestLogger.maxBodySize,\n });\n\n responsePromise.then(async (capturedResponse) => {\n const responseTime = startTime ? performance.now() - startTime : 0;\n\n const spanHandle = event.context[SPAN_HANDLE_SYMBOL];\n spanHandle?.setName(`${event.req.method} ${path}`);\n const spans = spanHandle?.end();\n const traceId = spanHandle?.traceId;\n\n const responseSize = capturedResponse.completed\n ? capturedResponse.size\n : undefined;\n const requestSize = parseContentLength(\n event.req.headers.get(\"content-length\"),\n );\n\n if (path) {\n client.requestCounter.addRequest({\n consumer: consumer?.identifier,\n method: event.req.method,\n path,\n statusCode: response.status,\n responseTime,\n requestSize,\n responseSize,\n });\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: event.req.method,\n path,\n url: event.req.url,\n headers: convertHeaders(\n Object.fromEntries(event.req.headers.entries()),\n ),\n size: requestSize,\n consumer: consumer?.identifier,\n body: event.context[REQUEST_BODY_SYMBOL],\n },\n {\n statusCode: response.status,\n responseTime: responseTime / 1000,\n headers: convertHeaders(\n Object.fromEntries(response.headers.entries()),\n ),\n size: responseSize,\n body: capturedResponse.body,\n },\n error?.cause instanceof Error ? error.cause : undefined,\n logs,\n spans,\n traceId,\n );\n }\n });\n\n if (\n path &&\n error?.status === 400 &&\n error.data &&\n Array.isArray((error.data as any).issues)\n ) {\n const issues = (error.data as any).issues as ZodError[\"issues\"];\n issues.forEach((issue) => {\n client.validationErrorCounter.addValidationError({\n consumer: consumer?.identifier,\n method: event.req.method,\n path,\n loc: issue.path.join(\".\"),\n msg: issue.message,\n type: issue.code,\n });\n });\n }\n\n if (path && error?.status === 500 && error.cause instanceof Error) {\n client.serverErrorCounter.addServerError({\n consumer: consumer?.identifier,\n method: event.req.method,\n path,\n type: error.cause.name,\n msg: error.cause.message,\n traceback: error.cause.stack || \"\",\n });\n }\n\n return newResponse;\n };\n\n app\n .use(\n onRequest(async (event) => {\n logsContext.enterWith([]);\n event.context[REQUEST_TIMESTAMP_SYMBOL] = performance.now();\n\n const spanHandle = client.spanCollector.startSpan();\n event.context[SPAN_HANDLE_SYMBOL] = spanHandle;\n spanHandle.enterContext();\n\n const requestContentType = event.req.headers.get(\"content-type\");\n const requestSize =\n parseContentLength(event.req.headers.get(\"content-length\")) ?? 0;\n\n if (\n client.requestLogger.enabled &&\n client.requestLogger.config.logRequestBody &&\n client.requestLogger.isSupportedContentType(requestContentType) &&\n requestSize <= client.requestLogger.maxBodySize\n ) {\n const clonedRequest = event.req.clone();\n const requestBody = Buffer.from(await clonedRequest.arrayBuffer());\n event.context[REQUEST_BODY_SYMBOL] = requestBody;\n }\n }),\n )\n .use(\n onResponse((response, event) => {\n if (client.isEnabled()) {\n return handleResponse(event, response, undefined);\n }\n }),\n )\n .use(\n onError((error, event) => {\n if (client.isEnabled()) {\n handleResponse(event, undefined, error);\n }\n }),\n );\n});\n\nexport function setConsumer(\n event: H3Event,\n consumer: ApitallyConsumer | string | null | undefined,\n) {\n event.context.apitallyConsumer = consumer || undefined;\n}\n\nfunction getConsumer(event: H3Event) {\n const consumer = event.context.apitallyConsumer;\n if (consumer) {\n return consumerFromStringOrObject(consumer);\n }\n return null;\n}\n"],"mappings":"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","names":["REQUEST_TIMESTAMP_SYMBOL","Symbol","REQUEST_BODY_SYMBOL","SPAN_HANDLE_SYMBOL","jsonHeaders","Headers","apitallyPlugin","definePlugin","app","config","client","ApitallyClient","logsContext","AsyncLocalStorage","setStartupData","attempt","appInfo","getAppInfo","appVersion","paths","length","startSync","setTimeout","requestLogger","captureLogs","patchConsole","patchWinston","handleResponse","event","response","error","req","method","toUpperCase","startTime","context","path","matchedRoute","route","consumer","getConsumer","consumerRegistry","addOrUpdateConsumer","Response","status","statusText","headers","mergeHeaders","newResponse","responsePromise","captureResponse","captureBody","enabled","logResponseBody","maxBodySize","then","capturedResponse","responseTime","performance","now","spanHandle","setName","spans","end","traceId","responseSize","completed","size","undefined","requestSize","parseContentLength","get","requestCounter","addRequest","identifier","statusCode","logs","getStore","logRequest","timestamp","Date","url","convertHeaders","Object","fromEntries","entries","body","cause","Error","data","Array","isArray","issues","forEach","issue","validationErrorCounter","addValidationError","loc","join","msg","message","type","code","serverErrorCounter","addServerError","name","traceback","stack","use","onRequest","enterWith","spanCollector","startSpan","enterContext","requestContentType","logRequestBody","isSupportedContentType","clonedRequest","clone","requestBody","Buffer","from","arrayBuffer","onResponse","isEnabled","onError","setConsumer","apitallyConsumer","consumerFromStringOrObject"]}
@@ -1,6 +1,8 @@
1
+ import { SpanHandle } from '../common/spanCollector.cjs';
1
2
  import { ApitallyConsumer, ApitallyConfig } from '../common/types.cjs';
2
3
  import * as h3 from 'h3';
3
4
  import { H3Event } from 'h3';
5
+ import '@opentelemetry/sdk-trace-base';
4
6
  import '../common/logging.cjs';
5
7
  import 'winston';
6
8
  import '../common/requestLogger.cjs';
@@ -10,11 +12,13 @@ import '../common/tempGzipFile.cjs';
10
12
 
11
13
  declare const REQUEST_TIMESTAMP_SYMBOL: unique symbol;
12
14
  declare const REQUEST_BODY_SYMBOL: unique symbol;
15
+ declare const SPAN_HANDLE_SYMBOL: unique symbol;
13
16
  declare module "h3" {
14
17
  interface H3EventContext {
15
18
  apitallyConsumer?: ApitallyConsumer | string;
16
19
  [REQUEST_TIMESTAMP_SYMBOL]?: number;
17
20
  [REQUEST_BODY_SYMBOL]?: Buffer;
21
+ [SPAN_HANDLE_SYMBOL]?: SpanHandle;
18
22
  }
19
23
  }
20
24
  declare const apitallyPlugin: (options: ApitallyConfig) => h3.H3Plugin;
@@ -1,6 +1,8 @@
1
+ import { SpanHandle } from '../common/spanCollector.js';
1
2
  import { ApitallyConsumer, ApitallyConfig } from '../common/types.js';
2
3
  import * as h3 from 'h3';
3
4
  import { H3Event } from 'h3';
5
+ import '@opentelemetry/sdk-trace-base';
4
6
  import '../common/logging.js';
5
7
  import 'winston';
6
8
  import '../common/requestLogger.js';
@@ -10,11 +12,13 @@ import '../common/tempGzipFile.js';
10
12
 
11
13
  declare const REQUEST_TIMESTAMP_SYMBOL: unique symbol;
12
14
  declare const REQUEST_BODY_SYMBOL: unique symbol;
15
+ declare const SPAN_HANDLE_SYMBOL: unique symbol;
13
16
  declare module "h3" {
14
17
  interface H3EventContext {
15
18
  apitallyConsumer?: ApitallyConsumer | string;
16
19
  [REQUEST_TIMESTAMP_SYMBOL]?: number;
17
20
  [REQUEST_BODY_SYMBOL]?: Buffer;
21
+ [SPAN_HANDLE_SYMBOL]?: SpanHandle;
18
22
  }
19
23
  }
20
24
  declare const apitallyPlugin: (options: ApitallyConfig) => h3.H3Plugin;
package/dist/h3/plugin.js CHANGED
@@ -12,6 +12,7 @@ import { patchConsole, patchWinston } from "../loggers/index.js";
12
12
  import { getAppInfo } from "./utils.js";
13
13
  const REQUEST_TIMESTAMP_SYMBOL = /* @__PURE__ */ Symbol("apitally.requestTimestamp");
14
14
  const REQUEST_BODY_SYMBOL = /* @__PURE__ */ Symbol("apitally.requestBody");
15
+ const SPAN_HANDLE_SYMBOL = /* @__PURE__ */ Symbol("apitally.spanHandle");
15
16
  const jsonHeaders = new Headers({
16
17
  "content-type": "application/json;charset=UTF-8"
17
18
  });
@@ -33,7 +34,7 @@ const apitallyPlugin = definePlugin((app, config) => {
33
34
  patchWinston(logsContext);
34
35
  }
35
36
  const handleResponse = /* @__PURE__ */ __name(async (event, response, error) => {
36
- var _a, _b;
37
+ var _a;
37
38
  if (event.req.method.toUpperCase() === "OPTIONS") {
38
39
  return response;
39
40
  }
@@ -54,6 +55,10 @@ const apitallyPlugin = definePlugin((app, config) => {
54
55
  });
55
56
  responsePromise.then(async (capturedResponse) => {
56
57
  const responseTime = startTime ? performance.now() - startTime : 0;
58
+ const spanHandle = event.context[SPAN_HANDLE_SYMBOL];
59
+ spanHandle == null ? void 0 : spanHandle.setName(`${event.req.method} ${path}`);
60
+ const spans = spanHandle == null ? void 0 : spanHandle.end();
61
+ const traceId = spanHandle == null ? void 0 : spanHandle.traceId;
57
62
  const responseSize = capturedResponse.completed ? capturedResponse.size : void 0;
58
63
  const requestSize = parseContentLength(event.req.headers.get("content-length"));
59
64
  if (path) {
@@ -84,12 +89,12 @@ const apitallyPlugin = definePlugin((app, config) => {
84
89
  headers: convertHeaders(Object.fromEntries(response.headers.entries())),
85
90
  size: responseSize,
86
91
  body: capturedResponse.body
87
- }, (error == null ? void 0 : error.cause) instanceof Error ? error.cause : void 0, logs);
92
+ }, (error == null ? void 0 : error.cause) instanceof Error ? error.cause : void 0, logs, spans, traceId);
88
93
  }
89
94
  });
90
- if (path && (error == null ? void 0 : error.status) === 400 && error.data && error.data.name === "ZodError") {
91
- const zodError = error.data;
92
- (_b = zodError.issues) == null ? void 0 : _b.forEach((issue) => {
95
+ if (path && (error == null ? void 0 : error.status) === 400 && error.data && Array.isArray(error.data.issues)) {
96
+ const issues = error.data.issues;
97
+ issues.forEach((issue) => {
93
98
  client.validationErrorCounter.addValidationError({
94
99
  consumer: consumer == null ? void 0 : consumer.identifier,
95
100
  method: event.req.method,
@@ -115,6 +120,9 @@ const apitallyPlugin = definePlugin((app, config) => {
115
120
  app.use(onRequest(async (event) => {
116
121
  logsContext.enterWith([]);
117
122
  event.context[REQUEST_TIMESTAMP_SYMBOL] = performance.now();
123
+ const spanHandle = client.spanCollector.startSpan();
124
+ event.context[SPAN_HANDLE_SYMBOL] = spanHandle;
125
+ spanHandle.enterContext();
118
126
  const requestContentType = event.req.headers.get("content-type");
119
127
  const requestSize = parseContentLength(event.req.headers.get("content-length")) ?? 0;
120
128
  if (client.requestLogger.enabled && client.requestLogger.config.logRequestBody && client.requestLogger.isSupportedContentType(requestContentType) && requestSize <= client.requestLogger.maxBodySize) {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/h3/plugin.ts"],"sourcesContent":["import type { H3Event, HTTPError } from \"h3\";\nimport { definePlugin, onError, onRequest, onResponse } from \"h3\";\nimport { AsyncLocalStorage } from \"node:async_hooks\";\nimport { performance } from \"node:perf_hooks\";\nimport type { ZodError } from \"zod\";\n\nimport { ApitallyClient } from \"../common/client.js\";\nimport { consumerFromStringOrObject } from \"../common/consumerRegistry.js\";\nimport { mergeHeaders, parseContentLength } from \"../common/headers.js\";\nimport type { LogRecord } from \"../common/requestLogger.js\";\nimport { convertHeaders } from \"../common/requestLogger.js\";\nimport { 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 REQUEST_TIMESTAMP_SYMBOL = Symbol(\"apitally.requestTimestamp\");\nconst REQUEST_BODY_SYMBOL = Symbol(\"apitally.requestBody\");\n\ndeclare module \"h3\" {\n interface H3EventContext {\n apitallyConsumer?: ApitallyConsumer | string;\n\n [REQUEST_TIMESTAMP_SYMBOL]?: number;\n [REQUEST_BODY_SYMBOL]?: Buffer;\n }\n}\n\nconst jsonHeaders = new Headers({\n \"content-type\": \"application/json;charset=UTF-8\",\n});\n\nexport const apitallyPlugin = definePlugin<ApitallyConfig>((app, config) => {\n const client = new ApitallyClient(config);\n const logsContext = new AsyncLocalStorage<LogRecord[]>();\n\n const setStartupData = (attempt: number = 1) => {\n const appInfo = getAppInfo(app, 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 if (client.requestLogger.config.captureLogs) {\n patchConsole(logsContext);\n patchWinston(logsContext);\n }\n\n const handleResponse = async (\n event: H3Event,\n response?: Response,\n error?: HTTPError,\n ) => {\n if (event.req.method.toUpperCase() === \"OPTIONS\") {\n return response;\n }\n\n const startTime = event.context[REQUEST_TIMESTAMP_SYMBOL];\n const path = event.context.matchedRoute?.route;\n\n const consumer = getConsumer(event);\n client.consumerRegistry.addOrUpdateConsumer(consumer);\n\n if (!response) {\n response = new Response(null, {\n status: error?.status || 500,\n statusText: error?.statusText || \"Internal Server Error\",\n headers: error?.headers\n ? mergeHeaders(jsonHeaders, error.headers)\n : jsonHeaders,\n });\n }\n\n const [newResponse, responsePromise] = captureResponse(response, {\n captureBody:\n client.requestLogger.enabled &&\n client.requestLogger.config.logResponseBody,\n maxBodySize: client.requestLogger.maxBodySize,\n });\n\n responsePromise.then(async (capturedResponse) => {\n const responseTime = startTime ? performance.now() - startTime : 0;\n const responseSize = capturedResponse.completed\n ? capturedResponse.size\n : undefined;\n const requestSize = parseContentLength(\n event.req.headers.get(\"content-length\"),\n );\n\n if (path) {\n client.requestCounter.addRequest({\n consumer: consumer?.identifier,\n method: event.req.method,\n path,\n statusCode: response.status,\n responseTime,\n requestSize,\n responseSize,\n });\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: event.req.method,\n path,\n url: event.req.url,\n headers: convertHeaders(\n Object.fromEntries(event.req.headers.entries()),\n ),\n size: requestSize,\n consumer: consumer?.identifier,\n body: event.context[REQUEST_BODY_SYMBOL],\n },\n {\n statusCode: response.status,\n responseTime: responseTime / 1000,\n headers: convertHeaders(\n Object.fromEntries(response.headers.entries()),\n ),\n size: responseSize,\n body: capturedResponse.body,\n },\n error?.cause instanceof Error ? error.cause : undefined,\n logs,\n );\n }\n });\n\n if (\n path &&\n error?.status === 400 &&\n error.data &&\n (error.data as any).name === \"ZodError\"\n ) {\n const zodError = error.data as ZodError;\n zodError.issues?.forEach((issue) => {\n client.validationErrorCounter.addValidationError({\n consumer: consumer?.identifier,\n method: event.req.method,\n path,\n loc: issue.path.join(\".\"),\n msg: issue.message,\n type: issue.code,\n });\n });\n }\n\n if (path && error?.status === 500 && error.cause instanceof Error) {\n client.serverErrorCounter.addServerError({\n consumer: consumer?.identifier,\n method: event.req.method,\n path,\n type: error.cause.name,\n msg: error.cause.message,\n traceback: error.cause.stack || \"\",\n });\n }\n\n return newResponse;\n };\n\n app\n .use(\n onRequest(async (event) => {\n logsContext.enterWith([]);\n event.context[REQUEST_TIMESTAMP_SYMBOL] = performance.now();\n const requestContentType = event.req.headers.get(\"content-type\");\n const requestSize =\n parseContentLength(event.req.headers.get(\"content-length\")) ?? 0;\n\n if (\n client.requestLogger.enabled &&\n client.requestLogger.config.logRequestBody &&\n client.requestLogger.isSupportedContentType(requestContentType) &&\n requestSize <= client.requestLogger.maxBodySize\n ) {\n const clonedRequest = event.req.clone();\n const requestBody = Buffer.from(await clonedRequest.arrayBuffer());\n event.context[REQUEST_BODY_SYMBOL] = requestBody;\n }\n }),\n )\n .use(\n onResponse((response, event) => {\n if (client.isEnabled()) {\n return handleResponse(event, response, undefined);\n }\n }),\n )\n .use(\n onError((error, event) => {\n if (client.isEnabled()) {\n handleResponse(event, undefined, error);\n }\n }),\n );\n});\n\nexport function setConsumer(\n event: H3Event,\n consumer: ApitallyConsumer | string | null | undefined,\n) {\n event.context.apitallyConsumer = consumer || undefined;\n}\n\nfunction getConsumer(event: H3Event) {\n const consumer = event.context.apitallyConsumer;\n if (consumer) {\n return consumerFromStringOrObject(consumer);\n }\n return null;\n}\n"],"mappings":"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","names":["definePlugin","onError","onRequest","onResponse","AsyncLocalStorage","performance","ApitallyClient","consumerFromStringOrObject","mergeHeaders","parseContentLength","convertHeaders","captureResponse","patchConsole","patchWinston","getAppInfo","REQUEST_TIMESTAMP_SYMBOL","Symbol","REQUEST_BODY_SYMBOL","jsonHeaders","Headers","apitallyPlugin","app","config","client","logsContext","setStartupData","attempt","appInfo","appVersion","paths","length","startSync","setTimeout","requestLogger","captureLogs","handleResponse","event","response","error","req","method","toUpperCase","startTime","context","path","matchedRoute","route","consumer","getConsumer","consumerRegistry","addOrUpdateConsumer","Response","status","statusText","headers","newResponse","responsePromise","captureBody","enabled","logResponseBody","maxBodySize","then","capturedResponse","responseTime","now","responseSize","completed","size","undefined","requestSize","get","requestCounter","addRequest","identifier","statusCode","logs","getStore","logRequest","timestamp","Date","url","Object","fromEntries","entries","body","cause","Error","data","name","zodError","issues","forEach","issue","validationErrorCounter","addValidationError","loc","join","msg","message","type","code","serverErrorCounter","addServerError","traceback","stack","use","enterWith","requestContentType","logRequestBody","isSupportedContentType","clonedRequest","clone","requestBody","Buffer","from","arrayBuffer","isEnabled","setConsumer","apitallyConsumer"]}
1
+ {"version":3,"sources":["../../src/h3/plugin.ts"],"sourcesContent":["import type { H3Event, HTTPError } from \"h3\";\nimport { definePlugin, onError, onRequest, onResponse } from \"h3\";\nimport { AsyncLocalStorage } from \"node:async_hooks\";\nimport { performance } from \"node:perf_hooks\";\nimport type { ZodError } from \"zod\";\n\nimport { ApitallyClient } from \"../common/client.js\";\nimport { consumerFromStringOrObject } from \"../common/consumerRegistry.js\";\nimport { mergeHeaders, parseContentLength } from \"../common/headers.js\";\nimport type { LogRecord } from \"../common/requestLogger.js\";\nimport { convertHeaders } from \"../common/requestLogger.js\";\nimport { 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 REQUEST_TIMESTAMP_SYMBOL = Symbol(\"apitally.requestTimestamp\");\nconst REQUEST_BODY_SYMBOL = Symbol(\"apitally.requestBody\");\nconst SPAN_HANDLE_SYMBOL = Symbol(\"apitally.spanHandle\");\n\ndeclare module \"h3\" {\n interface H3EventContext {\n apitallyConsumer?: ApitallyConsumer | string;\n\n [REQUEST_TIMESTAMP_SYMBOL]?: number;\n [REQUEST_BODY_SYMBOL]?: Buffer;\n [SPAN_HANDLE_SYMBOL]?: SpanHandle;\n }\n}\n\nconst jsonHeaders = new Headers({\n \"content-type\": \"application/json;charset=UTF-8\",\n});\n\nexport const apitallyPlugin = definePlugin<ApitallyConfig>((app, config) => {\n const client = new ApitallyClient(config);\n const logsContext = new AsyncLocalStorage<LogRecord[]>();\n\n const setStartupData = (attempt: number = 1) => {\n const appInfo = getAppInfo(app, 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 if (client.requestLogger.config.captureLogs) {\n patchConsole(logsContext);\n patchWinston(logsContext);\n }\n\n const handleResponse = async (\n event: H3Event,\n response?: Response,\n error?: HTTPError,\n ) => {\n if (event.req.method.toUpperCase() === \"OPTIONS\") {\n return response;\n }\n\n const startTime = event.context[REQUEST_TIMESTAMP_SYMBOL];\n const path = event.context.matchedRoute?.route;\n\n const consumer = getConsumer(event);\n client.consumerRegistry.addOrUpdateConsumer(consumer);\n\n if (!response) {\n response = new Response(null, {\n status: error?.status || 500,\n statusText: error?.statusText || \"Internal Server Error\",\n headers: error?.headers\n ? mergeHeaders(jsonHeaders, error.headers)\n : jsonHeaders,\n });\n }\n\n const [newResponse, responsePromise] = captureResponse(response, {\n captureBody:\n client.requestLogger.enabled &&\n client.requestLogger.config.logResponseBody,\n maxBodySize: client.requestLogger.maxBodySize,\n });\n\n responsePromise.then(async (capturedResponse) => {\n const responseTime = startTime ? performance.now() - startTime : 0;\n\n const spanHandle = event.context[SPAN_HANDLE_SYMBOL];\n spanHandle?.setName(`${event.req.method} ${path}`);\n const spans = spanHandle?.end();\n const traceId = spanHandle?.traceId;\n\n const responseSize = capturedResponse.completed\n ? capturedResponse.size\n : undefined;\n const requestSize = parseContentLength(\n event.req.headers.get(\"content-length\"),\n );\n\n if (path) {\n client.requestCounter.addRequest({\n consumer: consumer?.identifier,\n method: event.req.method,\n path,\n statusCode: response.status,\n responseTime,\n requestSize,\n responseSize,\n });\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: event.req.method,\n path,\n url: event.req.url,\n headers: convertHeaders(\n Object.fromEntries(event.req.headers.entries()),\n ),\n size: requestSize,\n consumer: consumer?.identifier,\n body: event.context[REQUEST_BODY_SYMBOL],\n },\n {\n statusCode: response.status,\n responseTime: responseTime / 1000,\n headers: convertHeaders(\n Object.fromEntries(response.headers.entries()),\n ),\n size: responseSize,\n body: capturedResponse.body,\n },\n error?.cause instanceof Error ? error.cause : undefined,\n logs,\n spans,\n traceId,\n );\n }\n });\n\n if (\n path &&\n error?.status === 400 &&\n error.data &&\n Array.isArray((error.data as any).issues)\n ) {\n const issues = (error.data as any).issues as ZodError[\"issues\"];\n issues.forEach((issue) => {\n client.validationErrorCounter.addValidationError({\n consumer: consumer?.identifier,\n method: event.req.method,\n path,\n loc: issue.path.join(\".\"),\n msg: issue.message,\n type: issue.code,\n });\n });\n }\n\n if (path && error?.status === 500 && error.cause instanceof Error) {\n client.serverErrorCounter.addServerError({\n consumer: consumer?.identifier,\n method: event.req.method,\n path,\n type: error.cause.name,\n msg: error.cause.message,\n traceback: error.cause.stack || \"\",\n });\n }\n\n return newResponse;\n };\n\n app\n .use(\n onRequest(async (event) => {\n logsContext.enterWith([]);\n event.context[REQUEST_TIMESTAMP_SYMBOL] = performance.now();\n\n const spanHandle = client.spanCollector.startSpan();\n event.context[SPAN_HANDLE_SYMBOL] = spanHandle;\n spanHandle.enterContext();\n\n const requestContentType = event.req.headers.get(\"content-type\");\n const requestSize =\n parseContentLength(event.req.headers.get(\"content-length\")) ?? 0;\n\n if (\n client.requestLogger.enabled &&\n client.requestLogger.config.logRequestBody &&\n client.requestLogger.isSupportedContentType(requestContentType) &&\n requestSize <= client.requestLogger.maxBodySize\n ) {\n const clonedRequest = event.req.clone();\n const requestBody = Buffer.from(await clonedRequest.arrayBuffer());\n event.context[REQUEST_BODY_SYMBOL] = requestBody;\n }\n }),\n )\n .use(\n onResponse((response, event) => {\n if (client.isEnabled()) {\n return handleResponse(event, response, undefined);\n }\n }),\n )\n .use(\n onError((error, event) => {\n if (client.isEnabled()) {\n handleResponse(event, undefined, error);\n }\n }),\n );\n});\n\nexport function setConsumer(\n event: H3Event,\n consumer: ApitallyConsumer | string | null | undefined,\n) {\n event.context.apitallyConsumer = consumer || undefined;\n}\n\nfunction getConsumer(event: H3Event) {\n const consumer = event.context.apitallyConsumer;\n if (consumer) {\n return consumerFromStringOrObject(consumer);\n }\n return null;\n}\n"],"mappings":"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","names":["definePlugin","onError","onRequest","onResponse","AsyncLocalStorage","performance","ApitallyClient","consumerFromStringOrObject","mergeHeaders","parseContentLength","convertHeaders","captureResponse","patchConsole","patchWinston","getAppInfo","REQUEST_TIMESTAMP_SYMBOL","Symbol","REQUEST_BODY_SYMBOL","SPAN_HANDLE_SYMBOL","jsonHeaders","Headers","apitallyPlugin","app","config","client","logsContext","setStartupData","attempt","appInfo","appVersion","paths","length","startSync","setTimeout","requestLogger","captureLogs","handleResponse","event","response","error","req","method","toUpperCase","startTime","context","path","matchedRoute","route","consumer","getConsumer","consumerRegistry","addOrUpdateConsumer","Response","status","statusText","headers","newResponse","responsePromise","captureBody","enabled","logResponseBody","maxBodySize","then","capturedResponse","responseTime","now","spanHandle","setName","spans","end","traceId","responseSize","completed","size","undefined","requestSize","get","requestCounter","addRequest","identifier","statusCode","logs","getStore","logRequest","timestamp","Date","url","Object","fromEntries","entries","body","cause","Error","data","Array","isArray","issues","forEach","issue","validationErrorCounter","addValidationError","loc","join","msg","message","type","code","serverErrorCounter","addServerError","name","traceback","stack","use","enterWith","spanCollector","startSpan","enterContext","requestContentType","logRequestBody","isSupportedContentType","clonedRequest","clone","requestBody","Buffer","from","arrayBuffer","isEnabled","setConsumer","apitallyConsumer"]}
package/dist/h3/utils.cjs CHANGED
@@ -22,7 +22,7 @@ __export(utils_exports, {
22
22
  getAppInfo: () => getAppInfo
23
23
  });
24
24
  module.exports = __toCommonJS(utils_exports);
25
- var import_packageVersions = require("../common/packageVersions.js");
25
+ var import_packageVersions = require('../common/packageVersions.cjs');
26
26
  function getAppInfo(h3, appVersion) {
27
27
  const versions = [];
28
28
  if (process.versions.node) {
@@ -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 hapi_exports = {};
30
30
  __export(hapi_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(hapi_exports);
35
- var import_plugin = __toESM(require("./plugin.js"), 1);
36
+ var import_spanCollector = require('../common/spanCollector.cjs');
37
+ var import_plugin = __toESM(require('./plugin.cjs'), 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/hapi/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/hapi/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/hapi/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/hapi/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"]}
@@ -25,17 +25,18 @@ __export(plugin_exports, {
25
25
  module.exports = __toCommonJS(plugin_exports);
26
26
  var import_node_async_hooks = require("node:async_hooks");
27
27
  var import_node_perf_hooks = require("node:perf_hooks");
28
- var import_client = require("../common/client.js");
29
- var import_consumerRegistry = require("../common/consumerRegistry.js");
30
- var import_requestLogger = require("../common/requestLogger.js");
31
- var import_loggers = require("../loggers/index.js");
32
- var import_utils = require("./utils.js");
28
+ var import_client = require('../common/client.cjs');
29
+ var import_consumerRegistry = require('../common/consumerRegistry.cjs');
30
+ var import_requestLogger = require('../common/requestLogger.cjs');
31
+ var import_loggers = require('../loggers/index.cjs');
32
+ var import_utils = require('./utils.cjs');
33
33
  const START_TIME_SYMBOL = /* @__PURE__ */ Symbol("apitally.startTime");
34
34
  const REQUEST_BODY_SYMBOL = /* @__PURE__ */ Symbol("apitally.requestBody");
35
35
  const REQUEST_SIZE_SYMBOL = /* @__PURE__ */ Symbol("apitally.requestSize");
36
36
  const RESPONSE_BODY_SYMBOL = /* @__PURE__ */ Symbol("apitally.responseBody");
37
37
  const RESPONSE_SIZE_SYMBOL = /* @__PURE__ */ Symbol("apitally.responseSize");
38
38
  const LOGS_SYMBOL = /* @__PURE__ */ Symbol("apitally.logs");
39
+ const SPAN_HANDLE_SYMBOL = /* @__PURE__ */ Symbol("apitally.spanHandle");
39
40
  function apitallyPlugin(config) {
40
41
  const client = new import_client.ApitallyClient(config);
41
42
  const logsContext = new import_node_async_hooks.AsyncLocalStorage();
@@ -70,10 +71,12 @@ function apitallyPlugin(config) {
70
71
  return h.continue;
71
72
  }
72
73
  request[START_TIME_SYMBOL] = import_node_perf_hooks.performance.now();
73
- const lifecycle = request._lifecycle.bind(request);
74
+ const spanHandle = client.spanCollector.startSpan();
74
75
  const logs = [];
76
+ request[SPAN_HANDLE_SYMBOL] = spanHandle;
75
77
  request[LOGS_SYMBOL] = logs;
76
- request._lifecycle = () => logsContext.run(logs, lifecycle);
78
+ const lifecycle = request._lifecycle.bind(request);
79
+ request._lifecycle = () => spanHandle.runInContext(() => logsContext.run(logs, lifecycle));
77
80
  const captureRequestBody = client.requestLogger.enabled && client.requestLogger.config.logRequestBody && client.requestLogger.isSupportedContentType(request.headers["content-type"]);
78
81
  const chunks = [];
79
82
  let size = 0;
@@ -120,6 +123,10 @@ function apitallyPlugin(config) {
120
123
  const startTime = request[START_TIME_SYMBOL];
121
124
  const responseTime = startTime ? import_node_perf_hooks.performance.now() - startTime : 0;
122
125
  const timestamp = (Date.now() - responseTime) / 1e3;
126
+ const spanHandle = request[SPAN_HANDLE_SYMBOL];
127
+ spanHandle == null ? void 0 : spanHandle.setName(`${request.method.toUpperCase()} ${request.route.path}`);
128
+ const spans = spanHandle == null ? void 0 : spanHandle.end();
129
+ const traceId = spanHandle == null ? void 0 : spanHandle.traceId;
123
130
  const requestBody = request[REQUEST_BODY_SYMBOL];
124
131
  const requestSize = request[REQUEST_SIZE_SYMBOL];
125
132
  const response = request.response;
@@ -184,7 +191,7 @@ function apitallyPlugin(config) {
184
191
  headers: (0, import_requestLogger.convertHeaders)(responseHeaders),
185
192
  size: responseSize,
186
193
  body: responseBody
187
- }, error, logs);
194
+ }, error, logs, spans, traceId);
188
195
  }
189
196
  return h.continue;
190
197
  });
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/hapi/plugin.ts"],"sourcesContent":["import type { Request, Server } from \"@hapi/hapi\";\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 type { LogRecord } from \"../common/requestLogger.js\";\nimport { convertHeaders } from \"../common/requestLogger.js\";\nimport { ApitallyConfig, ApitallyConsumer } from \"../common/types.js\";\nimport {\n handleHapiRequestEvent,\n patchConsole,\n patchPinoLogger,\n patchWinston,\n} from \"../loggers/index.js\";\nimport { getAppInfo, isBoom } from \"./utils.js\";\n\nconst START_TIME_SYMBOL = Symbol(\"apitally.startTime\");\nconst REQUEST_BODY_SYMBOL = Symbol(\"apitally.requestBody\");\nconst REQUEST_SIZE_SYMBOL = Symbol(\"apitally.requestSize\");\nconst RESPONSE_BODY_SYMBOL = Symbol(\"apitally.responseBody\");\nconst RESPONSE_SIZE_SYMBOL = Symbol(\"apitally.responseSize\");\nconst LOGS_SYMBOL = Symbol(\"apitally.logs\");\n\ndeclare module \"@hapi/hapi\" {\n interface Request {\n [START_TIME_SYMBOL]?: number;\n [REQUEST_BODY_SYMBOL]?: Buffer;\n [REQUEST_SIZE_SYMBOL]?: number;\n [RESPONSE_BODY_SYMBOL]?: Buffer;\n [RESPONSE_SIZE_SYMBOL]?: number;\n [LOGS_SYMBOL]?: LogRecord[];\n apitallyConsumer?: ApitallyConsumer | string;\n }\n\n interface ResponseObject {\n readonly contentType: string | null; // Missing in Hapi types\n }\n}\n\nexport default function apitallyPlugin(config: ApitallyConfig) {\n const client = new ApitallyClient(config);\n const logsContext = new AsyncLocalStorage<LogRecord[]>();\n let pinoLoggerPatched = false;\n\n if (client.requestLogger.enabled && client.requestLogger.config.captureLogs) {\n patchConsole(logsContext);\n patchWinston(logsContext);\n }\n\n return {\n name: \"apitally\",\n register: async function (server: Server) {\n server.ext(\"onPostStart\", async () => {\n if (\n \"logger\" in server &&\n client.requestLogger.enabled &&\n client.requestLogger.config.captureLogs\n ) {\n pinoLoggerPatched = await patchPinoLogger(\n (server as any).logger,\n logsContext,\n );\n }\n });\n\n server.ext(\"onPostStart\", () => {\n const appInfo = getAppInfo(server, config.appVersion);\n client.setStartupData(appInfo);\n client.startSync();\n });\n\n server.ext(\"onPreStop\", async () => {\n await client.handleShutdown();\n });\n\n server.events.on(\"request\", (_, event) => {\n if (\n !pinoLoggerPatched &&\n client.requestLogger.enabled &&\n client.requestLogger.config.captureLogs &&\n event.channel === \"app\"\n ) {\n handleHapiRequestEvent(event, logsContext);\n }\n });\n\n server.ext(\"onRequest\", async (request, h) => {\n if (!client.isEnabled() || request.method.toUpperCase() === \"OPTIONS\") {\n return h.continue;\n }\n\n request[START_TIME_SYMBOL] = performance.now();\n\n // Patch the lifecycle function to run with the logs context\n const lifecycle = (request as any)._lifecycle.bind(request);\n const logs: LogRecord[] = [];\n request[LOGS_SYMBOL] = logs;\n (request as any)._lifecycle = () => logsContext.run(logs, lifecycle);\n\n const captureRequestBody =\n client.requestLogger.enabled &&\n client.requestLogger.config.logRequestBody &&\n client.requestLogger.isSupportedContentType(\n request.headers[\"content-type\"],\n );\n const chunks: Buffer[] = [];\n let size = 0;\n request.events.on(\"peek\", (chunk: string, encoding: string) => {\n if (captureRequestBody) {\n chunks.push(Buffer.from(chunk, encoding as BufferEncoding));\n }\n size += Buffer.byteLength(chunk, encoding as BufferEncoding);\n });\n request.events.once(\"finish\", () => {\n if (chunks.length > 0) {\n request[REQUEST_BODY_SYMBOL] = Buffer.concat(chunks);\n }\n request[REQUEST_SIZE_SYMBOL] = size;\n });\n\n return h.continue;\n });\n\n server.ext(\"onPreResponse\", async (request, h) => {\n if (\n !client.isEnabled() ||\n request.method.toUpperCase() === \"OPTIONS\" ||\n isBoom(request.response)\n ) {\n return h.continue;\n }\n\n const captureResponseBody =\n client.requestLogger.enabled &&\n client.requestLogger.config.logResponseBody &&\n client.requestLogger.isSupportedContentType(\n request.response.contentType,\n );\n const chunks: Buffer[] = [];\n let size = 0;\n request.response.events.on(\n \"peek\",\n (chunk: string, encoding: string) => {\n if (captureResponseBody) {\n chunks.push(Buffer.from(chunk, encoding as BufferEncoding));\n }\n size += Buffer.byteLength(chunk, encoding as BufferEncoding);\n },\n );\n request.response.events.once(\"finish\", () => {\n if (chunks.length > 0) {\n request[RESPONSE_BODY_SYMBOL] = Buffer.concat(chunks);\n }\n request[RESPONSE_SIZE_SYMBOL] = size;\n });\n\n return h.continue;\n });\n\n server.ext(\"onPostResponse\", async (request, h) => {\n if (!client.isEnabled() || request.method.toUpperCase() === \"OPTIONS\") {\n return h.continue;\n }\n\n const startTime = request[START_TIME_SYMBOL];\n const responseTime = startTime ? performance.now() - startTime : 0;\n const timestamp = (Date.now() - responseTime) / 1000;\n const requestBody = request[REQUEST_BODY_SYMBOL];\n const requestSize = request[REQUEST_SIZE_SYMBOL];\n const response = request.response;\n let statusCode: number;\n let responseHeaders: Record<string, any>;\n let responseBody = request[RESPONSE_BODY_SYMBOL];\n let responseSize = request[RESPONSE_SIZE_SYMBOL];\n const error =\n (response as any)._error instanceof Error\n ? (response as any)._error\n : undefined;\n\n if (isBoom(response)) {\n // Handle Boom error object\n statusCode = response.output?.statusCode ?? 500;\n responseHeaders = response.output?.headers ?? {};\n } else {\n // Handle normal ResponseObject\n statusCode = response.statusCode ?? 200;\n responseHeaders = response.headers ?? {};\n }\n\n if (\n !responseBody &&\n (response as any)._payload &&\n client.requestLogger.enabled &&\n client.requestLogger.config.logResponseBody &&\n (isBoom(response) ||\n client.requestLogger.isSupportedContentType(response.contentType))\n ) {\n const responsePayload = (response as any)._payload;\n if (\n responsePayload &&\n responsePayload._data &&\n responsePayload._encoding &&\n typeof responsePayload._data === \"string\" &&\n typeof responsePayload._encoding === \"string\"\n ) {\n responseBody = Buffer.from(\n responsePayload._data,\n responsePayload._encoding as BufferEncoding,\n );\n }\n }\n\n if (!responseSize && responseBody) {\n responseSize = responseBody.length;\n }\n\n const consumer = request.apitallyConsumer\n ? consumerFromStringOrObject(request.apitallyConsumer)\n : null;\n client.consumerRegistry.addOrUpdateConsumer(consumer);\n\n if (request.route.path) {\n client.requestCounter.addRequest({\n consumer: consumer?.identifier,\n method: request.method,\n path: request.route.path,\n statusCode,\n responseTime,\n requestSize,\n responseSize,\n });\n\n if (statusCode === 500 && error) {\n client.serverErrorCounter.addServerError({\n consumer: consumer?.identifier,\n method: request.method,\n path: request.route.path,\n type: error.name,\n msg: error.message,\n traceback: error.stack || \"\",\n });\n }\n }\n\n if (client.requestLogger.enabled) {\n const logs = request[LOGS_SYMBOL];\n client.requestLogger.logRequest(\n {\n timestamp,\n consumer: consumer?.identifier,\n method: request.method,\n path: request.route.path,\n url: request.url?.href || \"\",\n headers: convertHeaders(request.headers),\n size: requestSize,\n body: requestBody,\n },\n {\n statusCode,\n responseTime: responseTime / 1000,\n headers: convertHeaders(responseHeaders),\n size: responseSize,\n body: responseBody,\n },\n error,\n logs,\n );\n }\n\n return h.continue;\n });\n },\n };\n}\n\nexport function setConsumer(\n request: Request,\n consumer: ApitallyConsumer | string | null | undefined,\n) {\n request.apitallyConsumer = consumer || undefined;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AACA;;;;;;AAAA,8BAAkC;AAClC,6BAA4B;AAE5B,oBAA+B;AAC/B,8BAA2C;AAE3C,2BAA+B;AAE/B,qBAKO;AACP,mBAAmC;AAEnC,MAAMA,oBAAoBC,uBAAO,oBAAA;AACjC,MAAMC,sBAAsBD,uBAAO,sBAAA;AACnC,MAAME,sBAAsBF,uBAAO,sBAAA;AACnC,MAAMG,uBAAuBH,uBAAO,uBAAA;AACpC,MAAMI,uBAAuBJ,uBAAO,uBAAA;AACpC,MAAMK,cAAcL,uBAAO,eAAA;AAkBZ,SAAf,eAAuCM,QAAsB;AAC3D,QAAMC,SAAS,IAAIC,6BAAeF,MAAAA;AAClC,QAAMG,cAAc,IAAIC,0CAAAA;AACxB,MAAIC,oBAAoB;AAExB,MAAIJ,OAAOK,cAAcC,WAAWN,OAAOK,cAAcN,OAAOQ,aAAa;AAC3EC,qCAAaN,WAAAA;AACbO,qCAAaP,WAAAA;EACf;AAEA,SAAO;IACLQ,MAAM;IACNC,UAAU,sCAAgBC,QAAc;AACtCA,aAAOC,IAAI,eAAe,YAAA;AACxB,YACE,YAAYD,UACZZ,OAAOK,cAAcC,WACrBN,OAAOK,cAAcN,OAAOQ,aAC5B;AACAH,8BAAoB,UAAMU,gCACvBF,OAAeG,QAChBb,WAAAA;QAEJ;MACF,CAAA;AAEAU,aAAOC,IAAI,eAAe,MAAA;AACxB,cAAMG,cAAUC,yBAAWL,QAAQb,OAAOmB,UAAU;AACpDlB,eAAOmB,eAAeH,OAAAA;AACtBhB,eAAOoB,UAAS;MAClB,CAAA;AAEAR,aAAOC,IAAI,aAAa,YAAA;AACtB,cAAMb,OAAOqB,eAAc;MAC7B,CAAA;AAEAT,aAAOU,OAAOC,GAAG,WAAW,CAACC,GAAGC,UAAAA;AAC9B,YACE,CAACrB,qBACDJ,OAAOK,cAAcC,WACrBN,OAAOK,cAAcN,OAAOQ,eAC5BkB,MAAMC,YAAY,OAClB;AACAC,qDAAuBF,OAAOvB,WAAAA;QAChC;MACF,CAAA;AAEAU,aAAOC,IAAI,aAAa,OAAOe,SAASC,MAAAA;AACtC,YAAI,CAAC7B,OAAO8B,UAAS,KAAMF,QAAQG,OAAOC,YAAW,MAAO,WAAW;AACrE,iBAAOH,EAAEI;QACX;AAEAL,gBAAQpC,iBAAAA,IAAqB0C,mCAAYC,IAAG;AAG5C,cAAMC,YAAaR,QAAgBS,WAAWC,KAAKV,OAAAA;AACnD,cAAMW,OAAoB,CAAA;AAC1BX,gBAAQ9B,WAAAA,IAAeyC;AACtBX,gBAAgBS,aAAa,MAAMnC,YAAYsC,IAAID,MAAMH,SAAAA;AAE1D,cAAMK,qBACJzC,OAAOK,cAAcC,WACrBN,OAAOK,cAAcN,OAAO2C,kBAC5B1C,OAAOK,cAAcsC,uBACnBf,QAAQgB,QAAQ,cAAA,CAAe;AAEnC,cAAMC,SAAmB,CAAA;AACzB,YAAIC,OAAO;AACXlB,gBAAQN,OAAOC,GAAG,QAAQ,CAACwB,OAAeC,aAAAA;AACxC,cAAIP,oBAAoB;AACtBI,mBAAOI,KAAKC,OAAOC,KAAKJ,OAAOC,QAAAA,CAAAA;UACjC;AACAF,kBAAQI,OAAOE,WAAWL,OAAOC,QAAAA;QACnC,CAAA;AACApB,gBAAQN,OAAO+B,KAAK,UAAU,MAAA;AAC5B,cAAIR,OAAOS,SAAS,GAAG;AACrB1B,oBAAQlC,mBAAAA,IAAuBwD,OAAOK,OAAOV,MAAAA;UAC/C;AACAjB,kBAAQjC,mBAAAA,IAAuBmD;QACjC,CAAA;AAEA,eAAOjB,EAAEI;MACX,CAAA;AAEArB,aAAOC,IAAI,iBAAiB,OAAOe,SAASC,MAAAA;AAC1C,YACE,CAAC7B,OAAO8B,UAAS,KACjBF,QAAQG,OAAOC,YAAW,MAAO,iBACjCwB,qBAAO5B,QAAQ6B,QAAQ,GACvB;AACA,iBAAO5B,EAAEI;QACX;AAEA,cAAMyB,sBACJ1D,OAAOK,cAAcC,WACrBN,OAAOK,cAAcN,OAAO4D,mBAC5B3D,OAAOK,cAAcsC,uBACnBf,QAAQ6B,SAASG,WAAW;AAEhC,cAAMf,SAAmB,CAAA;AACzB,YAAIC,OAAO;AACXlB,gBAAQ6B,SAASnC,OAAOC,GACtB,QACA,CAACwB,OAAeC,aAAAA;AACd,cAAIU,qBAAqB;AACvBb,mBAAOI,KAAKC,OAAOC,KAAKJ,OAAOC,QAAAA,CAAAA;UACjC;AACAF,kBAAQI,OAAOE,WAAWL,OAAOC,QAAAA;QACnC,CAAA;AAEFpB,gBAAQ6B,SAASnC,OAAO+B,KAAK,UAAU,MAAA;AACrC,cAAIR,OAAOS,SAAS,GAAG;AACrB1B,oBAAQhC,oBAAAA,IAAwBsD,OAAOK,OAAOV,MAAAA;UAChD;AACAjB,kBAAQ/B,oBAAAA,IAAwBiD;QAClC,CAAA;AAEA,eAAOjB,EAAEI;MACX,CAAA;AAEArB,aAAOC,IAAI,kBAAkB,OAAOe,SAASC,MAAAA;AA/JnD;AAgKQ,YAAI,CAAC7B,OAAO8B,UAAS,KAAMF,QAAQG,OAAOC,YAAW,MAAO,WAAW;AACrE,iBAAOH,EAAEI;QACX;AAEA,cAAM4B,YAAYjC,QAAQpC,iBAAAA;AAC1B,cAAMsE,eAAeD,YAAY3B,mCAAYC,IAAG,IAAK0B,YAAY;AACjE,cAAME,aAAaC,KAAK7B,IAAG,IAAK2B,gBAAgB;AAChD,cAAMG,cAAcrC,QAAQlC,mBAAAA;AAC5B,cAAMwE,cAActC,QAAQjC,mBAAAA;AAC5B,cAAM8D,WAAW7B,QAAQ6B;AACzB,YAAIU;AACJ,YAAIC;AACJ,YAAIC,eAAezC,QAAQhC,oBAAAA;AAC3B,YAAI0E,eAAe1C,QAAQ/B,oBAAAA;AAC3B,cAAM0E,QACHd,SAAiBe,kBAAkBC,QAC/BhB,SAAiBe,SAClBE;AAEN,gBAAIlB,qBAAOC,QAAAA,GAAW;AAEpBU,yBAAaV,cAASkB,WAATlB,mBAAiBU,eAAc;AAC5CC,8BAAkBX,cAASkB,WAATlB,mBAAiBb,YAAW,CAAC;QACjD,OAAO;AAELuB,uBAAaV,SAASU,cAAc;AACpCC,4BAAkBX,SAASb,WAAW,CAAC;QACzC;AAEA,YACE,CAACyB,gBACAZ,SAAiBmB,YAClB5E,OAAOK,cAAcC,WACrBN,OAAOK,cAAcN,OAAO4D,wBAC3BH,qBAAOC,QAAAA,KACNzD,OAAOK,cAAcsC,uBAAuBc,SAASG,WAAW,IAClE;AACA,gBAAMiB,kBAAmBpB,SAAiBmB;AAC1C,cACEC,mBACAA,gBAAgBC,SAChBD,gBAAgBE,aAChB,OAAOF,gBAAgBC,UAAU,YACjC,OAAOD,gBAAgBE,cAAc,UACrC;AACAV,2BAAenB,OAAOC,KACpB0B,gBAAgBC,OAChBD,gBAAgBE,SAAS;UAE7B;QACF;AAEA,YAAI,CAACT,gBAAgBD,cAAc;AACjCC,yBAAeD,aAAaf;QAC9B;AAEA,cAAM0B,WAAWpD,QAAQqD,uBACrBC,oDAA2BtD,QAAQqD,gBAAgB,IACnD;AACJjF,eAAOmF,iBAAiBC,oBAAoBJ,QAAAA;AAE5C,YAAIpD,QAAQyD,MAAMC,MAAM;AACtBtF,iBAAOuF,eAAeC,WAAW;YAC/BR,UAAUA,qCAAUS;YACpB1D,QAAQH,QAAQG;YAChBuD,MAAM1D,QAAQyD,MAAMC;YACpBnB;YACAL;YACAI;YACAI;UACF,CAAA;AAEA,cAAIH,eAAe,OAAOI,OAAO;AAC/BvE,mBAAO0F,mBAAmBC,eAAe;cACvCX,UAAUA,qCAAUS;cACpB1D,QAAQH,QAAQG;cAChBuD,MAAM1D,QAAQyD,MAAMC;cACpBM,MAAMrB,MAAM7D;cACZmF,KAAKtB,MAAMuB;cACXC,WAAWxB,MAAMyB,SAAS;YAC5B,CAAA;UACF;QACF;AAEA,YAAIhG,OAAOK,cAAcC,SAAS;AAChC,gBAAMiC,OAAOX,QAAQ9B,WAAAA;AACrBE,iBAAOK,cAAc4F,WACnB;YACElC;YACAiB,UAAUA,qCAAUS;YACpB1D,QAAQH,QAAQG;YAChBuD,MAAM1D,QAAQyD,MAAMC;YACpBY,OAAKtE,aAAQsE,QAARtE,mBAAauE,SAAQ;YAC1BvD,aAASwD,qCAAexE,QAAQgB,OAAO;YACvCE,MAAMoB;YACNmC,MAAMpC;UACR,GACA;YACEE;YACAL,cAAcA,eAAe;YAC7BlB,aAASwD,qCAAehC,eAAAA;YACxBtB,MAAMwB;YACN+B,MAAMhC;UACR,GACAE,OACAhC,IAAAA;QAEJ;AAEA,eAAOV,EAAEI;MACX,CAAA;IACF,GA5NU;EA6NZ;AACF;AA1OwBqE;AA4OjB,SAASC,YACd3E,SACAoD,UAAsD;AAEtDpD,UAAQqD,mBAAmBD,YAAYN;AACzC;AALgB6B;","names":["START_TIME_SYMBOL","Symbol","REQUEST_BODY_SYMBOL","REQUEST_SIZE_SYMBOL","RESPONSE_BODY_SYMBOL","RESPONSE_SIZE_SYMBOL","LOGS_SYMBOL","config","client","ApitallyClient","logsContext","AsyncLocalStorage","pinoLoggerPatched","requestLogger","enabled","captureLogs","patchConsole","patchWinston","name","register","server","ext","patchPinoLogger","logger","appInfo","getAppInfo","appVersion","setStartupData","startSync","handleShutdown","events","on","_","event","channel","handleHapiRequestEvent","request","h","isEnabled","method","toUpperCase","continue","performance","now","lifecycle","_lifecycle","bind","logs","run","captureRequestBody","logRequestBody","isSupportedContentType","headers","chunks","size","chunk","encoding","push","Buffer","from","byteLength","once","length","concat","isBoom","response","captureResponseBody","logResponseBody","contentType","startTime","responseTime","timestamp","Date","requestBody","requestSize","statusCode","responseHeaders","responseBody","responseSize","error","_error","Error","undefined","output","_payload","responsePayload","_data","_encoding","consumer","apitallyConsumer","consumerFromStringOrObject","consumerRegistry","addOrUpdateConsumer","route","path","requestCounter","addRequest","identifier","serverErrorCounter","addServerError","type","msg","message","traceback","stack","logRequest","url","href","convertHeaders","body","apitallyPlugin","setConsumer"]}
1
+ {"version":3,"sources":["../../src/hapi/plugin.ts"],"sourcesContent":["import type { Request, Server } from \"@hapi/hapi\";\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 type { LogRecord } from \"../common/requestLogger.js\";\nimport { convertHeaders } from \"../common/requestLogger.js\";\nimport type { SpanHandle } from \"../common/spanCollector.js\";\nimport { ApitallyConfig, ApitallyConsumer } from \"../common/types.js\";\nimport {\n handleHapiRequestEvent,\n patchConsole,\n patchPinoLogger,\n patchWinston,\n} from \"../loggers/index.js\";\nimport { getAppInfo, isBoom } from \"./utils.js\";\n\nconst START_TIME_SYMBOL = Symbol(\"apitally.startTime\");\nconst REQUEST_BODY_SYMBOL = Symbol(\"apitally.requestBody\");\nconst REQUEST_SIZE_SYMBOL = Symbol(\"apitally.requestSize\");\nconst RESPONSE_BODY_SYMBOL = Symbol(\"apitally.responseBody\");\nconst RESPONSE_SIZE_SYMBOL = Symbol(\"apitally.responseSize\");\nconst LOGS_SYMBOL = Symbol(\"apitally.logs\");\nconst SPAN_HANDLE_SYMBOL = Symbol(\"apitally.spanHandle\");\n\ndeclare module \"@hapi/hapi\" {\n interface Request {\n [START_TIME_SYMBOL]?: number;\n [REQUEST_BODY_SYMBOL]?: Buffer;\n [REQUEST_SIZE_SYMBOL]?: number;\n [RESPONSE_BODY_SYMBOL]?: Buffer;\n [RESPONSE_SIZE_SYMBOL]?: number;\n [LOGS_SYMBOL]?: LogRecord[];\n [SPAN_HANDLE_SYMBOL]?: SpanHandle;\n apitallyConsumer?: ApitallyConsumer | string;\n }\n\n interface ResponseObject {\n readonly contentType: string | null; // Missing in Hapi types\n }\n}\n\nexport default function apitallyPlugin(config: ApitallyConfig) {\n const client = new ApitallyClient(config);\n const logsContext = new AsyncLocalStorage<LogRecord[]>();\n let pinoLoggerPatched = false;\n\n if (client.requestLogger.enabled && client.requestLogger.config.captureLogs) {\n patchConsole(logsContext);\n patchWinston(logsContext);\n }\n\n return {\n name: \"apitally\",\n register: async function (server: Server) {\n server.ext(\"onPostStart\", async () => {\n if (\n \"logger\" in server &&\n client.requestLogger.enabled &&\n client.requestLogger.config.captureLogs\n ) {\n pinoLoggerPatched = await patchPinoLogger(\n (server as any).logger,\n logsContext,\n );\n }\n });\n\n server.ext(\"onPostStart\", () => {\n const appInfo = getAppInfo(server, config.appVersion);\n client.setStartupData(appInfo);\n client.startSync();\n });\n\n server.ext(\"onPreStop\", async () => {\n await client.handleShutdown();\n });\n\n server.events.on(\"request\", (_, event) => {\n if (\n !pinoLoggerPatched &&\n client.requestLogger.enabled &&\n client.requestLogger.config.captureLogs &&\n event.channel === \"app\"\n ) {\n handleHapiRequestEvent(event, logsContext);\n }\n });\n\n server.ext(\"onRequest\", async (request, h) => {\n if (!client.isEnabled() || request.method.toUpperCase() === \"OPTIONS\") {\n return h.continue;\n }\n\n request[START_TIME_SYMBOL] = performance.now();\n\n // Patch the lifecycle function to run with span and logs context\n const spanHandle = client.spanCollector.startSpan();\n const logs: LogRecord[] = [];\n request[SPAN_HANDLE_SYMBOL] = spanHandle;\n request[LOGS_SYMBOL] = logs;\n const lifecycle = (request as any)._lifecycle.bind(request);\n (request as any)._lifecycle = () =>\n spanHandle.runInContext(() => logsContext.run(logs, lifecycle));\n\n const captureRequestBody =\n client.requestLogger.enabled &&\n client.requestLogger.config.logRequestBody &&\n client.requestLogger.isSupportedContentType(\n request.headers[\"content-type\"],\n );\n const chunks: Buffer[] = [];\n let size = 0;\n request.events.on(\"peek\", (chunk: string, encoding: string) => {\n if (captureRequestBody) {\n chunks.push(Buffer.from(chunk, encoding as BufferEncoding));\n }\n size += Buffer.byteLength(chunk, encoding as BufferEncoding);\n });\n request.events.once(\"finish\", () => {\n if (chunks.length > 0) {\n request[REQUEST_BODY_SYMBOL] = Buffer.concat(chunks);\n }\n request[REQUEST_SIZE_SYMBOL] = size;\n });\n\n return h.continue;\n });\n\n server.ext(\"onPreResponse\", async (request, h) => {\n if (\n !client.isEnabled() ||\n request.method.toUpperCase() === \"OPTIONS\" ||\n isBoom(request.response)\n ) {\n return h.continue;\n }\n\n const captureResponseBody =\n client.requestLogger.enabled &&\n client.requestLogger.config.logResponseBody &&\n client.requestLogger.isSupportedContentType(\n request.response.contentType,\n );\n const chunks: Buffer[] = [];\n let size = 0;\n request.response.events.on(\n \"peek\",\n (chunk: string, encoding: string) => {\n if (captureResponseBody) {\n chunks.push(Buffer.from(chunk, encoding as BufferEncoding));\n }\n size += Buffer.byteLength(chunk, encoding as BufferEncoding);\n },\n );\n request.response.events.once(\"finish\", () => {\n if (chunks.length > 0) {\n request[RESPONSE_BODY_SYMBOL] = Buffer.concat(chunks);\n }\n request[RESPONSE_SIZE_SYMBOL] = size;\n });\n\n return h.continue;\n });\n\n server.ext(\"onPostResponse\", async (request, h) => {\n if (!client.isEnabled() || request.method.toUpperCase() === \"OPTIONS\") {\n return h.continue;\n }\n\n const startTime = request[START_TIME_SYMBOL];\n const responseTime = startTime ? performance.now() - startTime : 0;\n const timestamp = (Date.now() - responseTime) / 1000;\n\n const spanHandle = request[SPAN_HANDLE_SYMBOL];\n spanHandle?.setName(\n `${request.method.toUpperCase()} ${request.route.path}`,\n );\n const spans = spanHandle?.end();\n const traceId = spanHandle?.traceId;\n\n const requestBody = request[REQUEST_BODY_SYMBOL];\n const requestSize = request[REQUEST_SIZE_SYMBOL];\n const response = request.response;\n let statusCode: number;\n let responseHeaders: Record<string, any>;\n let responseBody = request[RESPONSE_BODY_SYMBOL];\n let responseSize = request[RESPONSE_SIZE_SYMBOL];\n const error =\n (response as any)._error instanceof Error\n ? (response as any)._error\n : undefined;\n\n if (isBoom(response)) {\n // Handle Boom error object\n statusCode = response.output?.statusCode ?? 500;\n responseHeaders = response.output?.headers ?? {};\n } else {\n // Handle normal ResponseObject\n statusCode = response.statusCode ?? 200;\n responseHeaders = response.headers ?? {};\n }\n\n if (\n !responseBody &&\n (response as any)._payload &&\n client.requestLogger.enabled &&\n client.requestLogger.config.logResponseBody &&\n (isBoom(response) ||\n client.requestLogger.isSupportedContentType(response.contentType))\n ) {\n const responsePayload = (response as any)._payload;\n if (\n responsePayload &&\n responsePayload._data &&\n responsePayload._encoding &&\n typeof responsePayload._data === \"string\" &&\n typeof responsePayload._encoding === \"string\"\n ) {\n responseBody = Buffer.from(\n responsePayload._data,\n responsePayload._encoding as BufferEncoding,\n );\n }\n }\n\n if (!responseSize && responseBody) {\n responseSize = responseBody.length;\n }\n\n const consumer = request.apitallyConsumer\n ? consumerFromStringOrObject(request.apitallyConsumer)\n : null;\n client.consumerRegistry.addOrUpdateConsumer(consumer);\n\n if (request.route.path) {\n client.requestCounter.addRequest({\n consumer: consumer?.identifier,\n method: request.method,\n path: request.route.path,\n statusCode,\n responseTime,\n requestSize,\n responseSize,\n });\n\n if (statusCode === 500 && error) {\n client.serverErrorCounter.addServerError({\n consumer: consumer?.identifier,\n method: request.method,\n path: request.route.path,\n type: error.name,\n msg: error.message,\n traceback: error.stack || \"\",\n });\n }\n }\n\n if (client.requestLogger.enabled) {\n const logs = request[LOGS_SYMBOL];\n client.requestLogger.logRequest(\n {\n timestamp,\n consumer: consumer?.identifier,\n method: request.method,\n path: request.route.path,\n url: request.url?.href || \"\",\n headers: convertHeaders(request.headers),\n size: requestSize,\n body: requestBody,\n },\n {\n statusCode,\n responseTime: responseTime / 1000,\n headers: convertHeaders(responseHeaders),\n size: responseSize,\n body: responseBody,\n },\n error,\n logs,\n spans,\n traceId,\n );\n }\n\n return h.continue;\n });\n },\n };\n}\n\nexport function setConsumer(\n request: Request,\n consumer: ApitallyConsumer | string | null | undefined,\n) {\n request.apitallyConsumer = consumer || undefined;\n}\n"],"mappings":"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","names":["START_TIME_SYMBOL","Symbol","REQUEST_BODY_SYMBOL","REQUEST_SIZE_SYMBOL","RESPONSE_BODY_SYMBOL","RESPONSE_SIZE_SYMBOL","LOGS_SYMBOL","SPAN_HANDLE_SYMBOL","config","client","ApitallyClient","logsContext","AsyncLocalStorage","pinoLoggerPatched","requestLogger","enabled","captureLogs","patchConsole","patchWinston","name","register","server","ext","patchPinoLogger","logger","appInfo","getAppInfo","appVersion","setStartupData","startSync","handleShutdown","events","on","_","event","channel","handleHapiRequestEvent","request","h","isEnabled","method","toUpperCase","continue","performance","now","spanHandle","spanCollector","startSpan","logs","lifecycle","_lifecycle","bind","runInContext","run","captureRequestBody","logRequestBody","isSupportedContentType","headers","chunks","size","chunk","encoding","push","Buffer","from","byteLength","once","length","concat","isBoom","response","captureResponseBody","logResponseBody","contentType","startTime","responseTime","timestamp","Date","setName","route","path","spans","end","traceId","requestBody","requestSize","statusCode","responseHeaders","responseBody","responseSize","error","_error","Error","undefined","output","_payload","responsePayload","_data","_encoding","consumer","apitallyConsumer","consumerFromStringOrObject","consumerRegistry","addOrUpdateConsumer","requestCounter","addRequest","identifier","serverErrorCounter","addServerError","type","msg","message","traceback","stack","logRequest","url","href","convertHeaders","body","apitallyPlugin","setConsumer"]}
@@ -1,6 +1,8 @@
1
+ import { SpanHandle } from '../common/spanCollector.cjs';
1
2
  import { ApitallyConsumer, ApitallyConfig } from '../common/types.cjs';
2
3
  import { Server, Request } from '@hapi/hapi';
3
4
  import { LogRecord } from '../common/requestLogger.cjs';
5
+ import '@opentelemetry/sdk-trace-base';
4
6
  import '../common/logging.cjs';
5
7
  import 'winston';
6
8
  import 'node:buffer';
@@ -13,6 +15,7 @@ declare const REQUEST_SIZE_SYMBOL: unique symbol;
13
15
  declare const RESPONSE_BODY_SYMBOL: unique symbol;
14
16
  declare const RESPONSE_SIZE_SYMBOL: unique symbol;
15
17
  declare const LOGS_SYMBOL: unique symbol;
18
+ declare const SPAN_HANDLE_SYMBOL: unique symbol;
16
19
  declare module "@hapi/hapi" {
17
20
  interface Request {
18
21
  [START_TIME_SYMBOL]?: number;
@@ -21,6 +24,7 @@ declare module "@hapi/hapi" {
21
24
  [RESPONSE_BODY_SYMBOL]?: Buffer;
22
25
  [RESPONSE_SIZE_SYMBOL]?: number;
23
26
  [LOGS_SYMBOL]?: LogRecord[];
27
+ [SPAN_HANDLE_SYMBOL]?: SpanHandle;
24
28
  apitallyConsumer?: ApitallyConsumer | string;
25
29
  }
26
30
  interface ResponseObject {
@@ -1,6 +1,8 @@
1
+ import { SpanHandle } from '../common/spanCollector.js';
1
2
  import { ApitallyConsumer, ApitallyConfig } from '../common/types.js';
2
3
  import { Server, Request } from '@hapi/hapi';
3
4
  import { LogRecord } from '../common/requestLogger.js';
5
+ import '@opentelemetry/sdk-trace-base';
4
6
  import '../common/logging.js';
5
7
  import 'winston';
6
8
  import 'node:buffer';
@@ -13,6 +15,7 @@ declare const REQUEST_SIZE_SYMBOL: unique symbol;
13
15
  declare const RESPONSE_BODY_SYMBOL: unique symbol;
14
16
  declare const RESPONSE_SIZE_SYMBOL: unique symbol;
15
17
  declare const LOGS_SYMBOL: unique symbol;
18
+ declare const SPAN_HANDLE_SYMBOL: unique symbol;
16
19
  declare module "@hapi/hapi" {
17
20
  interface Request {
18
21
  [START_TIME_SYMBOL]?: number;
@@ -21,6 +24,7 @@ declare module "@hapi/hapi" {
21
24
  [RESPONSE_BODY_SYMBOL]?: Buffer;
22
25
  [RESPONSE_SIZE_SYMBOL]?: number;
23
26
  [LOGS_SYMBOL]?: LogRecord[];
27
+ [SPAN_HANDLE_SYMBOL]?: SpanHandle;
24
28
  apitallyConsumer?: ApitallyConsumer | string;
25
29
  }
26
30
  interface ResponseObject {