apitally 0.23.0 → 0.24.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.
- package/dist/common/client.cjs +2 -1
- package/dist/common/client.cjs.map +1 -1
- package/dist/common/client.js +2 -1
- package/dist/common/client.js.map +1 -1
- package/dist/common/instance.cjs +158 -0
- package/dist/common/instance.cjs.map +1 -0
- package/dist/common/instance.d.cts +4 -0
- package/dist/common/instance.d.ts +4 -0
- package/dist/common/instance.js +134 -0
- package/dist/common/instance.js.map +1 -0
- package/dist/common/requestLogger.cjs +12 -17
- package/dist/common/requestLogger.cjs.map +1 -1
- package/dist/common/requestLogger.js +12 -17
- package/dist/common/requestLogger.js.map +1 -1
- package/dist/common/response.cjs +2 -2
- package/dist/common/response.cjs.map +1 -1
- package/dist/common/response.js +2 -2
- package/dist/common/response.js.map +1 -1
- package/dist/common/serverErrorCounter.cjs +3 -0
- package/dist/common/serverErrorCounter.cjs.map +1 -1
- package/dist/common/serverErrorCounter.js +3 -0
- package/dist/common/serverErrorCounter.js.map +1 -1
- package/dist/common/tempGzipFile.cjs +25 -2
- package/dist/common/tempGzipFile.cjs.map +1 -1
- package/dist/common/tempGzipFile.d.cts +3 -2
- package/dist/common/tempGzipFile.d.ts +3 -2
- package/dist/common/tempGzipFile.js +22 -3
- package/dist/common/tempGzipFile.js.map +1 -1
- package/dist/elysia/plugin.cjs +20 -17
- package/dist/elysia/plugin.cjs.map +1 -1
- package/dist/elysia/plugin.js +20 -17
- package/dist/elysia/plugin.js.map +1 -1
- package/dist/fastify/plugin.cjs +2 -2
- package/dist/fastify/plugin.cjs.map +1 -1
- package/dist/fastify/plugin.js +2 -2
- package/dist/fastify/plugin.js.map +1 -1
- package/dist/h3/plugin.cjs +2 -2
- package/dist/h3/plugin.cjs.map +1 -1
- package/dist/h3/plugin.js +2 -2
- package/dist/h3/plugin.js.map +1 -1
- package/dist/hapi/plugin.cjs +6 -6
- package/dist/hapi/plugin.cjs.map +1 -1
- package/dist/hapi/plugin.js +6 -6
- package/dist/hapi/plugin.js.map +1 -1
- package/dist/loggers/pino.cjs +1 -1
- package/dist/loggers/pino.cjs.map +1 -1
- package/dist/loggers/pino.js +1 -1
- package/dist/loggers/pino.js.map +1 -1
- package/package.json +5 -5
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/fastify/plugin.ts"],"sourcesContent":["import type {\n FastifyError,\n FastifyPluginAsync,\n FastifyReply,\n FastifyRequest,\n} from \"fastify\";\nimport fp from \"fastify-plugin\";\nimport { AsyncLocalStorage, AsyncResource } from \"node:async_hooks\";\n\nimport { ApitallyClient } from \"../common/client.js\";\nimport { consumerFromStringOrObject } from \"../common/consumerRegistry.js\";\nimport { parseContentLength } from \"../common/headers.js\";\nimport { getPackageVersion } from \"../common/packageVersions.js\";\nimport type { LogRecord } from \"../common/requestLogger.js\";\nimport { convertBody, convertHeaders } from \"../common/requestLogger.js\";\nimport {\n ApitallyConfig,\n ApitallyConsumer,\n PathInfo,\n ValidationError,\n} from \"../common/types.js\";\nimport {\n patchConsole,\n patchNestLogger,\n patchPinoLogger,\n patchWinston,\n} from \"../loggers/index.js\";\n\nconst LOGS_SYMBOL = Symbol(\"apitally.logs\");\nconst ASYNC_RESOURCE_SYMBOL = Symbol(\"apitally.logsContextAsyncResource\");\n\ndeclare module \"fastify\" {\n interface FastifyReply {\n payload: any;\n serverError?: FastifyError;\n }\n\n interface FastifyRequest {\n apitallyConsumer?: ApitallyConsumer | string | null;\n consumerIdentifier?: ApitallyConsumer | string | null; // For backwards compatibility\n\n [LOGS_SYMBOL]?: LogRecord[];\n [ASYNC_RESOURCE_SYMBOL]?: AsyncResource;\n }\n}\n\nconst apitallyPlugin: FastifyPluginAsync<ApitallyConfig> = async (\n fastify,\n config,\n) => {\n const client = new ApitallyClient(config);\n const routes: PathInfo[] = [];\n const logsContext = new AsyncLocalStorage<LogRecord[]>();\n\n if (client.requestLogger.config.captureLogs) {\n patchConsole(logsContext);\n patchWinston(logsContext);\n patchPinoLogger(fastify.log, logsContext);\n patchNestLogger(logsContext);\n }\n\n fastify.decorateRequest(\"apitallyConsumer\", null);\n fastify.decorateRequest(\"consumerIdentifier\", null); // For backwards compatibility\n fastify.decorateReply(\"payload\", null);\n\n fastify.addHook(\"onRoute\", (routeOptions) => {\n const methods = Array.isArray(routeOptions.method)\n ? routeOptions.method\n : [routeOptions.method];\n methods.forEach((method) => {\n if (![\"HEAD\", \"OPTIONS\"].includes(method.toUpperCase())) {\n routes.push({\n method: method.toUpperCase(),\n path: routeOptions.url,\n });\n }\n });\n });\n\n fastify.addHook(\"onReady\", () => {\n client.setStartupData(getAppInfo(routes, config.appVersion));\n client.startSync();\n });\n\n fastify.addHook(\"onClose\", async () => {\n await client.handleShutdown();\n });\n\n // Establish logs context for each request\n fastify.addHook(\"onRequest\", (request, reply, done) => {\n if (client.isEnabled()) {\n const logs: LogRecord[] = [];\n request[LOGS_SYMBOL] = logs;\n logsContext.run(logs, () => {\n const asyncResource = new AsyncResource(\"ApitallyLogsContext\");\n request[ASYNC_RESOURCE_SYMBOL] = asyncResource;\n asyncResource.runInAsyncScope(done, request.raw);\n });\n } else {\n done();\n }\n });\n\n // Restore async context for later hooks and route handlers\n fastify.addHook(\"preValidation\", (request, reply, done) => {\n if (client.isEnabled() && request[ASYNC_RESOURCE_SYMBOL]) {\n const asyncResource = request[ASYNC_RESOURCE_SYMBOL];\n asyncResource.runInAsyncScope(done, request.raw);\n } else {\n done();\n }\n });\n\n fastify.addHook(\"onSend\", (request, reply, payload: any, done) => {\n const contentType = reply.getHeader(\"content-type\") as string | undefined;\n if (client.requestLogger.isSupportedContentType(contentType)) {\n reply.payload = payload;\n }\n done();\n });\n\n fastify.addHook(\"onError\", (request, reply, error, done) => {\n if (!error.statusCode || error.statusCode === 500) {\n reply.serverError = error;\n }\n done();\n });\n\n fastify.addHook(\"onResponse\", (request, reply, done) => {\n if (client.isEnabled() && request.method.toUpperCase() !== \"OPTIONS\") {\n // Get path from routeOptions if available (from v4), otherwise fallback to deprecated routerPath\n const consumer = getConsumer(request);\n const path =\n \"routeOptions\" in request\n ? (request as any).routeOptions.url\n : (request as any).routerPath;\n const requestSize = parseContentLength(request.headers[\"content-length\"]);\n const responseSize = parseContentLength(\n reply.getHeader(\"content-length\"),\n );\n const responseTime = getResponseTime(reply);\n client.consumerRegistry.addOrUpdateConsumer(consumer);\n client.requestCounter.addRequest({\n consumer: consumer?.identifier,\n method: request.method,\n path,\n statusCode: reply.statusCode,\n responseTime,\n requestSize,\n responseSize,\n });\n\n if (\n (reply.statusCode === 400 || reply.statusCode === 422) &&\n reply.payload\n ) {\n try {\n const parsedPayload = JSON.parse(reply.payload);\n const validationErrors: ValidationError[] = [];\n\n if (\n (!parsedPayload.code ||\n parsedPayload.code === \"FST_ERR_VALIDATION\") &&\n typeof parsedPayload.message === \"string\"\n ) {\n validationErrors.push(...extractAjvErrors(parsedPayload.message));\n } else if (Array.isArray(parsedPayload.message)) {\n validationErrors.push(\n ...extractNestValidationErrors(parsedPayload.message),\n );\n }\n\n validationErrors.forEach((error) => {\n client.validationErrorCounter.addValidationError({\n consumer: consumer?.identifier,\n method: request.method,\n path: path,\n ...error,\n });\n });\n } catch (error) {} // eslint-disable-line no-empty\n }\n\n if (reply.statusCode === 500 && reply.serverError) {\n client.serverErrorCounter.addServerError({\n consumer: consumer?.identifier,\n method: request.method,\n path: path,\n type: reply.serverError.name,\n msg: reply.serverError.message,\n traceback: reply.serverError.stack || \"\",\n });\n }\n\n if (client.requestLogger.enabled) {\n const logs = request[LOGS_SYMBOL];\n client.requestLogger.logRequest(\n {\n timestamp: Date.now() / 1000,\n method: request.method,\n path,\n url: `${request.protocol}://${request.host ?? request.hostname}${request.originalUrl ?? request.url}`,\n headers: convertHeaders(request.headers),\n size: requestSize,\n consumer: consumer?.identifier,\n body: convertBody(request.body, request.headers[\"content-type\"]),\n },\n {\n statusCode: reply.statusCode,\n responseTime: responseTime / 1000,\n headers: convertHeaders(reply.getHeaders()),\n size: responseSize,\n body: convertBody(\n reply.payload,\n reply.getHeader(\"content-type\")?.toString(),\n ),\n },\n reply.serverError,\n logs,\n );\n }\n }\n\n done();\n });\n};\n\nfunction getAppInfo(routes: PathInfo[], appVersion?: string) {\n const versions = [[\"nodejs\", process.version.replace(/^v/, \"\")]];\n const fastifyVersion = getPackageVersion(\"fastify\");\n const pinoVersion = getPackageVersion(\"pino\");\n const nestjsVersion = getPackageVersion(\"@nestjs/core\");\n const apitallyVersion = getPackageVersion(\"../..\");\n if (fastifyVersion) {\n versions.push([\"fastify\", fastifyVersion]);\n }\n if (pinoVersion) {\n versions.push([\"pino\", pinoVersion]);\n }\n if (nestjsVersion) {\n versions.push([\"nestjs\", nestjsVersion]);\n }\n if (apitallyVersion) {\n versions.push([\"apitally\", apitallyVersion]);\n }\n if (appVersion) {\n versions.push([\"app\", appVersion]);\n }\n return {\n paths: routes,\n versions: Object.fromEntries(versions),\n client: \"js:fastify\",\n };\n}\n\nexport function setConsumer(\n request: FastifyRequest,\n consumer: ApitallyConsumer | string | null | undefined,\n) {\n request.apitallyConsumer = consumer || undefined;\n}\n\nfunction getConsumer(request: FastifyRequest) {\n if (request.apitallyConsumer) {\n return consumerFromStringOrObject(request.apitallyConsumer);\n } else if (request.consumerIdentifier) {\n // For backwards compatibility\n process.emitWarning(\n \"The consumerIdentifier property on the request object is deprecated. Use apitallyConsumer instead.\",\n \"DeprecationWarning\",\n );\n return consumerFromStringOrObject(request.consumerIdentifier);\n }\n return null;\n}\n\nfunction getResponseTime(reply: FastifyReply) {\n if (reply.elapsedTime !== undefined) {\n return reply.elapsedTime;\n } else if ((reply as any).getResponseTime !== undefined) {\n return (reply as any).getResponseTime();\n }\n return 0;\n}\n\nfunction extractAjvErrors(message: string): ValidationError[] {\n try {\n const regex =\n /(?<=^|, )((?:headers|params|query|querystring|body)[/.][^ ]+)(?= )/g;\n const matches: { match: string; index: number }[] = [];\n let match: RegExpExecArray | null;\n while ((match = regex.exec(message)) !== null) {\n matches.push({ match: match[0], index: match.index });\n }\n\n return matches.map((m, i) => {\n const endIndex =\n i + 1 < matches.length ? matches[i + 1].index - 2 : message.length;\n const matchSplit = m.match.split(/[/.]/);\n if (matchSplit[0] === \"querystring\") {\n matchSplit[0] = \"query\";\n }\n return {\n loc: matchSplit.join(\".\"),\n msg: message.substring(m.index, endIndex),\n type: \"\",\n };\n });\n } catch (error) {\n return [];\n }\n}\n\nfunction extractNestValidationErrors(message: any[]): ValidationError[] {\n try {\n return message\n .filter((msg: any) => typeof msg === \"string\")\n .map((msg: any) => ({\n loc: \"\",\n msg,\n type: \"\",\n }));\n } catch (error) {\n return [];\n }\n}\n\nexport { apitallyPlugin };\n\nexport default fp(apitallyPlugin, {\n name: \"apitally\",\n});\n"],"mappings":"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","names":["fp","AsyncLocalStorage","AsyncResource","ApitallyClient","consumerFromStringOrObject","parseContentLength","getPackageVersion","convertBody","convertHeaders","patchConsole","patchNestLogger","patchPinoLogger","patchWinston","LOGS_SYMBOL","Symbol","ASYNC_RESOURCE_SYMBOL","apitallyPlugin","fastify","config","client","routes","logsContext","requestLogger","captureLogs","log","decorateRequest","decorateReply","addHook","routeOptions","methods","Array","isArray","method","forEach","includes","toUpperCase","push","path","url","setStartupData","getAppInfo","appVersion","startSync","handleShutdown","request","reply","done","isEnabled","logs","run","asyncResource","runInAsyncScope","raw","payload","contentType","getHeader","isSupportedContentType","error","statusCode","serverError","consumer","getConsumer","routerPath","requestSize","headers","responseSize","responseTime","getResponseTime","consumerRegistry","addOrUpdateConsumer","requestCounter","addRequest","identifier","parsedPayload","JSON","parse","validationErrors","code","message","extractAjvErrors","extractNestValidationErrors","validationErrorCounter","addValidationError","serverErrorCounter","addServerError","type","name","msg","traceback","stack","enabled","logRequest","timestamp","Date","now","protocol","host","hostname","originalUrl","size","body","getHeaders","toString","versions","process","version","replace","fastifyVersion","pinoVersion","nestjsVersion","apitallyVersion","paths","Object","fromEntries","setConsumer","apitallyConsumer","undefined","consumerIdentifier","emitWarning","elapsedTime","regex","matches","match","exec","index","map","m","i","endIndex","length","matchSplit","split","loc","join","substring","filter"]}
|
|
1
|
+
{"version":3,"sources":["../../src/fastify/plugin.ts"],"sourcesContent":["import type {\n FastifyError,\n FastifyPluginAsync,\n FastifyReply,\n FastifyRequest,\n} from \"fastify\";\nimport fp from \"fastify-plugin\";\nimport { AsyncLocalStorage, AsyncResource } from \"node:async_hooks\";\n\nimport { ApitallyClient } from \"../common/client.js\";\nimport { consumerFromStringOrObject } from \"../common/consumerRegistry.js\";\nimport { parseContentLength } from \"../common/headers.js\";\nimport { getPackageVersion } from \"../common/packageVersions.js\";\nimport type { LogRecord } from \"../common/requestLogger.js\";\nimport { convertBody, convertHeaders } from \"../common/requestLogger.js\";\nimport {\n ApitallyConfig,\n ApitallyConsumer,\n PathInfo,\n ValidationError,\n} from \"../common/types.js\";\nimport {\n patchConsole,\n patchNestLogger,\n patchPinoLogger,\n patchWinston,\n} from \"../loggers/index.js\";\n\nconst LOGS_SYMBOL = Symbol(\"apitally.logs\");\nconst ASYNC_RESOURCE_SYMBOL = Symbol(\"apitally.logsContextAsyncResource\");\n\ndeclare module \"fastify\" {\n interface FastifyReply {\n payload: any;\n serverError?: FastifyError;\n }\n\n interface FastifyRequest {\n apitallyConsumer?: ApitallyConsumer | string | null;\n consumerIdentifier?: ApitallyConsumer | string | null; // For backwards compatibility\n\n [LOGS_SYMBOL]?: LogRecord[];\n [ASYNC_RESOURCE_SYMBOL]?: AsyncResource;\n }\n}\n\nconst apitallyPlugin: FastifyPluginAsync<ApitallyConfig> = async (\n fastify,\n config,\n) => {\n const client = new ApitallyClient(config);\n const routes: PathInfo[] = [];\n const logsContext = new AsyncLocalStorage<LogRecord[]>();\n\n if (client.requestLogger.config.captureLogs) {\n patchConsole(logsContext);\n patchWinston(logsContext);\n patchPinoLogger(fastify.log, logsContext);\n patchNestLogger(logsContext);\n }\n\n fastify.decorateRequest(\"apitallyConsumer\", null);\n fastify.decorateRequest(\"consumerIdentifier\", null); // For backwards compatibility\n fastify.decorateReply(\"payload\", null);\n\n fastify.addHook(\"onRoute\", (routeOptions) => {\n const methods = Array.isArray(routeOptions.method)\n ? routeOptions.method\n : [routeOptions.method];\n methods.forEach((method) => {\n if (![\"HEAD\", \"OPTIONS\"].includes(method.toUpperCase())) {\n routes.push({\n method: method.toUpperCase(),\n path: routeOptions.url,\n });\n }\n });\n });\n\n fastify.addHook(\"onReady\", () => {\n client.setStartupData(getAppInfo(routes, config.appVersion));\n client.startSync();\n });\n\n fastify.addHook(\"onClose\", async () => {\n await client.handleShutdown();\n });\n\n // Establish logs context for each request\n fastify.addHook(\"onRequest\", (request, reply, done) => {\n if (client.isEnabled()) {\n const logs: LogRecord[] = [];\n request[LOGS_SYMBOL] = logs;\n logsContext.run(logs, () => {\n const asyncResource = new AsyncResource(\"ApitallyLogsContext\");\n request[ASYNC_RESOURCE_SYMBOL] = asyncResource;\n asyncResource.runInAsyncScope(done, request.raw);\n });\n } else {\n done();\n }\n });\n\n // Restore async context for later hooks and route handlers\n fastify.addHook(\"preValidation\", (request, reply, done) => {\n if (client.isEnabled() && request[ASYNC_RESOURCE_SYMBOL]) {\n const asyncResource = request[ASYNC_RESOURCE_SYMBOL];\n asyncResource.runInAsyncScope(done, request.raw);\n } else {\n done();\n }\n });\n\n fastify.addHook(\"onSend\", (request, reply, payload: any, done) => {\n const contentType = reply.getHeader(\"content-type\") as string | undefined;\n if (client.requestLogger.isSupportedContentType(contentType)) {\n reply.payload = payload;\n }\n done();\n });\n\n fastify.addHook(\"onError\", (request, reply, error, done) => {\n if (!error.statusCode || error.statusCode === 500) {\n reply.serverError = error;\n }\n done();\n });\n\n fastify.addHook(\"onResponse\", (request, reply, done) => {\n if (client.isEnabled() && request.method.toUpperCase() !== \"OPTIONS\") {\n // Get path from routeOptions if available (from v4), otherwise fallback to deprecated routerPath\n const consumer = getConsumer(request);\n const path =\n \"routeOptions\" in request\n ? (request as any).routeOptions.url\n : (request as any).routerPath;\n const requestSize = parseContentLength(request.headers[\"content-length\"]);\n const responseSize = parseContentLength(\n reply.getHeader(\"content-length\"),\n );\n const responseTime = getResponseTime(reply);\n client.consumerRegistry.addOrUpdateConsumer(consumer);\n client.requestCounter.addRequest({\n consumer: consumer?.identifier,\n method: request.method,\n path,\n statusCode: reply.statusCode,\n responseTime,\n requestSize,\n responseSize,\n });\n\n if (\n (reply.statusCode === 400 || reply.statusCode === 422) &&\n reply.payload\n ) {\n try {\n const parsedPayload = JSON.parse(reply.payload);\n const validationErrors: ValidationError[] = [];\n\n if (\n (!parsedPayload.code ||\n parsedPayload.code === \"FST_ERR_VALIDATION\") &&\n typeof parsedPayload.message === \"string\"\n ) {\n validationErrors.push(...extractAjvErrors(parsedPayload.message));\n } else if (Array.isArray(parsedPayload.message)) {\n validationErrors.push(\n ...extractNestValidationErrors(parsedPayload.message),\n );\n }\n\n validationErrors.forEach((error) => {\n client.validationErrorCounter.addValidationError({\n consumer: consumer?.identifier,\n method: request.method,\n path: path,\n ...error,\n });\n });\n } catch (error) {} // eslint-disable-line no-empty\n }\n\n if (reply.statusCode === 500 && reply.serverError) {\n client.serverErrorCounter.addServerError({\n consumer: consumer?.identifier,\n method: request.method,\n path: path,\n type: reply.serverError.name,\n msg: reply.serverError.message,\n traceback: reply.serverError.stack || \"\",\n });\n }\n\n if (client.requestLogger.enabled) {\n const logs = request[LOGS_SYMBOL];\n client.requestLogger.logRequest(\n {\n timestamp: Date.now() / 1000,\n method: request.method,\n path,\n url: `${request.protocol}://${request.host ?? request.hostname}${request.originalUrl ?? request.url}`,\n headers: convertHeaders(request.headers),\n size: requestSize,\n consumer: consumer?.identifier,\n body: convertBody(request.body, request.headers[\"content-type\"]),\n },\n {\n statusCode: reply.statusCode,\n responseTime: responseTime / 1000,\n headers: convertHeaders(reply.getHeaders()),\n size: responseSize,\n body: convertBody(\n reply.payload,\n reply.getHeader(\"content-type\")?.toString(),\n ),\n },\n reply.serverError,\n logs,\n );\n }\n }\n\n done();\n });\n};\n\nfunction getAppInfo(routes: PathInfo[], appVersion?: string) {\n const versions = [[\"nodejs\", process.version.replace(/^v/, \"\")]];\n const fastifyVersion = getPackageVersion(\"fastify\");\n const pinoVersion = getPackageVersion(\"pino\");\n const nestjsVersion = getPackageVersion(\"@nestjs/core\");\n const apitallyVersion = getPackageVersion(\"../..\");\n if (fastifyVersion) {\n versions.push([\"fastify\", fastifyVersion]);\n }\n if (pinoVersion) {\n versions.push([\"pino\", pinoVersion]);\n }\n if (nestjsVersion) {\n versions.push([\"nestjs\", nestjsVersion]);\n }\n if (apitallyVersion) {\n versions.push([\"apitally\", apitallyVersion]);\n }\n if (appVersion) {\n versions.push([\"app\", appVersion]);\n }\n return {\n paths: routes,\n versions: Object.fromEntries(versions),\n client: \"js:fastify\",\n };\n}\n\nexport function setConsumer(\n request: FastifyRequest,\n consumer: ApitallyConsumer | string | null | undefined,\n) {\n request.apitallyConsumer = consumer || undefined;\n}\n\nfunction getConsumer(request: FastifyRequest) {\n if (request.apitallyConsumer) {\n return consumerFromStringOrObject(request.apitallyConsumer);\n } else if (request.consumerIdentifier) {\n // For backwards compatibility\n process.emitWarning(\n \"The consumerIdentifier property on the request object is deprecated. Use apitallyConsumer instead.\",\n \"DeprecationWarning\",\n );\n return consumerFromStringOrObject(request.consumerIdentifier);\n }\n return null;\n}\n\nfunction getResponseTime(reply: FastifyReply) {\n if (reply.elapsedTime !== undefined) {\n return reply.elapsedTime;\n } else if ((reply as any).getResponseTime !== undefined) {\n return (reply as any).getResponseTime();\n }\n return 0;\n}\n\nfunction extractAjvErrors(message: string): ValidationError[] {\n try {\n const regex =\n /(?<=^|, )((?:headers|params|query|querystring|body)[/.][^ ]+)(?= )/g;\n const matches: { match: string; index: number }[] = [];\n let match: RegExpExecArray | null;\n while ((match = regex.exec(message)) !== null) {\n matches.push({ match: match[0], index: match.index });\n }\n\n return matches.map((m, i) => {\n const endIndex =\n i + 1 < matches.length ? matches[i + 1].index - 2 : message.length;\n const matchSplit = m.match.split(/[/.]/);\n if (matchSplit[0] === \"querystring\") {\n matchSplit[0] = \"query\";\n }\n return {\n loc: matchSplit.join(\".\"),\n msg: message.substring(m.index, endIndex),\n type: \"\",\n };\n });\n } catch (error) {\n return [];\n }\n}\n\nfunction extractNestValidationErrors(message: any[]): ValidationError[] {\n try {\n return message\n .filter((msg: any) => typeof msg === \"string\")\n .map((msg: any) => ({\n loc: \"\",\n msg,\n type: \"\",\n }));\n } catch (error) {\n return [];\n }\n}\n\nexport { apitallyPlugin };\n\nexport default fp(apitallyPlugin, {\n name: \"apitally\",\n});\n"],"mappings":"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","names":["fp","AsyncLocalStorage","AsyncResource","ApitallyClient","consumerFromStringOrObject","parseContentLength","getPackageVersion","convertBody","convertHeaders","patchConsole","patchNestLogger","patchPinoLogger","patchWinston","LOGS_SYMBOL","Symbol","ASYNC_RESOURCE_SYMBOL","apitallyPlugin","fastify","config","client","routes","logsContext","requestLogger","captureLogs","log","decorateRequest","decorateReply","addHook","routeOptions","methods","Array","isArray","method","forEach","includes","toUpperCase","push","path","url","setStartupData","getAppInfo","appVersion","startSync","handleShutdown","request","reply","done","isEnabled","logs","run","asyncResource","runInAsyncScope","raw","payload","contentType","getHeader","isSupportedContentType","error","statusCode","serverError","consumer","getConsumer","routerPath","requestSize","headers","responseSize","responseTime","getResponseTime","consumerRegistry","addOrUpdateConsumer","requestCounter","addRequest","identifier","parsedPayload","JSON","parse","validationErrors","code","message","extractAjvErrors","extractNestValidationErrors","validationErrorCounter","addValidationError","serverErrorCounter","addServerError","type","name","msg","traceback","stack","enabled","logRequest","timestamp","Date","now","protocol","host","hostname","originalUrl","size","body","getHeaders","toString","versions","process","version","replace","fastifyVersion","pinoVersion","nestjsVersion","apitallyVersion","paths","Object","fromEntries","setConsumer","apitallyConsumer","undefined","consumerIdentifier","emitWarning","elapsedTime","regex","matches","match","exec","index","map","m","i","endIndex","length","matchSplit","split","loc","join","substring","filter"]}
|
package/dist/h3/plugin.cjs
CHANGED
|
@@ -33,8 +33,8 @@ var import_requestLogger = require("../common/requestLogger.js");
|
|
|
33
33
|
var import_response = require("../common/response.js");
|
|
34
34
|
var import_loggers = require("../loggers/index.js");
|
|
35
35
|
var import_utils = require("./utils.js");
|
|
36
|
-
const REQUEST_TIMESTAMP_SYMBOL = Symbol("apitally.requestTimestamp");
|
|
37
|
-
const REQUEST_BODY_SYMBOL = Symbol("apitally.requestBody");
|
|
36
|
+
const REQUEST_TIMESTAMP_SYMBOL = /* @__PURE__ */ Symbol("apitally.requestTimestamp");
|
|
37
|
+
const REQUEST_BODY_SYMBOL = /* @__PURE__ */ Symbol("apitally.requestBody");
|
|
38
38
|
const jsonHeaders = new Headers({
|
|
39
39
|
"content-type": "application/json;charset=UTF-8"
|
|
40
40
|
});
|
package/dist/h3/plugin.cjs.map
CHANGED
|
@@ -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":["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 { 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":["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"]}
|
package/dist/h3/plugin.js
CHANGED
|
@@ -10,8 +10,8 @@ import { convertHeaders } from "../common/requestLogger.js";
|
|
|
10
10
|
import { captureResponse } from "../common/response.js";
|
|
11
11
|
import { patchConsole, patchWinston } from "../loggers/index.js";
|
|
12
12
|
import { getAppInfo } from "./utils.js";
|
|
13
|
-
const REQUEST_TIMESTAMP_SYMBOL = Symbol("apitally.requestTimestamp");
|
|
14
|
-
const REQUEST_BODY_SYMBOL = Symbol("apitally.requestBody");
|
|
13
|
+
const REQUEST_TIMESTAMP_SYMBOL = /* @__PURE__ */ Symbol("apitally.requestTimestamp");
|
|
14
|
+
const REQUEST_BODY_SYMBOL = /* @__PURE__ */ Symbol("apitally.requestBody");
|
|
15
15
|
const jsonHeaders = new Headers({
|
|
16
16
|
"content-type": "application/json;charset=UTF-8"
|
|
17
17
|
});
|
package/dist/h3/plugin.js.map
CHANGED
|
@@ -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 { 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"]}
|
package/dist/hapi/plugin.cjs
CHANGED
|
@@ -30,12 +30,12 @@ var import_consumerRegistry = require("../common/consumerRegistry.js");
|
|
|
30
30
|
var import_requestLogger = require("../common/requestLogger.js");
|
|
31
31
|
var import_loggers = require("../loggers/index.js");
|
|
32
32
|
var import_utils = require("./utils.js");
|
|
33
|
-
const START_TIME_SYMBOL = Symbol("apitally.startTime");
|
|
34
|
-
const REQUEST_BODY_SYMBOL = Symbol("apitally.requestBody");
|
|
35
|
-
const REQUEST_SIZE_SYMBOL = Symbol("apitally.requestSize");
|
|
36
|
-
const RESPONSE_BODY_SYMBOL = Symbol("apitally.responseBody");
|
|
37
|
-
const RESPONSE_SIZE_SYMBOL = Symbol("apitally.responseSize");
|
|
38
|
-
const LOGS_SYMBOL = Symbol("apitally.logs");
|
|
33
|
+
const START_TIME_SYMBOL = /* @__PURE__ */ Symbol("apitally.startTime");
|
|
34
|
+
const REQUEST_BODY_SYMBOL = /* @__PURE__ */ Symbol("apitally.requestBody");
|
|
35
|
+
const REQUEST_SIZE_SYMBOL = /* @__PURE__ */ Symbol("apitally.requestSize");
|
|
36
|
+
const RESPONSE_BODY_SYMBOL = /* @__PURE__ */ Symbol("apitally.responseBody");
|
|
37
|
+
const RESPONSE_SIZE_SYMBOL = /* @__PURE__ */ Symbol("apitally.responseSize");
|
|
38
|
+
const LOGS_SYMBOL = /* @__PURE__ */ Symbol("apitally.logs");
|
|
39
39
|
function apitallyPlugin(config) {
|
|
40
40
|
const client = new import_client.ApitallyClient(config);
|
|
41
41
|
const logsContext = new import_node_async_hooks.AsyncLocalStorage();
|
package/dist/hapi/plugin.cjs.map
CHANGED
|
@@ -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":"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","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 { 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":"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","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"]}
|
package/dist/hapi/plugin.js
CHANGED
|
@@ -7,12 +7,12 @@ import { consumerFromStringOrObject } from "../common/consumerRegistry.js";
|
|
|
7
7
|
import { convertHeaders } from "../common/requestLogger.js";
|
|
8
8
|
import { handleHapiRequestEvent, patchConsole, patchPinoLogger, patchWinston } from "../loggers/index.js";
|
|
9
9
|
import { getAppInfo, isBoom } from "./utils.js";
|
|
10
|
-
const START_TIME_SYMBOL = Symbol("apitally.startTime");
|
|
11
|
-
const REQUEST_BODY_SYMBOL = Symbol("apitally.requestBody");
|
|
12
|
-
const REQUEST_SIZE_SYMBOL = Symbol("apitally.requestSize");
|
|
13
|
-
const RESPONSE_BODY_SYMBOL = Symbol("apitally.responseBody");
|
|
14
|
-
const RESPONSE_SIZE_SYMBOL = Symbol("apitally.responseSize");
|
|
15
|
-
const LOGS_SYMBOL = Symbol("apitally.logs");
|
|
10
|
+
const START_TIME_SYMBOL = /* @__PURE__ */ Symbol("apitally.startTime");
|
|
11
|
+
const REQUEST_BODY_SYMBOL = /* @__PURE__ */ Symbol("apitally.requestBody");
|
|
12
|
+
const REQUEST_SIZE_SYMBOL = /* @__PURE__ */ Symbol("apitally.requestSize");
|
|
13
|
+
const RESPONSE_BODY_SYMBOL = /* @__PURE__ */ Symbol("apitally.responseBody");
|
|
14
|
+
const RESPONSE_SIZE_SYMBOL = /* @__PURE__ */ Symbol("apitally.responseSize");
|
|
15
|
+
const LOGS_SYMBOL = /* @__PURE__ */ Symbol("apitally.logs");
|
|
16
16
|
function apitallyPlugin(config) {
|
|
17
17
|
const client = new ApitallyClient(config);
|
|
18
18
|
const logsContext = new AsyncLocalStorage();
|
package/dist/hapi/plugin.js.map
CHANGED
|
@@ -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":"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","names":["AsyncLocalStorage","performance","ApitallyClient","consumerFromStringOrObject","convertHeaders","handleHapiRequestEvent","patchConsole","patchPinoLogger","patchWinston","getAppInfo","isBoom","START_TIME_SYMBOL","Symbol","REQUEST_BODY_SYMBOL","REQUEST_SIZE_SYMBOL","RESPONSE_BODY_SYMBOL","RESPONSE_SIZE_SYMBOL","LOGS_SYMBOL","config","client","logsContext","pinoLoggerPatched","requestLogger","enabled","captureLogs","name","register","server","ext","logger","appInfo","appVersion","setStartupData","startSync","handleShutdown","events","on","_","event","channel","request","h","isEnabled","method","toUpperCase","continue","now","lifecycle","_lifecycle","bind","logs","run","captureRequestBody","logRequestBody","isSupportedContentType","headers","chunks","size","chunk","encoding","push","Buffer","from","byteLength","once","length","concat","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","consumerRegistry","addOrUpdateConsumer","route","path","requestCounter","addRequest","identifier","serverErrorCounter","addServerError","type","msg","message","traceback","stack","logRequest","url","href","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 { 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":"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","names":["AsyncLocalStorage","performance","ApitallyClient","consumerFromStringOrObject","convertHeaders","handleHapiRequestEvent","patchConsole","patchPinoLogger","patchWinston","getAppInfo","isBoom","START_TIME_SYMBOL","Symbol","REQUEST_BODY_SYMBOL","REQUEST_SIZE_SYMBOL","RESPONSE_BODY_SYMBOL","RESPONSE_SIZE_SYMBOL","LOGS_SYMBOL","config","client","logsContext","pinoLoggerPatched","requestLogger","enabled","captureLogs","name","register","server","ext","logger","appInfo","appVersion","setStartupData","startSync","handleShutdown","events","on","_","event","channel","request","h","isEnabled","method","toUpperCase","continue","now","lifecycle","_lifecycle","bind","logs","run","captureRequestBody","logRequestBody","isSupportedContentType","headers","chunks","size","chunk","encoding","push","Buffer","from","byteLength","once","length","concat","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","consumerRegistry","addOrUpdateConsumer","route","path","requestCounter","addRequest","identifier","serverErrorCounter","addServerError","type","msg","message","traceback","stack","logRequest","url","href","body","apitallyPlugin","setConsumer"]}
|
package/dist/loggers/pino.cjs
CHANGED
|
@@ -35,7 +35,7 @@ module.exports = __toCommonJS(pino_exports);
|
|
|
35
35
|
var import_utils = require("./utils.js");
|
|
36
36
|
var _a;
|
|
37
37
|
const MAX_BUFFER_SIZE = 1e3;
|
|
38
|
-
const originalStreamSym = Symbol.for("apitally.originalStream");
|
|
38
|
+
const originalStreamSym = /* @__PURE__ */ Symbol.for("apitally.originalStream");
|
|
39
39
|
const logLevelMap = {
|
|
40
40
|
10: "trace",
|
|
41
41
|
20: "debug",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/loggers/pino.ts"],"sourcesContent":["import { AsyncLocalStorage } from \"node:async_hooks\";\n\nimport type { LogRecord } from \"../common/requestLogger.js\";\nimport { formatMessage, removeKeys } from \"./utils.js\";\n\nconst MAX_BUFFER_SIZE = 1000;\n\nconst originalStreamSym = Symbol.for(\"apitally.originalStream\");\nconst logLevelMap: Record<number, string> = {\n 10: \"trace\",\n 20: \"debug\",\n 30: \"info\",\n 40: \"warn\",\n 50: \"error\",\n 60: \"fatal\",\n};\n\nexport async function patchPinoLogger(\n logger: any,\n logsContext: AsyncLocalStorage<LogRecord[]>,\n) {\n try {\n // Find stream and message key symbols on the logger and its prototype\n const symbols = [\n ...Object.getOwnPropertySymbols(logger),\n ...Object.getOwnPropertySymbols(Object.getPrototypeOf(logger)),\n ];\n const streamSym = symbols.find(\n (sym) => sym.toString() === \"Symbol(pino.stream)\",\n );\n const messageKeySym = symbols.find(\n (sym) => sym.toString() === \"Symbol(pino.messageKey)\",\n );\n if (!streamSym || !messageKeySym) {\n // not a pino logger\n return false;\n }\n\n if (!(originalStreamSym in logger)) {\n logger[originalStreamSym] = logger[streamSym];\n }\n\n const originalStream = logger[originalStreamSym];\n if (originalStream) {\n const pino = await import(/* webpackIgnore: true */ \"pino\");\n const captureStream = new ApitallyLogCaptureStream(\n logsContext,\n logger[messageKeySym],\n );\n logger[streamSym] = pino.default.multistream(\n [\n { level: 0, stream: originalStream },\n { level: 0, stream: captureStream },\n ],\n {\n levels: logger.levels,\n },\n );\n }\n return true;\n } catch {\n // ignore errors\n return false;\n }\n}\n\nfunction filterLogs(obj: any, messageKey: string) {\n return obj[messageKey] !== \"request completed\";\n}\n\nclass ApitallyLogCaptureStream {\n private logsContext: AsyncLocalStorage<LogRecord[]>;\n private messageKey: string;\n\n constructor(logsContext: AsyncLocalStorage<LogRecord[]>, messageKey: string) {\n this.logsContext = logsContext;\n this.messageKey = messageKey;\n }\n\n write(msg: string): void {\n const logs = this.logsContext.getStore();\n if (!logs || !msg || logs.length >= MAX_BUFFER_SIZE) {\n return;\n }\n\n let obj: any;\n try {\n obj = JSON.parse(msg);\n } catch (e) {\n return;\n }\n if (\n obj === null ||\n typeof obj !== \"object\" ||\n !filterLogs(obj, this.messageKey)\n ) {\n return;\n }\n\n try {\n let message = obj[this.messageKey];\n const ignoreKeys = [\n \"hostname\",\n \"level\",\n this.messageKey,\n \"pid\",\n \"time\",\n \"reqId\",\n \"req\",\n \"res\",\n ];\n if (!message && \"data\" in obj && \"tags\" in obj) {\n // hapi-pino uses data and tags instead of the message key\n message = obj.data;\n ignoreKeys.push(\"data\");\n ignoreKeys.push(\"tags\");\n }\n const rest = removeKeys(obj, ignoreKeys);\n const formattedMessage = formatMessage(message, rest);\n if (formattedMessage) {\n logs.push({\n timestamp: this.convertTime(obj.time),\n level: logLevelMap[obj.level] || \"info\",\n message: formattedMessage,\n });\n }\n } catch (e) {\n // ignore\n }\n }\n\n private convertTime(time: any): number {\n if (typeof time === \"number\" && !isNaN(time)) {\n return time / 1000; // Convert milliseconds to seconds\n }\n return Date.now() / 1000; // Fallback to current time\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAGA;;;;;AAAA,mBAA0C;AAA1C;AAEA,MAAMA,kBAAkB;AAExB,MAAMC,oBAAoBC,
|
|
1
|
+
{"version":3,"sources":["../../src/loggers/pino.ts"],"sourcesContent":["import { AsyncLocalStorage } from \"node:async_hooks\";\n\nimport type { LogRecord } from \"../common/requestLogger.js\";\nimport { formatMessage, removeKeys } from \"./utils.js\";\n\nconst MAX_BUFFER_SIZE = 1000;\n\nconst originalStreamSym = Symbol.for(\"apitally.originalStream\");\nconst logLevelMap: Record<number, string> = {\n 10: \"trace\",\n 20: \"debug\",\n 30: \"info\",\n 40: \"warn\",\n 50: \"error\",\n 60: \"fatal\",\n};\n\nexport async function patchPinoLogger(\n logger: any,\n logsContext: AsyncLocalStorage<LogRecord[]>,\n) {\n try {\n // Find stream and message key symbols on the logger and its prototype\n const symbols = [\n ...Object.getOwnPropertySymbols(logger),\n ...Object.getOwnPropertySymbols(Object.getPrototypeOf(logger)),\n ];\n const streamSym = symbols.find(\n (sym) => sym.toString() === \"Symbol(pino.stream)\",\n );\n const messageKeySym = symbols.find(\n (sym) => sym.toString() === \"Symbol(pino.messageKey)\",\n );\n if (!streamSym || !messageKeySym) {\n // not a pino logger\n return false;\n }\n\n if (!(originalStreamSym in logger)) {\n logger[originalStreamSym] = logger[streamSym];\n }\n\n const originalStream = logger[originalStreamSym];\n if (originalStream) {\n const pino = await import(/* webpackIgnore: true */ \"pino\");\n const captureStream = new ApitallyLogCaptureStream(\n logsContext,\n logger[messageKeySym],\n );\n logger[streamSym] = pino.default.multistream(\n [\n { level: 0, stream: originalStream },\n { level: 0, stream: captureStream },\n ],\n {\n levels: logger.levels,\n },\n );\n }\n return true;\n } catch {\n // ignore errors\n return false;\n }\n}\n\nfunction filterLogs(obj: any, messageKey: string) {\n return obj[messageKey] !== \"request completed\";\n}\n\nclass ApitallyLogCaptureStream {\n private logsContext: AsyncLocalStorage<LogRecord[]>;\n private messageKey: string;\n\n constructor(logsContext: AsyncLocalStorage<LogRecord[]>, messageKey: string) {\n this.logsContext = logsContext;\n this.messageKey = messageKey;\n }\n\n write(msg: string): void {\n const logs = this.logsContext.getStore();\n if (!logs || !msg || logs.length >= MAX_BUFFER_SIZE) {\n return;\n }\n\n let obj: any;\n try {\n obj = JSON.parse(msg);\n } catch (e) {\n return;\n }\n if (\n obj === null ||\n typeof obj !== \"object\" ||\n !filterLogs(obj, this.messageKey)\n ) {\n return;\n }\n\n try {\n let message = obj[this.messageKey];\n const ignoreKeys = [\n \"hostname\",\n \"level\",\n this.messageKey,\n \"pid\",\n \"time\",\n \"reqId\",\n \"req\",\n \"res\",\n ];\n if (!message && \"data\" in obj && \"tags\" in obj) {\n // hapi-pino uses data and tags instead of the message key\n message = obj.data;\n ignoreKeys.push(\"data\");\n ignoreKeys.push(\"tags\");\n }\n const rest = removeKeys(obj, ignoreKeys);\n const formattedMessage = formatMessage(message, rest);\n if (formattedMessage) {\n logs.push({\n timestamp: this.convertTime(obj.time),\n level: logLevelMap[obj.level] || \"info\",\n message: formattedMessage,\n });\n }\n } catch (e) {\n // ignore\n }\n }\n\n private convertTime(time: any): number {\n if (typeof time === \"number\" && !isNaN(time)) {\n return time / 1000; // Convert milliseconds to seconds\n }\n return Date.now() / 1000; // Fallback to current time\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAGA;;;;;AAAA,mBAA0C;AAA1C;AAEA,MAAMA,kBAAkB;AAExB,MAAMC,oBAAoBC,uBAAOC,IAAI,yBAAA;AACrC,MAAMC,cAAsC;EAC1C,IAAI;EACJ,IAAI;EACJ,IAAI;EACJ,IAAI;EACJ,IAAI;EACJ,IAAI;AACN;AAEA,eAAsBC,gBACpBC,QACAC,aAA2C;AAE3C,MAAI;AAEF,UAAMC,UAAU;SACXC,OAAOC,sBAAsBJ,MAAAA;SAC7BG,OAAOC,sBAAsBD,OAAOE,eAAeL,MAAAA,CAAAA;;AAExD,UAAMM,YAAYJ,QAAQK,KACxB,CAACC,QAAQA,IAAIC,SAAQ,MAAO,qBAAA;AAE9B,UAAMC,gBAAgBR,QAAQK,KAC5B,CAACC,QAAQA,IAAIC,SAAQ,MAAO,yBAAA;AAE9B,QAAI,CAACH,aAAa,CAACI,eAAe;AAEhC,aAAO;IACT;AAEA,QAAI,EAAEf,qBAAqBK,SAAS;AAClCA,aAAOL,iBAAAA,IAAqBK,OAAOM,SAAAA;IACrC;AAEA,UAAMK,iBAAiBX,OAAOL,iBAAAA;AAC9B,QAAIgB,gBAAgB;AAClB,YAAMC,OAAO,MAAM;;QAAiC;MAAA;AACpD,YAAMC,gBAAgB,IAAIC,yBACxBb,aACAD,OAAOU,aAAAA,CAAc;AAEvBV,aAAOM,SAAAA,IAAaM,KAAKG,QAAQC,YAC/B;QACE;UAAEC,OAAO;UAAGC,QAAQP;QAAe;QACnC;UAAEM,OAAO;UAAGC,QAAQL;QAAc;SAEpC;QACEM,QAAQnB,OAAOmB;MACjB,CAAA;IAEJ;AACA,WAAO;EACT,QAAQ;AAEN,WAAO;EACT;AACF;AA/CsBpB;AAiDtB,SAASqB,WAAWC,KAAUC,YAAkB;AAC9C,SAAOD,IAAIC,UAAAA,MAAgB;AAC7B;AAFSF;AAIT,IAAMN,4BAAN,WAAMA;EACIb;EACAqB;EAER,YAAYrB,aAA6CqB,YAAoB;AAC3E,SAAKrB,cAAcA;AACnB,SAAKqB,aAAaA;EACpB;EAEAC,MAAMC,KAAmB;AACvB,UAAMC,OAAO,KAAKxB,YAAYyB,SAAQ;AACtC,QAAI,CAACD,QAAQ,CAACD,OAAOC,KAAKE,UAAUjC,iBAAiB;AACnD;IACF;AAEA,QAAI2B;AACJ,QAAI;AACFA,YAAMO,KAAKC,MAAML,GAAAA;IACnB,SAASM,GAAG;AACV;IACF;AACA,QACET,QAAQ,QACR,OAAOA,QAAQ,YACf,CAACD,WAAWC,KAAK,KAAKC,UAAU,GAChC;AACA;IACF;AAEA,QAAI;AACF,UAAIS,UAAUV,IAAI,KAAKC,UAAU;AACjC,YAAMU,aAAa;QACjB;QACA;QACA,KAAKV;QACL;QACA;QACA;QACA;QACA;;AAEF,UAAI,CAACS,WAAW,UAAUV,OAAO,UAAUA,KAAK;AAE9CU,kBAAUV,IAAIY;AACdD,mBAAWE,KAAK,MAAA;AAChBF,mBAAWE,KAAK,MAAA;MAClB;AACA,YAAMC,WAAOC,yBAAWf,KAAKW,UAAAA;AAC7B,YAAMK,uBAAmBC,4BAAcP,SAASI,IAAAA;AAChD,UAAIE,kBAAkB;AACpBZ,aAAKS,KAAK;UACRK,WAAW,KAAKC,YAAYnB,IAAIoB,IAAI;UACpCxB,OAAOnB,YAAYuB,IAAIJ,KAAK,KAAK;UACjCc,SAASM;QACX,CAAA;MACF;IACF,SAASP,GAAG;IAEZ;EACF;EAEQU,YAAYC,MAAmB;AACrC,QAAI,OAAOA,SAAS,YAAY,CAACC,MAAMD,IAAAA,GAAO;AAC5C,aAAOA,OAAO;IAChB;AACA,WAAOE,KAAKC,IAAG,IAAK;EACtB;AACF,GAnEM9B,wCAAN;","names":["MAX_BUFFER_SIZE","originalStreamSym","Symbol","for","logLevelMap","patchPinoLogger","logger","logsContext","symbols","Object","getOwnPropertySymbols","getPrototypeOf","streamSym","find","sym","toString","messageKeySym","originalStream","pino","captureStream","ApitallyLogCaptureStream","default","multistream","level","stream","levels","filterLogs","obj","messageKey","write","msg","logs","getStore","length","JSON","parse","e","message","ignoreKeys","data","push","rest","removeKeys","formattedMessage","formatMessage","timestamp","convertTime","time","isNaN","Date","now"]}
|
package/dist/loggers/pino.js
CHANGED
|
@@ -3,7 +3,7 @@ var __name = (target, value) => __defProp(target, "name", { value, configurable:
|
|
|
3
3
|
var _a;
|
|
4
4
|
import { formatMessage, removeKeys } from "./utils.js";
|
|
5
5
|
const MAX_BUFFER_SIZE = 1e3;
|
|
6
|
-
const originalStreamSym = Symbol.for("apitally.originalStream");
|
|
6
|
+
const originalStreamSym = /* @__PURE__ */ Symbol.for("apitally.originalStream");
|
|
7
7
|
const logLevelMap = {
|
|
8
8
|
10: "trace",
|
|
9
9
|
20: "debug",
|
package/dist/loggers/pino.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/loggers/pino.ts"],"sourcesContent":["import { AsyncLocalStorage } from \"node:async_hooks\";\n\nimport type { LogRecord } from \"../common/requestLogger.js\";\nimport { formatMessage, removeKeys } from \"./utils.js\";\n\nconst MAX_BUFFER_SIZE = 1000;\n\nconst originalStreamSym = Symbol.for(\"apitally.originalStream\");\nconst logLevelMap: Record<number, string> = {\n 10: \"trace\",\n 20: \"debug\",\n 30: \"info\",\n 40: \"warn\",\n 50: \"error\",\n 60: \"fatal\",\n};\n\nexport async function patchPinoLogger(\n logger: any,\n logsContext: AsyncLocalStorage<LogRecord[]>,\n) {\n try {\n // Find stream and message key symbols on the logger and its prototype\n const symbols = [\n ...Object.getOwnPropertySymbols(logger),\n ...Object.getOwnPropertySymbols(Object.getPrototypeOf(logger)),\n ];\n const streamSym = symbols.find(\n (sym) => sym.toString() === \"Symbol(pino.stream)\",\n );\n const messageKeySym = symbols.find(\n (sym) => sym.toString() === \"Symbol(pino.messageKey)\",\n );\n if (!streamSym || !messageKeySym) {\n // not a pino logger\n return false;\n }\n\n if (!(originalStreamSym in logger)) {\n logger[originalStreamSym] = logger[streamSym];\n }\n\n const originalStream = logger[originalStreamSym];\n if (originalStream) {\n const pino = await import(/* webpackIgnore: true */ \"pino\");\n const captureStream = new ApitallyLogCaptureStream(\n logsContext,\n logger[messageKeySym],\n );\n logger[streamSym] = pino.default.multistream(\n [\n { level: 0, stream: originalStream },\n { level: 0, stream: captureStream },\n ],\n {\n levels: logger.levels,\n },\n );\n }\n return true;\n } catch {\n // ignore errors\n return false;\n }\n}\n\nfunction filterLogs(obj: any, messageKey: string) {\n return obj[messageKey] !== \"request completed\";\n}\n\nclass ApitallyLogCaptureStream {\n private logsContext: AsyncLocalStorage<LogRecord[]>;\n private messageKey: string;\n\n constructor(logsContext: AsyncLocalStorage<LogRecord[]>, messageKey: string) {\n this.logsContext = logsContext;\n this.messageKey = messageKey;\n }\n\n write(msg: string): void {\n const logs = this.logsContext.getStore();\n if (!logs || !msg || logs.length >= MAX_BUFFER_SIZE) {\n return;\n }\n\n let obj: any;\n try {\n obj = JSON.parse(msg);\n } catch (e) {\n return;\n }\n if (\n obj === null ||\n typeof obj !== \"object\" ||\n !filterLogs(obj, this.messageKey)\n ) {\n return;\n }\n\n try {\n let message = obj[this.messageKey];\n const ignoreKeys = [\n \"hostname\",\n \"level\",\n this.messageKey,\n \"pid\",\n \"time\",\n \"reqId\",\n \"req\",\n \"res\",\n ];\n if (!message && \"data\" in obj && \"tags\" in obj) {\n // hapi-pino uses data and tags instead of the message key\n message = obj.data;\n ignoreKeys.push(\"data\");\n ignoreKeys.push(\"tags\");\n }\n const rest = removeKeys(obj, ignoreKeys);\n const formattedMessage = formatMessage(message, rest);\n if (formattedMessage) {\n logs.push({\n timestamp: this.convertTime(obj.time),\n level: logLevelMap[obj.level] || \"info\",\n message: formattedMessage,\n });\n }\n } catch (e) {\n // ignore\n }\n }\n\n private convertTime(time: any): number {\n if (typeof time === \"number\" && !isNaN(time)) {\n return time / 1000; // Convert milliseconds to seconds\n }\n return Date.now() / 1000; // Fallback to current time\n }\n}\n"],"mappings":";;AAGA;SAASA,eAAeC,kBAAkB;AAE1C,MAAMC,kBAAkB;AAExB,MAAMC,oBAAoBC,
|
|
1
|
+
{"version":3,"sources":["../../src/loggers/pino.ts"],"sourcesContent":["import { AsyncLocalStorage } from \"node:async_hooks\";\n\nimport type { LogRecord } from \"../common/requestLogger.js\";\nimport { formatMessage, removeKeys } from \"./utils.js\";\n\nconst MAX_BUFFER_SIZE = 1000;\n\nconst originalStreamSym = Symbol.for(\"apitally.originalStream\");\nconst logLevelMap: Record<number, string> = {\n 10: \"trace\",\n 20: \"debug\",\n 30: \"info\",\n 40: \"warn\",\n 50: \"error\",\n 60: \"fatal\",\n};\n\nexport async function patchPinoLogger(\n logger: any,\n logsContext: AsyncLocalStorage<LogRecord[]>,\n) {\n try {\n // Find stream and message key symbols on the logger and its prototype\n const symbols = [\n ...Object.getOwnPropertySymbols(logger),\n ...Object.getOwnPropertySymbols(Object.getPrototypeOf(logger)),\n ];\n const streamSym = symbols.find(\n (sym) => sym.toString() === \"Symbol(pino.stream)\",\n );\n const messageKeySym = symbols.find(\n (sym) => sym.toString() === \"Symbol(pino.messageKey)\",\n );\n if (!streamSym || !messageKeySym) {\n // not a pino logger\n return false;\n }\n\n if (!(originalStreamSym in logger)) {\n logger[originalStreamSym] = logger[streamSym];\n }\n\n const originalStream = logger[originalStreamSym];\n if (originalStream) {\n const pino = await import(/* webpackIgnore: true */ \"pino\");\n const captureStream = new ApitallyLogCaptureStream(\n logsContext,\n logger[messageKeySym],\n );\n logger[streamSym] = pino.default.multistream(\n [\n { level: 0, stream: originalStream },\n { level: 0, stream: captureStream },\n ],\n {\n levels: logger.levels,\n },\n );\n }\n return true;\n } catch {\n // ignore errors\n return false;\n }\n}\n\nfunction filterLogs(obj: any, messageKey: string) {\n return obj[messageKey] !== \"request completed\";\n}\n\nclass ApitallyLogCaptureStream {\n private logsContext: AsyncLocalStorage<LogRecord[]>;\n private messageKey: string;\n\n constructor(logsContext: AsyncLocalStorage<LogRecord[]>, messageKey: string) {\n this.logsContext = logsContext;\n this.messageKey = messageKey;\n }\n\n write(msg: string): void {\n const logs = this.logsContext.getStore();\n if (!logs || !msg || logs.length >= MAX_BUFFER_SIZE) {\n return;\n }\n\n let obj: any;\n try {\n obj = JSON.parse(msg);\n } catch (e) {\n return;\n }\n if (\n obj === null ||\n typeof obj !== \"object\" ||\n !filterLogs(obj, this.messageKey)\n ) {\n return;\n }\n\n try {\n let message = obj[this.messageKey];\n const ignoreKeys = [\n \"hostname\",\n \"level\",\n this.messageKey,\n \"pid\",\n \"time\",\n \"reqId\",\n \"req\",\n \"res\",\n ];\n if (!message && \"data\" in obj && \"tags\" in obj) {\n // hapi-pino uses data and tags instead of the message key\n message = obj.data;\n ignoreKeys.push(\"data\");\n ignoreKeys.push(\"tags\");\n }\n const rest = removeKeys(obj, ignoreKeys);\n const formattedMessage = formatMessage(message, rest);\n if (formattedMessage) {\n logs.push({\n timestamp: this.convertTime(obj.time),\n level: logLevelMap[obj.level] || \"info\",\n message: formattedMessage,\n });\n }\n } catch (e) {\n // ignore\n }\n }\n\n private convertTime(time: any): number {\n if (typeof time === \"number\" && !isNaN(time)) {\n return time / 1000; // Convert milliseconds to seconds\n }\n return Date.now() / 1000; // Fallback to current time\n }\n}\n"],"mappings":";;AAGA;SAASA,eAAeC,kBAAkB;AAE1C,MAAMC,kBAAkB;AAExB,MAAMC,oBAAoBC,uBAAOC,IAAI,yBAAA;AACrC,MAAMC,cAAsC;EAC1C,IAAI;EACJ,IAAI;EACJ,IAAI;EACJ,IAAI;EACJ,IAAI;EACJ,IAAI;AACN;AAEA,eAAsBC,gBACpBC,QACAC,aAA2C;AAE3C,MAAI;AAEF,UAAMC,UAAU;SACXC,OAAOC,sBAAsBJ,MAAAA;SAC7BG,OAAOC,sBAAsBD,OAAOE,eAAeL,MAAAA,CAAAA;;AAExD,UAAMM,YAAYJ,QAAQK,KACxB,CAACC,QAAQA,IAAIC,SAAQ,MAAO,qBAAA;AAE9B,UAAMC,gBAAgBR,QAAQK,KAC5B,CAACC,QAAQA,IAAIC,SAAQ,MAAO,yBAAA;AAE9B,QAAI,CAACH,aAAa,CAACI,eAAe;AAEhC,aAAO;IACT;AAEA,QAAI,EAAEf,qBAAqBK,SAAS;AAClCA,aAAOL,iBAAAA,IAAqBK,OAAOM,SAAAA;IACrC;AAEA,UAAMK,iBAAiBX,OAAOL,iBAAAA;AAC9B,QAAIgB,gBAAgB;AAClB,YAAMC,OAAO,MAAM;;QAAiC;MAAA;AACpD,YAAMC,gBAAgB,IAAIC,yBACxBb,aACAD,OAAOU,aAAAA,CAAc;AAEvBV,aAAOM,SAAAA,IAAaM,KAAKG,QAAQC,YAC/B;QACE;UAAEC,OAAO;UAAGC,QAAQP;QAAe;QACnC;UAAEM,OAAO;UAAGC,QAAQL;QAAc;SAEpC;QACEM,QAAQnB,OAAOmB;MACjB,CAAA;IAEJ;AACA,WAAO;EACT,QAAQ;AAEN,WAAO;EACT;AACF;AA/CsBpB;AAiDtB,SAASqB,WAAWC,KAAUC,YAAkB;AAC9C,SAAOD,IAAIC,UAAAA,MAAgB;AAC7B;AAFSF;AAIT,IAAMN,4BAAN,WAAMA;EACIb;EACAqB;EAER,YAAYrB,aAA6CqB,YAAoB;AAC3E,SAAKrB,cAAcA;AACnB,SAAKqB,aAAaA;EACpB;EAEAC,MAAMC,KAAmB;AACvB,UAAMC,OAAO,KAAKxB,YAAYyB,SAAQ;AACtC,QAAI,CAACD,QAAQ,CAACD,OAAOC,KAAKE,UAAUjC,iBAAiB;AACnD;IACF;AAEA,QAAI2B;AACJ,QAAI;AACFA,YAAMO,KAAKC,MAAML,GAAAA;IACnB,SAASM,GAAG;AACV;IACF;AACA,QACET,QAAQ,QACR,OAAOA,QAAQ,YACf,CAACD,WAAWC,KAAK,KAAKC,UAAU,GAChC;AACA;IACF;AAEA,QAAI;AACF,UAAIS,UAAUV,IAAI,KAAKC,UAAU;AACjC,YAAMU,aAAa;QACjB;QACA;QACA,KAAKV;QACL;QACA;QACA;QACA;QACA;;AAEF,UAAI,CAACS,WAAW,UAAUV,OAAO,UAAUA,KAAK;AAE9CU,kBAAUV,IAAIY;AACdD,mBAAWE,KAAK,MAAA;AAChBF,mBAAWE,KAAK,MAAA;MAClB;AACA,YAAMC,OAAO1C,WAAW4B,KAAKW,UAAAA;AAC7B,YAAMI,mBAAmB5C,cAAcuC,SAASI,IAAAA;AAChD,UAAIC,kBAAkB;AACpBX,aAAKS,KAAK;UACRG,WAAW,KAAKC,YAAYjB,IAAIkB,IAAI;UACpCtB,OAAOnB,YAAYuB,IAAIJ,KAAK,KAAK;UACjCc,SAASK;QACX,CAAA;MACF;IACF,SAASN,GAAG;IAEZ;EACF;EAEQQ,YAAYC,MAAmB;AACrC,QAAI,OAAOA,SAAS,YAAY,CAACC,MAAMD,IAAAA,GAAO;AAC5C,aAAOA,OAAO;IAChB;AACA,WAAOE,KAAKC,IAAG,IAAK;EACtB;AACF,GAnEM5B,wCAAN;","names":["formatMessage","removeKeys","MAX_BUFFER_SIZE","originalStreamSym","Symbol","for","logLevelMap","patchPinoLogger","logger","logsContext","symbols","Object","getOwnPropertySymbols","getPrototypeOf","streamSym","find","sym","toString","messageKeySym","originalStream","pino","captureStream","ApitallyLogCaptureStream","default","multistream","level","stream","levels","filterLogs","obj","messageKey","write","msg","logs","getStore","length","JSON","parse","e","message","ignoreKeys","data","push","rest","formattedMessage","timestamp","convertTime","time","isNaN","Date","now"]}
|