apitally 0.21.4 → 0.22.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/requestLogger.cjs +23 -12
- package/dist/common/requestLogger.cjs.map +1 -1
- package/dist/common/requestLogger.d.cts +1 -1
- package/dist/common/requestLogger.d.ts +1 -1
- package/dist/common/requestLogger.js +23 -12
- package/dist/common/requestLogger.js.map +1 -1
- package/dist/common/response.cjs +63 -88
- package/dist/common/response.cjs.map +1 -1
- package/dist/common/response.d.cts +14 -6
- package/dist/common/response.d.ts +14 -6
- package/dist/common/response.js +61 -83
- package/dist/common/response.js.map +1 -1
- package/dist/elysia/index.d.cts +6 -0
- package/dist/elysia/index.d.ts +6 -0
- package/dist/elysia/plugin.cjs +104 -76
- package/dist/elysia/plugin.cjs.map +1 -1
- package/dist/elysia/plugin.d.cts +10 -0
- package/dist/elysia/plugin.d.ts +10 -0
- package/dist/elysia/plugin.js +105 -77
- package/dist/elysia/plugin.js.map +1 -1
- package/dist/h3/plugin.cjs +59 -59
- package/dist/h3/plugin.cjs.map +1 -1
- package/dist/h3/plugin.js +60 -60
- package/dist/h3/plugin.js.map +1 -1
- package/dist/h3/utils.cjs +1 -1
- package/dist/h3/utils.cjs.map +1 -1
- package/dist/h3/utils.js +1 -1
- package/dist/h3/utils.js.map +1 -1
- package/dist/hono/middleware.cjs +63 -64
- package/dist/hono/middleware.cjs.map +1 -1
- package/dist/hono/middleware.js +64 -65
- package/dist/hono/middleware.js.map +1 -1
- package/package.json +2 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/hono/middleware.ts"],"sourcesContent":["import { Context, Hono } from \"hono\";\nimport { MiddlewareHandler } from \"hono/types\";\nimport { AsyncLocalStorage } from \"node:async_hooks\";\nimport { performance } from \"node:perf_hooks\";\n\nimport { ApitallyClient } from \"../common/client.js\";\nimport { consumerFromStringOrObject } from \"../common/consumerRegistry.js\";\nimport { parseContentLength } from \"../common/headers.js\";\nimport type { LogRecord } from \"../common/requestLogger.js\";\nimport { convertHeaders } from \"../common/requestLogger.js\";\nimport {\n getResponseBody,\n getResponseJson,\n measureResponseSize,\n} from \"../common/response.js\";\nimport { ApitallyConfig, ApitallyConsumer } from \"../common/types.js\";\nimport { patchConsole, patchWinston } from \"../loggers/index.js\";\nimport { extractZodErrors, getAppInfo } from \"./utils.js\";\n\ndeclare module \"hono\" {\n interface ContextVariableMap {\n apitallyConsumer?: ApitallyConsumer | string;\n }\n}\n\nexport function useApitally(app: Hono, config: ApitallyConfig) {\n const client = new ApitallyClient(config);\n const middleware = getMiddleware(client);\n app.use(middleware);\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\nfunction getMiddleware(client: ApitallyClient): MiddlewareHandler {\n const logsContext = new AsyncLocalStorage<LogRecord[]>();\n\n if (client.requestLogger.config.captureLogs) {\n patchConsole(logsContext);\n patchWinston(logsContext);\n }\n\n return async (c, next) => {\n if (!client.isEnabled() || c.req.method.toUpperCase() === \"OPTIONS\") {\n await next();\n return;\n }\n\n await logsContext.run([], async () => {\n const timestamp = Date.now() / 1000;\n const startTime = performance.now();\n\n await next();\n\n let response;\n const responseTime = performance.now() - startTime;\n const [responseSize, newResponse] = await measureResponseSize(c.res);\n const requestSize = parseContentLength(c.req.header(\"content-length\"));\n\n const consumer = getConsumer(c);\n client.consumerRegistry.addOrUpdateConsumer(consumer);\n\n client.requestCounter.addRequest({\n consumer: consumer?.identifier,\n method: c.req.method,\n path: c.req.routePath,\n statusCode: c.res.status,\n responseTime,\n requestSize,\n responseSize,\n });\n\n response = newResponse;\n\n if (c.res.status === 400) {\n const [responseJson, newResponse] = await getResponseJson(response);\n const validationErrors = extractZodErrors(responseJson);\n validationErrors.forEach((error) => {\n client.validationErrorCounter.addValidationError({\n consumer: consumer?.identifier,\n method: c.req.method,\n path: c.req.routePath,\n ...error,\n });\n });\n response = newResponse;\n }\n\n if (c.error) {\n client.serverErrorCounter.addServerError({\n consumer: consumer?.identifier,\n method: c.req.method,\n path: c.req.routePath,\n type: c.error.name,\n msg: c.error.message,\n traceback: c.error.stack || \"\",\n });\n }\n\n if (client.requestLogger.enabled) {\n let requestBody;\n let responseBody;\n let newResponse = response;\n const requestContentType = c.req.header(\"content-type\");\n const responseContentType = c.res.headers.get(\"content-type\");\n if (\n client.requestLogger.config.logRequestBody &&\n client.requestLogger.isSupportedContentType(requestContentType)\n ) {\n requestBody = Buffer.from(await c.req.arrayBuffer());\n }\n if (\n client.requestLogger.config.logResponseBody &&\n client.requestLogger.isSupportedContentType(responseContentType)\n ) {\n [responseBody, newResponse] = await getResponseBody(response);\n response = newResponse;\n }\n const logs = logsContext.getStore();\n client.requestLogger.logRequest(\n {\n timestamp,\n method: c.req.method,\n path: c.req.routePath,\n url: c.req.url,\n headers: convertHeaders(c.req.header()),\n size: requestSize,\n consumer: consumer?.identifier,\n body: requestBody,\n },\n {\n statusCode: c.res.status,\n responseTime: responseTime / 1000,\n headers: convertHeaders(c.res.headers),\n size: responseSize,\n body: responseBody,\n },\n c.error,\n logs,\n );\n }\n c.res = response;\n });\n };\n}\n\nexport function setConsumer(\n c: Context,\n consumer: ApitallyConsumer | string | null | undefined,\n) {\n c.set(\"apitallyConsumer\", consumer || undefined);\n}\n\nfunction getConsumer(c: Context) {\n const consumer = c.get(\"apitallyConsumer\");\n if (consumer) {\n return consumerFromStringOrObject(consumer);\n }\n return null;\n}\n"],"mappings":"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","names":["useApitally","app","config","client","ApitallyClient","middleware","getMiddleware","use","setStartupData","attempt","appInfo","getAppInfo","appVersion","paths","length","startSync","setTimeout","logsContext","AsyncLocalStorage","requestLogger","captureLogs","patchConsole","patchWinston","c","next","isEnabled","req","method","toUpperCase","run","timestamp","Date","now","startTime","performance","response","responseTime","responseSize","newResponse","measureResponseSize","res","requestSize","parseContentLength","header","consumer","getConsumer","consumerRegistry","addOrUpdateConsumer","requestCounter","addRequest","identifier","path","routePath","statusCode","status","responseJson","getResponseJson","validationErrors","extractZodErrors","forEach","error","validationErrorCounter","addValidationError","serverErrorCounter","addServerError","type","name","msg","message","traceback","stack","enabled","requestBody","responseBody","requestContentType","responseContentType","headers","get","logRequestBody","isSupportedContentType","Buffer","from","arrayBuffer","logResponseBody","getResponseBody","logs","getStore","logRequest","url","convertHeaders","size","body","setConsumer","set","undefined","consumerFromStringOrObject"]}
|
|
1
|
+
{"version":3,"sources":["../../src/hono/middleware.ts"],"sourcesContent":["import { Context, Hono } from \"hono\";\nimport { MiddlewareHandler } from \"hono/types\";\nimport { AsyncLocalStorage } from \"node:async_hooks\";\nimport { performance } from \"node:perf_hooks\";\n\nimport { ApitallyClient } from \"../common/client.js\";\nimport { consumerFromStringOrObject } from \"../common/consumerRegistry.js\";\nimport { parseContentLength } from \"../common/headers.js\";\nimport type { LogRecord } from \"../common/requestLogger.js\";\nimport { convertHeaders } from \"../common/requestLogger.js\";\nimport { captureResponse, getResponseJson } from \"../common/response.js\";\nimport { ApitallyConfig, ApitallyConsumer } from \"../common/types.js\";\nimport { patchConsole, patchWinston } from \"../loggers/index.js\";\nimport { extractZodErrors, getAppInfo } from \"./utils.js\";\n\ndeclare module \"hono\" {\n interface ContextVariableMap {\n apitallyConsumer?: ApitallyConsumer | string;\n }\n}\n\nexport function useApitally(app: Hono, config: ApitallyConfig) {\n const client = new ApitallyClient(config);\n const middleware = getMiddleware(client);\n app.use(middleware);\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\nfunction getMiddleware(client: ApitallyClient): MiddlewareHandler {\n const logsContext = new AsyncLocalStorage<LogRecord[]>();\n\n if (client.requestLogger.config.captureLogs) {\n patchConsole(logsContext);\n patchWinston(logsContext);\n }\n\n return async (c, next) => {\n if (!client.isEnabled() || c.req.method.toUpperCase() === \"OPTIONS\") {\n await next();\n return;\n }\n\n await logsContext.run([], async () => {\n const timestamp = Date.now() / 1000;\n const startTime = performance.now();\n\n await next();\n\n const [newResponse, responsePromise] = captureResponse(c.res, {\n captureBody:\n (client.requestLogger.enabled &&\n client.requestLogger.config.logResponseBody &&\n client.requestLogger.isSupportedContentType(\n c.res.headers.get(\"content-type\"),\n )) ||\n (c.res.status === 400 &&\n c.res.headers.get(\"content-type\") === \"application/json\"),\n maxBodySize: client.requestLogger.maxBodySize,\n });\n c.res = newResponse;\n\n const statusCode = c.res.status;\n const responseHeaders = c.res.headers;\n\n responsePromise.then(async (capturedResponse) => {\n const responseTime = performance.now() - startTime;\n const requestSize = parseContentLength(c.req.header(\"content-length\"));\n const responseSize = capturedResponse.completed\n ? capturedResponse.size\n : undefined;\n\n const consumer = getConsumer(c);\n client.consumerRegistry.addOrUpdateConsumer(consumer);\n\n client.requestCounter.addRequest({\n consumer: consumer?.identifier,\n method: c.req.method,\n path: c.req.routePath,\n statusCode,\n responseTime,\n requestSize,\n responseSize,\n });\n\n if (statusCode === 400 && capturedResponse.body) {\n const responseJson = getResponseJson(capturedResponse.body);\n const validationErrors = extractZodErrors(responseJson);\n validationErrors.forEach((error) => {\n client.validationErrorCounter.addValidationError({\n consumer: consumer?.identifier,\n method: c.req.method,\n path: c.req.routePath,\n ...error,\n });\n });\n }\n\n if (c.error) {\n client.serverErrorCounter.addServerError({\n consumer: consumer?.identifier,\n method: c.req.method,\n path: c.req.routePath,\n type: c.error.name,\n msg: c.error.message,\n traceback: c.error.stack || \"\",\n });\n }\n\n if (client.requestLogger.enabled) {\n let requestBody;\n const responseBody = capturedResponse.body;\n const requestContentType = c.req.header(\"content-type\");\n if (\n client.requestLogger.config.logRequestBody &&\n client.requestLogger.isSupportedContentType(requestContentType)\n ) {\n requestBody = Buffer.from(await c.req.arrayBuffer());\n }\n const logs = logsContext.getStore();\n client.requestLogger.logRequest(\n {\n timestamp,\n method: c.req.method,\n path: c.req.routePath,\n url: c.req.url,\n headers: convertHeaders(c.req.header()),\n size: requestSize,\n consumer: consumer?.identifier,\n body: requestBody,\n },\n {\n statusCode: statusCode,\n responseTime: responseTime / 1000,\n headers: convertHeaders(responseHeaders),\n size: responseSize,\n body: responseBody,\n },\n c.error,\n logs,\n );\n }\n });\n });\n };\n}\n\nexport function setConsumer(\n c: Context,\n consumer: ApitallyConsumer | string | null | undefined,\n) {\n c.set(\"apitallyConsumer\", consumer || undefined);\n}\n\nfunction getConsumer(c: Context) {\n const consumer = c.get(\"apitallyConsumer\");\n if (consumer) {\n return consumerFromStringOrObject(consumer);\n }\n return null;\n}\n"],"mappings":"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","names":["useApitally","app","config","client","ApitallyClient","middleware","getMiddleware","use","setStartupData","attempt","appInfo","getAppInfo","appVersion","paths","length","startSync","setTimeout","logsContext","AsyncLocalStorage","requestLogger","captureLogs","patchConsole","patchWinston","c","next","isEnabled","req","method","toUpperCase","run","timestamp","Date","now","startTime","performance","newResponse","responsePromise","captureResponse","res","captureBody","enabled","logResponseBody","isSupportedContentType","headers","get","status","maxBodySize","statusCode","responseHeaders","then","capturedResponse","responseTime","requestSize","parseContentLength","header","responseSize","completed","size","undefined","consumer","getConsumer","consumerRegistry","addOrUpdateConsumer","requestCounter","addRequest","identifier","path","routePath","body","responseJson","getResponseJson","validationErrors","extractZodErrors","forEach","error","validationErrorCounter","addValidationError","serverErrorCounter","addServerError","type","name","msg","message","traceback","stack","requestBody","responseBody","requestContentType","logRequestBody","Buffer","from","arrayBuffer","logs","getStore","logRequest","url","convertHeaders","setConsumer","set","consumerFromStringOrObject"]}
|
package/dist/hono/middleware.js
CHANGED
|
@@ -6,7 +6,7 @@ import { ApitallyClient } from "../common/client.js";
|
|
|
6
6
|
import { consumerFromStringOrObject } from "../common/consumerRegistry.js";
|
|
7
7
|
import { parseContentLength } from "../common/headers.js";
|
|
8
8
|
import { convertHeaders } from "../common/requestLogger.js";
|
|
9
|
-
import {
|
|
9
|
+
import { captureResponse, getResponseJson } from "../common/response.js";
|
|
10
10
|
import { patchConsole, patchWinston } from "../loggers/index.js";
|
|
11
11
|
import { extractZodErrors, getAppInfo } from "./utils.js";
|
|
12
12
|
function useApitally(app, config) {
|
|
@@ -40,77 +40,76 @@ function getMiddleware(client) {
|
|
|
40
40
|
const timestamp = Date.now() / 1e3;
|
|
41
41
|
const startTime = performance.now();
|
|
42
42
|
await next();
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
const requestSize = parseContentLength(c.req.header("content-length"));
|
|
47
|
-
const consumer = getConsumer(c);
|
|
48
|
-
client.consumerRegistry.addOrUpdateConsumer(consumer);
|
|
49
|
-
client.requestCounter.addRequest({
|
|
50
|
-
consumer: consumer == null ? void 0 : consumer.identifier,
|
|
51
|
-
method: c.req.method,
|
|
52
|
-
path: c.req.routePath,
|
|
53
|
-
statusCode: c.res.status,
|
|
54
|
-
responseTime,
|
|
55
|
-
requestSize,
|
|
56
|
-
responseSize
|
|
43
|
+
const [newResponse, responsePromise] = captureResponse(c.res, {
|
|
44
|
+
captureBody: client.requestLogger.enabled && client.requestLogger.config.logResponseBody && client.requestLogger.isSupportedContentType(c.res.headers.get("content-type")) || c.res.status === 400 && c.res.headers.get("content-type") === "application/json",
|
|
45
|
+
maxBodySize: client.requestLogger.maxBodySize
|
|
57
46
|
});
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
});
|
|
69
|
-
});
|
|
70
|
-
response = newResponse2;
|
|
71
|
-
}
|
|
72
|
-
if (c.error) {
|
|
73
|
-
client.serverErrorCounter.addServerError({
|
|
47
|
+
c.res = newResponse;
|
|
48
|
+
const statusCode = c.res.status;
|
|
49
|
+
const responseHeaders = c.res.headers;
|
|
50
|
+
responsePromise.then(async (capturedResponse) => {
|
|
51
|
+
const responseTime = performance.now() - startTime;
|
|
52
|
+
const requestSize = parseContentLength(c.req.header("content-length"));
|
|
53
|
+
const responseSize = capturedResponse.completed ? capturedResponse.size : void 0;
|
|
54
|
+
const consumer = getConsumer(c);
|
|
55
|
+
client.consumerRegistry.addOrUpdateConsumer(consumer);
|
|
56
|
+
client.requestCounter.addRequest({
|
|
74
57
|
consumer: consumer == null ? void 0 : consumer.identifier,
|
|
75
58
|
method: c.req.method,
|
|
76
59
|
path: c.req.routePath,
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
60
|
+
statusCode,
|
|
61
|
+
responseTime,
|
|
62
|
+
requestSize,
|
|
63
|
+
responseSize
|
|
80
64
|
});
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
65
|
+
if (statusCode === 400 && capturedResponse.body) {
|
|
66
|
+
const responseJson = getResponseJson(capturedResponse.body);
|
|
67
|
+
const validationErrors = extractZodErrors(responseJson);
|
|
68
|
+
validationErrors.forEach((error) => {
|
|
69
|
+
client.validationErrorCounter.addValidationError({
|
|
70
|
+
consumer: consumer == null ? void 0 : consumer.identifier,
|
|
71
|
+
method: c.req.method,
|
|
72
|
+
path: c.req.routePath,
|
|
73
|
+
...error
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
if (c.error) {
|
|
78
|
+
client.serverErrorCounter.addServerError({
|
|
79
|
+
consumer: consumer == null ? void 0 : consumer.identifier,
|
|
80
|
+
method: c.req.method,
|
|
81
|
+
path: c.req.routePath,
|
|
82
|
+
type: c.error.name,
|
|
83
|
+
msg: c.error.message,
|
|
84
|
+
traceback: c.error.stack || ""
|
|
85
|
+
});
|
|
90
86
|
}
|
|
91
|
-
if (client.requestLogger.
|
|
92
|
-
|
|
93
|
-
|
|
87
|
+
if (client.requestLogger.enabled) {
|
|
88
|
+
let requestBody;
|
|
89
|
+
const responseBody = capturedResponse.body;
|
|
90
|
+
const requestContentType = c.req.header("content-type");
|
|
91
|
+
if (client.requestLogger.config.logRequestBody && client.requestLogger.isSupportedContentType(requestContentType)) {
|
|
92
|
+
requestBody = Buffer.from(await c.req.arrayBuffer());
|
|
93
|
+
}
|
|
94
|
+
const logs = logsContext.getStore();
|
|
95
|
+
client.requestLogger.logRequest({
|
|
96
|
+
timestamp,
|
|
97
|
+
method: c.req.method,
|
|
98
|
+
path: c.req.routePath,
|
|
99
|
+
url: c.req.url,
|
|
100
|
+
headers: convertHeaders(c.req.header()),
|
|
101
|
+
size: requestSize,
|
|
102
|
+
consumer: consumer == null ? void 0 : consumer.identifier,
|
|
103
|
+
body: requestBody
|
|
104
|
+
}, {
|
|
105
|
+
statusCode,
|
|
106
|
+
responseTime: responseTime / 1e3,
|
|
107
|
+
headers: convertHeaders(responseHeaders),
|
|
108
|
+
size: responseSize,
|
|
109
|
+
body: responseBody
|
|
110
|
+
}, c.error, logs);
|
|
94
111
|
}
|
|
95
|
-
|
|
96
|
-
client.requestLogger.logRequest({
|
|
97
|
-
timestamp,
|
|
98
|
-
method: c.req.method,
|
|
99
|
-
path: c.req.routePath,
|
|
100
|
-
url: c.req.url,
|
|
101
|
-
headers: convertHeaders(c.req.header()),
|
|
102
|
-
size: requestSize,
|
|
103
|
-
consumer: consumer == null ? void 0 : consumer.identifier,
|
|
104
|
-
body: requestBody
|
|
105
|
-
}, {
|
|
106
|
-
statusCode: c.res.status,
|
|
107
|
-
responseTime: responseTime / 1e3,
|
|
108
|
-
headers: convertHeaders(c.res.headers),
|
|
109
|
-
size: responseSize,
|
|
110
|
-
body: responseBody
|
|
111
|
-
}, c.error, logs);
|
|
112
|
-
}
|
|
113
|
-
c.res = response;
|
|
112
|
+
});
|
|
114
113
|
});
|
|
115
114
|
};
|
|
116
115
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/hono/middleware.ts"],"sourcesContent":["import { Context, Hono } from \"hono\";\nimport { MiddlewareHandler } from \"hono/types\";\nimport { AsyncLocalStorage } from \"node:async_hooks\";\nimport { performance } from \"node:perf_hooks\";\n\nimport { ApitallyClient } from \"../common/client.js\";\nimport { consumerFromStringOrObject } from \"../common/consumerRegistry.js\";\nimport { parseContentLength } from \"../common/headers.js\";\nimport type { LogRecord } from \"../common/requestLogger.js\";\nimport { convertHeaders } from \"../common/requestLogger.js\";\nimport {\n getResponseBody,\n getResponseJson,\n measureResponseSize,\n} from \"../common/response.js\";\nimport { ApitallyConfig, ApitallyConsumer } from \"../common/types.js\";\nimport { patchConsole, patchWinston } from \"../loggers/index.js\";\nimport { extractZodErrors, getAppInfo } from \"./utils.js\";\n\ndeclare module \"hono\" {\n interface ContextVariableMap {\n apitallyConsumer?: ApitallyConsumer | string;\n }\n}\n\nexport function useApitally(app: Hono, config: ApitallyConfig) {\n const client = new ApitallyClient(config);\n const middleware = getMiddleware(client);\n app.use(middleware);\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\nfunction getMiddleware(client: ApitallyClient): MiddlewareHandler {\n const logsContext = new AsyncLocalStorage<LogRecord[]>();\n\n if (client.requestLogger.config.captureLogs) {\n patchConsole(logsContext);\n patchWinston(logsContext);\n }\n\n return async (c, next) => {\n if (!client.isEnabled() || c.req.method.toUpperCase() === \"OPTIONS\") {\n await next();\n return;\n }\n\n await logsContext.run([], async () => {\n const timestamp = Date.now() / 1000;\n const startTime = performance.now();\n\n await next();\n\n let response;\n const responseTime = performance.now() - startTime;\n const [responseSize, newResponse] = await measureResponseSize(c.res);\n const requestSize = parseContentLength(c.req.header(\"content-length\"));\n\n const consumer = getConsumer(c);\n client.consumerRegistry.addOrUpdateConsumer(consumer);\n\n client.requestCounter.addRequest({\n consumer: consumer?.identifier,\n method: c.req.method,\n path: c.req.routePath,\n statusCode: c.res.status,\n responseTime,\n requestSize,\n responseSize,\n });\n\n response = newResponse;\n\n if (c.res.status === 400) {\n const [responseJson, newResponse] = await getResponseJson(response);\n const validationErrors = extractZodErrors(responseJson);\n validationErrors.forEach((error) => {\n client.validationErrorCounter.addValidationError({\n consumer: consumer?.identifier,\n method: c.req.method,\n path: c.req.routePath,\n ...error,\n });\n });\n response = newResponse;\n }\n\n if (c.error) {\n client.serverErrorCounter.addServerError({\n consumer: consumer?.identifier,\n method: c.req.method,\n path: c.req.routePath,\n type: c.error.name,\n msg: c.error.message,\n traceback: c.error.stack || \"\",\n });\n }\n\n if (client.requestLogger.enabled) {\n let requestBody;\n let responseBody;\n let newResponse = response;\n const requestContentType = c.req.header(\"content-type\");\n const responseContentType = c.res.headers.get(\"content-type\");\n if (\n client.requestLogger.config.logRequestBody &&\n client.requestLogger.isSupportedContentType(requestContentType)\n ) {\n requestBody = Buffer.from(await c.req.arrayBuffer());\n }\n if (\n client.requestLogger.config.logResponseBody &&\n client.requestLogger.isSupportedContentType(responseContentType)\n ) {\n [responseBody, newResponse] = await getResponseBody(response);\n response = newResponse;\n }\n const logs = logsContext.getStore();\n client.requestLogger.logRequest(\n {\n timestamp,\n method: c.req.method,\n path: c.req.routePath,\n url: c.req.url,\n headers: convertHeaders(c.req.header()),\n size: requestSize,\n consumer: consumer?.identifier,\n body: requestBody,\n },\n {\n statusCode: c.res.status,\n responseTime: responseTime / 1000,\n headers: convertHeaders(c.res.headers),\n size: responseSize,\n body: responseBody,\n },\n c.error,\n logs,\n );\n }\n c.res = response;\n });\n };\n}\n\nexport function setConsumer(\n c: Context,\n consumer: ApitallyConsumer | string | null | undefined,\n) {\n c.set(\"apitallyConsumer\", consumer || undefined);\n}\n\nfunction getConsumer(c: Context) {\n const consumer = c.get(\"apitallyConsumer\");\n if (consumer) {\n return consumerFromStringOrObject(consumer);\n }\n return null;\n}\n"],"mappings":"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","names":["AsyncLocalStorage","performance","ApitallyClient","consumerFromStringOrObject","parseContentLength","convertHeaders","getResponseBody","getResponseJson","measureResponseSize","patchConsole","patchWinston","extractZodErrors","getAppInfo","useApitally","app","config","client","middleware","getMiddleware","use","setStartupData","attempt","appInfo","appVersion","paths","length","startSync","setTimeout","logsContext","requestLogger","captureLogs","c","next","isEnabled","req","method","toUpperCase","run","timestamp","Date","now","startTime","response","responseTime","responseSize","newResponse","res","requestSize","header","consumer","getConsumer","consumerRegistry","addOrUpdateConsumer","requestCounter","addRequest","identifier","path","routePath","statusCode","status","responseJson","validationErrors","forEach","error","validationErrorCounter","addValidationError","serverErrorCounter","addServerError","type","name","msg","message","traceback","stack","enabled","requestBody","responseBody","requestContentType","responseContentType","headers","get","logRequestBody","isSupportedContentType","Buffer","from","arrayBuffer","logResponseBody","logs","getStore","logRequest","url","size","body","setConsumer","set","undefined"]}
|
|
1
|
+
{"version":3,"sources":["../../src/hono/middleware.ts"],"sourcesContent":["import { Context, Hono } from \"hono\";\nimport { MiddlewareHandler } from \"hono/types\";\nimport { AsyncLocalStorage } from \"node:async_hooks\";\nimport { performance } from \"node:perf_hooks\";\n\nimport { ApitallyClient } from \"../common/client.js\";\nimport { consumerFromStringOrObject } from \"../common/consumerRegistry.js\";\nimport { parseContentLength } from \"../common/headers.js\";\nimport type { LogRecord } from \"../common/requestLogger.js\";\nimport { convertHeaders } from \"../common/requestLogger.js\";\nimport { captureResponse, getResponseJson } from \"../common/response.js\";\nimport { ApitallyConfig, ApitallyConsumer } from \"../common/types.js\";\nimport { patchConsole, patchWinston } from \"../loggers/index.js\";\nimport { extractZodErrors, getAppInfo } from \"./utils.js\";\n\ndeclare module \"hono\" {\n interface ContextVariableMap {\n apitallyConsumer?: ApitallyConsumer | string;\n }\n}\n\nexport function useApitally(app: Hono, config: ApitallyConfig) {\n const client = new ApitallyClient(config);\n const middleware = getMiddleware(client);\n app.use(middleware);\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\nfunction getMiddleware(client: ApitallyClient): MiddlewareHandler {\n const logsContext = new AsyncLocalStorage<LogRecord[]>();\n\n if (client.requestLogger.config.captureLogs) {\n patchConsole(logsContext);\n patchWinston(logsContext);\n }\n\n return async (c, next) => {\n if (!client.isEnabled() || c.req.method.toUpperCase() === \"OPTIONS\") {\n await next();\n return;\n }\n\n await logsContext.run([], async () => {\n const timestamp = Date.now() / 1000;\n const startTime = performance.now();\n\n await next();\n\n const [newResponse, responsePromise] = captureResponse(c.res, {\n captureBody:\n (client.requestLogger.enabled &&\n client.requestLogger.config.logResponseBody &&\n client.requestLogger.isSupportedContentType(\n c.res.headers.get(\"content-type\"),\n )) ||\n (c.res.status === 400 &&\n c.res.headers.get(\"content-type\") === \"application/json\"),\n maxBodySize: client.requestLogger.maxBodySize,\n });\n c.res = newResponse;\n\n const statusCode = c.res.status;\n const responseHeaders = c.res.headers;\n\n responsePromise.then(async (capturedResponse) => {\n const responseTime = performance.now() - startTime;\n const requestSize = parseContentLength(c.req.header(\"content-length\"));\n const responseSize = capturedResponse.completed\n ? capturedResponse.size\n : undefined;\n\n const consumer = getConsumer(c);\n client.consumerRegistry.addOrUpdateConsumer(consumer);\n\n client.requestCounter.addRequest({\n consumer: consumer?.identifier,\n method: c.req.method,\n path: c.req.routePath,\n statusCode,\n responseTime,\n requestSize,\n responseSize,\n });\n\n if (statusCode === 400 && capturedResponse.body) {\n const responseJson = getResponseJson(capturedResponse.body);\n const validationErrors = extractZodErrors(responseJson);\n validationErrors.forEach((error) => {\n client.validationErrorCounter.addValidationError({\n consumer: consumer?.identifier,\n method: c.req.method,\n path: c.req.routePath,\n ...error,\n });\n });\n }\n\n if (c.error) {\n client.serverErrorCounter.addServerError({\n consumer: consumer?.identifier,\n method: c.req.method,\n path: c.req.routePath,\n type: c.error.name,\n msg: c.error.message,\n traceback: c.error.stack || \"\",\n });\n }\n\n if (client.requestLogger.enabled) {\n let requestBody;\n const responseBody = capturedResponse.body;\n const requestContentType = c.req.header(\"content-type\");\n if (\n client.requestLogger.config.logRequestBody &&\n client.requestLogger.isSupportedContentType(requestContentType)\n ) {\n requestBody = Buffer.from(await c.req.arrayBuffer());\n }\n const logs = logsContext.getStore();\n client.requestLogger.logRequest(\n {\n timestamp,\n method: c.req.method,\n path: c.req.routePath,\n url: c.req.url,\n headers: convertHeaders(c.req.header()),\n size: requestSize,\n consumer: consumer?.identifier,\n body: requestBody,\n },\n {\n statusCode: statusCode,\n responseTime: responseTime / 1000,\n headers: convertHeaders(responseHeaders),\n size: responseSize,\n body: responseBody,\n },\n c.error,\n logs,\n );\n }\n });\n });\n };\n}\n\nexport function setConsumer(\n c: Context,\n consumer: ApitallyConsumer | string | null | undefined,\n) {\n c.set(\"apitallyConsumer\", consumer || undefined);\n}\n\nfunction getConsumer(c: Context) {\n const consumer = c.get(\"apitallyConsumer\");\n if (consumer) {\n return consumerFromStringOrObject(consumer);\n }\n return null;\n}\n"],"mappings":"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","names":["AsyncLocalStorage","performance","ApitallyClient","consumerFromStringOrObject","parseContentLength","convertHeaders","captureResponse","getResponseJson","patchConsole","patchWinston","extractZodErrors","getAppInfo","useApitally","app","config","client","middleware","getMiddleware","use","setStartupData","attempt","appInfo","appVersion","paths","length","startSync","setTimeout","logsContext","requestLogger","captureLogs","c","next","isEnabled","req","method","toUpperCase","run","timestamp","Date","now","startTime","newResponse","responsePromise","res","captureBody","enabled","logResponseBody","isSupportedContentType","headers","get","status","maxBodySize","statusCode","responseHeaders","then","capturedResponse","responseTime","requestSize","header","responseSize","completed","size","undefined","consumer","getConsumer","consumerRegistry","addOrUpdateConsumer","requestCounter","addRequest","identifier","path","routePath","body","responseJson","validationErrors","forEach","error","validationErrorCounter","addValidationError","serverErrorCounter","addServerError","type","name","msg","message","traceback","stack","requestBody","responseBody","requestContentType","logRequestBody","Buffer","from","arrayBuffer","logs","getStore","logRequest","url","setConsumer","set"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "apitally",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.22.0",
|
|
4
4
|
"description": "Simple API monitoring & analytics for REST APIs built with Express, Fastify, NestJS, AdonisJS, Hono, H3, Elysia, Hapi, and Koa.",
|
|
5
5
|
"author": "Apitally <hello@apitally.io>",
|
|
6
6
|
"license": "MIT",
|
|
@@ -302,7 +302,7 @@
|
|
|
302
302
|
"fastify": "^5.0.0",
|
|
303
303
|
"fastify-plugin": "^5.0.1",
|
|
304
304
|
"globals": "^16.2.0",
|
|
305
|
-
"h3": "2.0.
|
|
305
|
+
"h3": "2.0.1-rc.5",
|
|
306
306
|
"hono": "^4.6.5",
|
|
307
307
|
"koa": "^3.0.0",
|
|
308
308
|
"koa-bodyparser": "^4.4.1",
|