apitally 0.11.0 → 0.11.2
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/README.md +3 -3
- package/dist/common/client.cjs +37 -21
- package/dist/common/client.cjs.map +1 -1
- package/dist/common/client.js +37 -21
- package/dist/common/client.js.map +1 -1
- package/dist/common/requestLogger.cjs +47 -21
- package/dist/common/requestLogger.cjs.map +1 -1
- package/dist/common/requestLogger.d.cts +2 -0
- package/dist/common/requestLogger.d.ts +2 -0
- package/dist/common/requestLogger.js +37 -21
- package/dist/common/requestLogger.js.map +1 -1
- package/dist/express/index.cjs +37 -21
- package/dist/express/index.cjs.map +1 -1
- package/dist/express/index.js +37 -21
- package/dist/express/index.js.map +1 -1
- package/dist/express/middleware.cjs +37 -21
- package/dist/express/middleware.cjs.map +1 -1
- package/dist/express/middleware.js +37 -21
- package/dist/express/middleware.js.map +1 -1
- package/dist/fastify/index.cjs +37 -21
- package/dist/fastify/index.cjs.map +1 -1
- package/dist/fastify/index.js +37 -21
- package/dist/fastify/index.js.map +1 -1
- package/dist/fastify/plugin.cjs +37 -21
- package/dist/fastify/plugin.cjs.map +1 -1
- package/dist/fastify/plugin.js +37 -21
- package/dist/fastify/plugin.js.map +1 -1
- package/dist/hono/index.cjs +37 -21
- package/dist/hono/index.cjs.map +1 -1
- package/dist/hono/index.js +37 -21
- package/dist/hono/index.js.map +1 -1
- package/dist/hono/middleware.cjs +37 -21
- package/dist/hono/middleware.cjs.map +1 -1
- package/dist/hono/middleware.js +37 -21
- package/dist/hono/middleware.js.map +1 -1
- package/dist/koa/index.cjs +37 -21
- package/dist/koa/index.cjs.map +1 -1
- package/dist/koa/index.js +37 -21
- package/dist/koa/index.js.map +1 -1
- package/dist/koa/middleware.cjs +37 -21
- package/dist/koa/middleware.cjs.map +1 -1
- package/dist/koa/middleware.js +37 -21
- package/dist/koa/middleware.js.map +1 -1
- package/dist/nestjs/index.cjs +37 -21
- package/dist/nestjs/index.cjs.map +1 -1
- package/dist/nestjs/index.js +37 -21
- package/dist/nestjs/index.js.map +1 -1
- package/package.json +3 -1
|
@@ -2,6 +2,7 @@ var __defProp = Object.defineProperty;
|
|
|
2
2
|
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
3
3
|
|
|
4
4
|
// src/common/requestLogger.ts
|
|
5
|
+
import AsyncLock from "async-lock";
|
|
5
6
|
import { Buffer as Buffer3 } from "buffer";
|
|
6
7
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
7
8
|
import { unlinkSync as unlinkSync2, writeFileSync } from "fs";
|
|
@@ -89,12 +90,18 @@ var ALLOWED_CONTENT_TYPES = [
|
|
|
89
90
|
"text/plain"
|
|
90
91
|
];
|
|
91
92
|
var EXCLUDE_PATH_PATTERNS = [
|
|
92
|
-
/\/_?healthz
|
|
93
|
-
/\/_?health[_-]?checks
|
|
94
|
-
/\/_?heart[_-]?beats
|
|
95
|
-
/\/ping
|
|
96
|
-
/\/ready
|
|
97
|
-
/\/live$/
|
|
93
|
+
/\/_?healthz?$/i,
|
|
94
|
+
/\/_?health[_-]?checks?$/i,
|
|
95
|
+
/\/_?heart[_-]?beats?$/i,
|
|
96
|
+
/\/ping$/i,
|
|
97
|
+
/\/ready$/i,
|
|
98
|
+
/\/live$/i
|
|
99
|
+
];
|
|
100
|
+
var EXCLUDE_USER_AGENT_PATTERNS = [
|
|
101
|
+
/health[-_ ]?check/i,
|
|
102
|
+
/microsoft-azure-application-lb/i,
|
|
103
|
+
/googlehc/i,
|
|
104
|
+
/kube-probe/i
|
|
98
105
|
];
|
|
99
106
|
var MASK_QUERY_PARAM_PATTERNS = [
|
|
100
107
|
/auth/i,
|
|
@@ -130,6 +137,7 @@ var _RequestLogger = class _RequestLogger {
|
|
|
130
137
|
currentFile = null;
|
|
131
138
|
files = [];
|
|
132
139
|
maintainIntervalId;
|
|
140
|
+
lock = new AsyncLock();
|
|
133
141
|
constructor(config) {
|
|
134
142
|
this.config = {
|
|
135
143
|
...DEFAULT_CONFIG,
|
|
@@ -149,6 +157,9 @@ var _RequestLogger = class _RequestLogger {
|
|
|
149
157
|
];
|
|
150
158
|
return matchPatterns(urlPath, patterns);
|
|
151
159
|
}
|
|
160
|
+
shouldExcludeUserAgent(userAgent) {
|
|
161
|
+
return userAgent ? matchPatterns(userAgent, EXCLUDE_USER_AGENT_PATTERNS) : false;
|
|
162
|
+
}
|
|
152
163
|
shouldMaskQueryParam(name) {
|
|
153
164
|
const patterns = [
|
|
154
165
|
...this.config.maskQueryParams,
|
|
@@ -184,11 +195,12 @@ var _RequestLogger = class _RequestLogger {
|
|
|
184
195
|
]);
|
|
185
196
|
}
|
|
186
197
|
logRequest(request, response) {
|
|
187
|
-
var _a, _b;
|
|
198
|
+
var _a, _b, _c;
|
|
188
199
|
if (!this.enabled || this.suspendUntil !== null) return;
|
|
189
200
|
const url = new URL(request.url);
|
|
190
201
|
const path = request.path ?? url.pathname;
|
|
191
|
-
|
|
202
|
+
const userAgent = (_a = request.headers.find(([k]) => k.toLowerCase() === "user-agent")) == null ? void 0 : _a[1];
|
|
203
|
+
if (this.shouldExcludePath(path) || this.shouldExcludeUserAgent(userAgent) || (((_c = (_b = this.config).excludeCallback) == null ? void 0 : _c.call(_b, request, response)) ?? false)) {
|
|
192
204
|
return;
|
|
193
205
|
}
|
|
194
206
|
url.search = this.config.logQueryParams ? this.maskQueryParams(url.search) : "";
|
|
@@ -251,15 +263,17 @@ var _RequestLogger = class _RequestLogger {
|
|
|
251
263
|
if (!this.enabled || this.pendingWrites.length === 0) {
|
|
252
264
|
return;
|
|
253
265
|
}
|
|
254
|
-
|
|
255
|
-
this.currentFile
|
|
256
|
-
|
|
257
|
-
while (this.pendingWrites.length > 0) {
|
|
258
|
-
const item = this.pendingWrites.shift();
|
|
259
|
-
if (item) {
|
|
260
|
-
await this.currentFile.writeLine(Buffer3.from(item));
|
|
266
|
+
return this.lock.acquire("file", async () => {
|
|
267
|
+
if (!this.currentFile) {
|
|
268
|
+
this.currentFile = new TempGzipFile();
|
|
261
269
|
}
|
|
262
|
-
|
|
270
|
+
while (this.pendingWrites.length > 0) {
|
|
271
|
+
const item = this.pendingWrites.shift();
|
|
272
|
+
if (item) {
|
|
273
|
+
await this.currentFile.writeLine(Buffer3.from(item));
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
});
|
|
263
277
|
}
|
|
264
278
|
getFile() {
|
|
265
279
|
return this.files.shift();
|
|
@@ -268,11 +282,13 @@ var _RequestLogger = class _RequestLogger {
|
|
|
268
282
|
this.files.unshift(file);
|
|
269
283
|
}
|
|
270
284
|
async rotateFile() {
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
285
|
+
return this.lock.acquire("file", async () => {
|
|
286
|
+
if (this.currentFile) {
|
|
287
|
+
await this.currentFile.close();
|
|
288
|
+
this.files.push(this.currentFile);
|
|
289
|
+
this.currentFile = null;
|
|
290
|
+
}
|
|
291
|
+
});
|
|
276
292
|
}
|
|
277
293
|
async maintain() {
|
|
278
294
|
await this.writeToFile();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/common/requestLogger.ts","../../src/common/tempGzipFile.ts"],"sourcesContent":["import { Buffer } from \"buffer\";\nimport { randomUUID } from \"crypto\";\nimport { unlinkSync, writeFileSync } from \"fs\";\nimport { IncomingHttpHeaders, OutgoingHttpHeaders } from \"http\";\nimport { tmpdir } from \"os\";\nimport { join } from \"path\";\n\nimport TempGzipFile from \"./tempGzipFile.js\";\n\nconst MAX_BODY_SIZE = 50_000; // 50 KB (uncompressed)\nconst MAX_FILE_SIZE = 1_000_000; // 1 MB (compressed)\nconst MAX_FILES = 50;\nconst MAX_PENDING_WRITES = 100;\nconst BODY_TOO_LARGE = Buffer.from(\"<body too large>\");\nconst BODY_MASKED = Buffer.from(\"<masked>\");\nconst MASKED = \"******\";\nconst ALLOWED_CONTENT_TYPES = [\"application/json\", \"text/plain\"];\nconst EXCLUDE_PATH_PATTERNS = [\n /\\/_?healthz?$/,\n /\\/_?health[_-]?checks?$/,\n /\\/_?heart[_-]?beats?$/,\n /\\/ping$/,\n /\\/ready$/,\n /\\/live$/,\n];\nconst MASK_QUERY_PARAM_PATTERNS = [\n /auth/i,\n /api-?key/i,\n /secret/i,\n /token/i,\n /password/i,\n /pwd/i,\n];\nconst MASK_HEADER_PATTERNS = [\n /auth/i,\n /api-?key/i,\n /secret/i,\n /token/i,\n /cookie/i,\n];\n\nexport type Request = {\n timestamp: number;\n method: string;\n path?: string;\n url: string;\n headers: [string, string][];\n size?: number;\n consumer?: string;\n body?: Buffer;\n};\n\nexport type Response = {\n statusCode: number;\n responseTime: number;\n headers: [string, string][];\n size?: number;\n body?: Buffer;\n};\n\nexport type RequestLoggingConfig = {\n enabled: boolean;\n logQueryParams: boolean;\n logRequestHeaders: boolean;\n logRequestBody: boolean;\n logResponseHeaders: boolean;\n logResponseBody: boolean;\n maskQueryParams: RegExp[];\n maskHeaders: RegExp[];\n maskRequestBodyCallback?: (request: Request) => Buffer | null | undefined;\n maskResponseBodyCallback?: (\n request: Request,\n response: Response,\n ) => Buffer | null | undefined;\n excludePaths: RegExp[];\n excludeCallback?: (request: Request, response: Response) => boolean;\n};\n\nconst DEFAULT_CONFIG: RequestLoggingConfig = {\n enabled: false,\n logQueryParams: true,\n logRequestHeaders: false,\n logRequestBody: false,\n logResponseHeaders: true,\n logResponseBody: false,\n maskQueryParams: [],\n maskHeaders: [],\n excludePaths: [],\n};\n\nexport default class RequestLogger {\n public config: RequestLoggingConfig;\n public enabled: boolean;\n public suspendUntil: number | null = null;\n private pendingWrites: string[] = [];\n private currentFile: TempGzipFile | null = null;\n private files: TempGzipFile[] = [];\n private maintainIntervalId?: NodeJS.Timeout;\n\n constructor(config?: Partial<RequestLoggingConfig>) {\n this.config = { ...DEFAULT_CONFIG, ...config };\n this.enabled = this.config.enabled && checkWritableFs();\n\n if (this.enabled) {\n this.maintainIntervalId = setInterval(() => {\n this.maintain();\n }, 1000);\n }\n }\n\n private shouldExcludePath(urlPath: string) {\n const patterns = [...this.config.excludePaths, ...EXCLUDE_PATH_PATTERNS];\n return matchPatterns(urlPath, patterns);\n }\n\n private shouldMaskQueryParam(name: string) {\n const patterns = [\n ...this.config.maskQueryParams,\n ...MASK_QUERY_PARAM_PATTERNS,\n ];\n return matchPatterns(name, patterns);\n }\n\n private shouldMaskHeader(name: string) {\n const patterns = [...this.config.maskHeaders, ...MASK_HEADER_PATTERNS];\n return matchPatterns(name, patterns);\n }\n\n private hasSupportedContentType(headers: [string, string][]) {\n const contentType = headers.find(\n ([k]) => k.toLowerCase() === \"content-type\",\n )?.[1];\n return (\n contentType !== undefined &&\n ALLOWED_CONTENT_TYPES.some((t) => contentType.startsWith(t))\n );\n }\n\n private maskQueryParams(search: string) {\n const params = new URLSearchParams(search);\n for (const [key] of params) {\n if (this.shouldMaskQueryParam(key)) {\n params.set(key, MASKED);\n }\n }\n return params.toString();\n }\n\n private maskHeaders(headers: [string, string][]): [string, string][] {\n return headers.map(([k, v]) => [k, this.shouldMaskHeader(k) ? MASKED : v]);\n }\n\n logRequest(request: Request, response: Response) {\n if (!this.enabled || this.suspendUntil !== null) return;\n\n const url = new URL(request.url);\n const path = request.path ?? url.pathname;\n\n if (\n this.shouldExcludePath(path) ||\n (this.config.excludeCallback?.(request, response) ?? false)\n ) {\n return;\n }\n\n // Process query params\n url.search = this.config.logQueryParams\n ? this.maskQueryParams(url.search)\n : \"\";\n request.url = url.toString();\n\n // Process headers\n request.headers = this.config.logRequestHeaders\n ? this.maskHeaders(request.headers)\n : [];\n response.headers = this.config.logResponseHeaders\n ? this.maskHeaders(response.headers)\n : [];\n\n // Process request body\n if (\n !this.config.logRequestBody ||\n !this.hasSupportedContentType(request.headers)\n ) {\n request.body = undefined;\n } else if (request.body) {\n if (request.body.length > MAX_BODY_SIZE) {\n request.body = BODY_TOO_LARGE;\n } else if (this.config.maskRequestBodyCallback) {\n try {\n request.body =\n this.config.maskRequestBodyCallback(request) ?? BODY_MASKED;\n if (request.body.length > MAX_BODY_SIZE) {\n request.body = BODY_TOO_LARGE;\n }\n } catch {\n request.body = undefined;\n }\n }\n }\n\n // Process response body\n if (\n !this.config.logResponseBody ||\n !this.hasSupportedContentType(response.headers)\n ) {\n response.body = undefined;\n } else if (response.body) {\n if (response.body.length > MAX_BODY_SIZE) {\n response.body = BODY_TOO_LARGE;\n } else if (this.config.maskResponseBodyCallback) {\n try {\n response.body =\n this.config.maskResponseBodyCallback(request, response) ??\n BODY_MASKED;\n if (response.body.length > MAX_BODY_SIZE) {\n response.body = BODY_TOO_LARGE;\n }\n } catch {\n response.body = undefined;\n }\n }\n }\n\n const item = {\n uuid: randomUUID(),\n request: skipEmptyValues(request),\n response: skipEmptyValues(response),\n };\n [item.request.body, item.response.body].forEach((body) => {\n if (body) {\n // @ts-expect-error Different return type\n body.toJSON = function () {\n return this.toString(\"base64\");\n };\n }\n });\n this.pendingWrites.push(JSON.stringify(item));\n\n if (this.pendingWrites.length > MAX_PENDING_WRITES) {\n this.pendingWrites.shift();\n }\n }\n\n async writeToFile() {\n if (!this.enabled || this.pendingWrites.length === 0) {\n return;\n }\n if (!this.currentFile) {\n this.currentFile = new TempGzipFile();\n }\n while (this.pendingWrites.length > 0) {\n const item = this.pendingWrites.shift();\n if (item) {\n await this.currentFile.writeLine(Buffer.from(item));\n }\n }\n }\n\n getFile() {\n return this.files.shift();\n }\n\n retryFileLater(file: TempGzipFile) {\n this.files.unshift(file);\n }\n\n async rotateFile() {\n if (this.currentFile) {\n await this.currentFile.close();\n this.files.push(this.currentFile);\n this.currentFile = null;\n }\n }\n\n async maintain() {\n await this.writeToFile();\n if (this.currentFile && this.currentFile.size > MAX_FILE_SIZE) {\n await this.rotateFile();\n }\n while (this.files.length > MAX_FILES) {\n const file = this.files.shift();\n file?.delete();\n }\n if (this.suspendUntil !== null && this.suspendUntil < Date.now()) {\n this.suspendUntil = null;\n }\n }\n\n async clear() {\n this.pendingWrites = [];\n await this.rotateFile();\n this.files.forEach((file) => {\n file.delete();\n });\n this.files = [];\n }\n\n async close() {\n this.enabled = false;\n await this.clear();\n if (this.maintainIntervalId) {\n clearInterval(this.maintainIntervalId);\n }\n }\n}\n\nexport function convertHeaders(\n headers:\n | Headers\n | IncomingHttpHeaders\n | OutgoingHttpHeaders\n | Record<string, string | string[] | number | undefined>,\n) {\n if (headers instanceof Headers) {\n return Array.from(headers.entries());\n }\n return Object.entries(headers).flatMap(([key, value]) => {\n if (value === undefined) {\n return [];\n }\n if (Array.isArray(value)) {\n return value.map((v) => [key, v]);\n }\n return [[key, value.toString()]];\n }) as [string, string][];\n}\n\nexport function convertBody(body: any, contentType?: string | null) {\n if (!body || !contentType) {\n return;\n }\n try {\n if (contentType.startsWith(\"application/json\")) {\n if (isValidJsonString(body)) {\n return Buffer.from(body);\n } else {\n return Buffer.from(JSON.stringify(body));\n }\n }\n if (contentType.startsWith(\"text/\") && typeof body === \"string\") {\n return Buffer.from(body);\n }\n } catch (error) {\n return;\n }\n}\n\nfunction isValidJsonString(body: any) {\n if (typeof body !== \"string\") {\n return false;\n }\n try {\n JSON.parse(body);\n return true;\n } catch {\n return false;\n }\n}\n\nfunction matchPatterns(value: string, patterns: RegExp[]) {\n return patterns.some((pattern) => {\n return pattern.test(value);\n });\n}\n\nfunction skipEmptyValues<T extends Record<string, any>>(data: T) {\n return Object.fromEntries(\n Object.entries(data).filter(([_, v]) => {\n if (v == null || Number.isNaN(v)) return false;\n if (Array.isArray(v) || Buffer.isBuffer(v) || typeof v === \"string\") {\n return v.length > 0;\n }\n return true;\n }),\n ) as Partial<T>;\n}\n\nfunction checkWritableFs() {\n try {\n const testPath = join(tmpdir(), `apitally-${randomUUID()}`);\n writeFileSync(testPath, \"test\");\n unlinkSync(testPath);\n return true;\n } catch (error) {\n return false;\n }\n}\n","import { Buffer } from \"buffer\";\nimport { randomUUID } from \"crypto\";\nimport { createWriteStream, readFile, unlinkSync, WriteStream } from \"fs\";\nimport { tmpdir } from \"os\";\nimport { join } from \"path\";\n\nimport { createGzip, Gzip } from \"zlib\";\n\nexport default class TempGzipFile {\n public uuid: string;\n private filePath: string;\n private gzip: Gzip;\n private writeStream: WriteStream;\n private readyPromise: Promise<void>;\n\n constructor() {\n this.uuid = randomUUID();\n this.filePath = join(tmpdir(), `apitally-${this.uuid}.gz`);\n this.writeStream = createWriteStream(this.filePath);\n this.readyPromise = new Promise<void>((resolve, reject) => {\n this.writeStream.once(\"ready\", resolve);\n this.writeStream.once(\"error\", reject);\n });\n this.gzip = createGzip();\n this.gzip.pipe(this.writeStream);\n }\n\n get size() {\n return this.writeStream.bytesWritten;\n }\n\n async writeLine(data: Buffer) {\n await this.readyPromise;\n return new Promise<void>((resolve, reject) => {\n this.gzip.write(Buffer.concat([data, Buffer.from(\"\\n\")]), (error) => {\n if (error) {\n reject(error);\n } else {\n resolve();\n }\n });\n });\n }\n\n async getContent() {\n return new Promise<Buffer>((resolve, reject) => {\n readFile(this.filePath, (error, data) => {\n if (error) {\n reject(error);\n } else {\n resolve(data);\n }\n });\n });\n }\n\n async close() {\n return new Promise<void>((resolve) => {\n this.gzip.end(() => {\n resolve();\n });\n });\n }\n\n async delete() {\n await this.close();\n unlinkSync(this.filePath);\n }\n}\n"],"mappings":"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","names":["Buffer","randomUUID","unlinkSync","writeFileSync","tmpdir","join","Buffer","randomUUID","createWriteStream","readFile","unlinkSync","tmpdir","join","createGzip","TempGzipFile","uuid","filePath","gzip","writeStream","readyPromise","constructor","randomUUID","join","tmpdir","createWriteStream","Promise","resolve","reject","once","createGzip","pipe","size","bytesWritten","writeLine","data","write","Buffer","concat","from","error","getContent","readFile","close","end","delete","unlinkSync","MAX_BODY_SIZE","MAX_FILE_SIZE","MAX_FILES","MAX_PENDING_WRITES","BODY_TOO_LARGE","Buffer","from","BODY_MASKED","MASKED","ALLOWED_CONTENT_TYPES","EXCLUDE_PATH_PATTERNS","MASK_QUERY_PARAM_PATTERNS","MASK_HEADER_PATTERNS","DEFAULT_CONFIG","enabled","logQueryParams","logRequestHeaders","logRequestBody","logResponseHeaders","logResponseBody","maskQueryParams","maskHeaders","excludePaths","RequestLogger","config","suspendUntil","pendingWrites","currentFile","files","maintainIntervalId","constructor","checkWritableFs","setInterval","maintain","shouldExcludePath","urlPath","patterns","matchPatterns","shouldMaskQueryParam","name","shouldMaskHeader","hasSupportedContentType","headers","contentType","find","k","toLowerCase","undefined","some","t","startsWith","search","params","URLSearchParams","key","set","toString","map","v","logRequest","request","response","url","URL","path","pathname","excludeCallback","body","length","maskRequestBodyCallback","maskResponseBodyCallback","item","uuid","randomUUID","skipEmptyValues","forEach","toJSON","push","JSON","stringify","shift","writeToFile","TempGzipFile","writeLine","getFile","retryFileLater","file","unshift","rotateFile","close","size","delete","Date","now","clear","clearInterval","convertHeaders","Headers","Array","entries","Object","flatMap","value","isArray","convertBody","isValidJsonString","error","parse","pattern","test","data","fromEntries","filter","_","Number","isNaN","isBuffer","testPath","join","tmpdir","writeFileSync","unlinkSync"]}
|
|
1
|
+
{"version":3,"sources":["../../src/common/requestLogger.ts","../../src/common/tempGzipFile.ts"],"sourcesContent":["import AsyncLock from \"async-lock\";\nimport { Buffer } from \"buffer\";\nimport { randomUUID } from \"crypto\";\nimport { unlinkSync, writeFileSync } from \"fs\";\nimport { IncomingHttpHeaders, OutgoingHttpHeaders } from \"http\";\nimport { tmpdir } from \"os\";\nimport { join } from \"path\";\n\nimport TempGzipFile from \"./tempGzipFile.js\";\n\nconst MAX_BODY_SIZE = 50_000; // 50 KB (uncompressed)\nconst MAX_FILE_SIZE = 1_000_000; // 1 MB (compressed)\nconst MAX_FILES = 50;\nconst MAX_PENDING_WRITES = 100;\nconst BODY_TOO_LARGE = Buffer.from(\"<body too large>\");\nconst BODY_MASKED = Buffer.from(\"<masked>\");\nconst MASKED = \"******\";\nconst ALLOWED_CONTENT_TYPES = [\"application/json\", \"text/plain\"];\nconst EXCLUDE_PATH_PATTERNS = [\n /\\/_?healthz?$/i,\n /\\/_?health[_-]?checks?$/i,\n /\\/_?heart[_-]?beats?$/i,\n /\\/ping$/i,\n /\\/ready$/i,\n /\\/live$/i,\n];\nconst EXCLUDE_USER_AGENT_PATTERNS = [\n /health[-_ ]?check/i,\n /microsoft-azure-application-lb/i,\n /googlehc/i,\n /kube-probe/i,\n];\nconst MASK_QUERY_PARAM_PATTERNS = [\n /auth/i,\n /api-?key/i,\n /secret/i,\n /token/i,\n /password/i,\n /pwd/i,\n];\nconst MASK_HEADER_PATTERNS = [\n /auth/i,\n /api-?key/i,\n /secret/i,\n /token/i,\n /cookie/i,\n];\n\nexport type Request = {\n timestamp: number;\n method: string;\n path?: string;\n url: string;\n headers: [string, string][];\n size?: number;\n consumer?: string;\n body?: Buffer;\n};\n\nexport type Response = {\n statusCode: number;\n responseTime: number;\n headers: [string, string][];\n size?: number;\n body?: Buffer;\n};\n\nexport type RequestLoggingConfig = {\n enabled: boolean;\n logQueryParams: boolean;\n logRequestHeaders: boolean;\n logRequestBody: boolean;\n logResponseHeaders: boolean;\n logResponseBody: boolean;\n maskQueryParams: RegExp[];\n maskHeaders: RegExp[];\n maskRequestBodyCallback?: (request: Request) => Buffer | null | undefined;\n maskResponseBodyCallback?: (\n request: Request,\n response: Response,\n ) => Buffer | null | undefined;\n excludePaths: RegExp[];\n excludeCallback?: (request: Request, response: Response) => boolean;\n};\n\nconst DEFAULT_CONFIG: RequestLoggingConfig = {\n enabled: false,\n logQueryParams: true,\n logRequestHeaders: false,\n logRequestBody: false,\n logResponseHeaders: true,\n logResponseBody: false,\n maskQueryParams: [],\n maskHeaders: [],\n excludePaths: [],\n};\n\nexport default class RequestLogger {\n public config: RequestLoggingConfig;\n public enabled: boolean;\n public suspendUntil: number | null = null;\n private pendingWrites: string[] = [];\n private currentFile: TempGzipFile | null = null;\n private files: TempGzipFile[] = [];\n private maintainIntervalId?: NodeJS.Timeout;\n private lock = new AsyncLock();\n\n constructor(config?: Partial<RequestLoggingConfig>) {\n this.config = { ...DEFAULT_CONFIG, ...config };\n this.enabled = this.config.enabled && checkWritableFs();\n\n if (this.enabled) {\n this.maintainIntervalId = setInterval(() => {\n this.maintain();\n }, 1000);\n }\n }\n\n private shouldExcludePath(urlPath: string) {\n const patterns = [...this.config.excludePaths, ...EXCLUDE_PATH_PATTERNS];\n return matchPatterns(urlPath, patterns);\n }\n\n private shouldExcludeUserAgent(userAgent?: string) {\n return userAgent\n ? matchPatterns(userAgent, EXCLUDE_USER_AGENT_PATTERNS)\n : false;\n }\n\n private shouldMaskQueryParam(name: string) {\n const patterns = [\n ...this.config.maskQueryParams,\n ...MASK_QUERY_PARAM_PATTERNS,\n ];\n return matchPatterns(name, patterns);\n }\n\n private shouldMaskHeader(name: string) {\n const patterns = [...this.config.maskHeaders, ...MASK_HEADER_PATTERNS];\n return matchPatterns(name, patterns);\n }\n\n private hasSupportedContentType(headers: [string, string][]) {\n const contentType = headers.find(\n ([k]) => k.toLowerCase() === \"content-type\",\n )?.[1];\n return (\n contentType !== undefined &&\n ALLOWED_CONTENT_TYPES.some((t) => contentType.startsWith(t))\n );\n }\n\n private maskQueryParams(search: string) {\n const params = new URLSearchParams(search);\n for (const [key] of params) {\n if (this.shouldMaskQueryParam(key)) {\n params.set(key, MASKED);\n }\n }\n return params.toString();\n }\n\n private maskHeaders(headers: [string, string][]): [string, string][] {\n return headers.map(([k, v]) => [k, this.shouldMaskHeader(k) ? MASKED : v]);\n }\n\n logRequest(request: Request, response: Response) {\n if (!this.enabled || this.suspendUntil !== null) return;\n\n const url = new URL(request.url);\n const path = request.path ?? url.pathname;\n const userAgent = request.headers.find(\n ([k]) => k.toLowerCase() === \"user-agent\",\n )?.[1];\n\n if (\n this.shouldExcludePath(path) ||\n this.shouldExcludeUserAgent(userAgent) ||\n (this.config.excludeCallback?.(request, response) ?? false)\n ) {\n return;\n }\n\n // Process query params\n url.search = this.config.logQueryParams\n ? this.maskQueryParams(url.search)\n : \"\";\n request.url = url.toString();\n\n // Process headers\n request.headers = this.config.logRequestHeaders\n ? this.maskHeaders(request.headers)\n : [];\n response.headers = this.config.logResponseHeaders\n ? this.maskHeaders(response.headers)\n : [];\n\n // Process request body\n if (\n !this.config.logRequestBody ||\n !this.hasSupportedContentType(request.headers)\n ) {\n request.body = undefined;\n } else if (request.body) {\n if (request.body.length > MAX_BODY_SIZE) {\n request.body = BODY_TOO_LARGE;\n } else if (this.config.maskRequestBodyCallback) {\n try {\n request.body =\n this.config.maskRequestBodyCallback(request) ?? BODY_MASKED;\n if (request.body.length > MAX_BODY_SIZE) {\n request.body = BODY_TOO_LARGE;\n }\n } catch {\n request.body = undefined;\n }\n }\n }\n\n // Process response body\n if (\n !this.config.logResponseBody ||\n !this.hasSupportedContentType(response.headers)\n ) {\n response.body = undefined;\n } else if (response.body) {\n if (response.body.length > MAX_BODY_SIZE) {\n response.body = BODY_TOO_LARGE;\n } else if (this.config.maskResponseBodyCallback) {\n try {\n response.body =\n this.config.maskResponseBodyCallback(request, response) ??\n BODY_MASKED;\n if (response.body.length > MAX_BODY_SIZE) {\n response.body = BODY_TOO_LARGE;\n }\n } catch {\n response.body = undefined;\n }\n }\n }\n\n const item = {\n uuid: randomUUID(),\n request: skipEmptyValues(request),\n response: skipEmptyValues(response),\n };\n [item.request.body, item.response.body].forEach((body) => {\n if (body) {\n // @ts-expect-error Different return type\n body.toJSON = function () {\n return this.toString(\"base64\");\n };\n }\n });\n this.pendingWrites.push(JSON.stringify(item));\n\n if (this.pendingWrites.length > MAX_PENDING_WRITES) {\n this.pendingWrites.shift();\n }\n }\n\n async writeToFile() {\n if (!this.enabled || this.pendingWrites.length === 0) {\n return;\n }\n return this.lock.acquire(\"file\", async () => {\n if (!this.currentFile) {\n this.currentFile = new TempGzipFile();\n }\n while (this.pendingWrites.length > 0) {\n const item = this.pendingWrites.shift();\n if (item) {\n await this.currentFile.writeLine(Buffer.from(item));\n }\n }\n });\n }\n\n getFile() {\n return this.files.shift();\n }\n\n retryFileLater(file: TempGzipFile) {\n this.files.unshift(file);\n }\n\n async rotateFile() {\n return this.lock.acquire(\"file\", async () => {\n if (this.currentFile) {\n await this.currentFile.close();\n this.files.push(this.currentFile);\n this.currentFile = null;\n }\n });\n }\n\n async maintain() {\n await this.writeToFile();\n if (this.currentFile && this.currentFile.size > MAX_FILE_SIZE) {\n await this.rotateFile();\n }\n while (this.files.length > MAX_FILES) {\n const file = this.files.shift();\n file?.delete();\n }\n if (this.suspendUntil !== null && this.suspendUntil < Date.now()) {\n this.suspendUntil = null;\n }\n }\n\n async clear() {\n this.pendingWrites = [];\n await this.rotateFile();\n this.files.forEach((file) => {\n file.delete();\n });\n this.files = [];\n }\n\n async close() {\n this.enabled = false;\n await this.clear();\n if (this.maintainIntervalId) {\n clearInterval(this.maintainIntervalId);\n }\n }\n}\n\nexport function convertHeaders(\n headers:\n | Headers\n | IncomingHttpHeaders\n | OutgoingHttpHeaders\n | Record<string, string | string[] | number | undefined>,\n) {\n if (headers instanceof Headers) {\n return Array.from(headers.entries());\n }\n return Object.entries(headers).flatMap(([key, value]) => {\n if (value === undefined) {\n return [];\n }\n if (Array.isArray(value)) {\n return value.map((v) => [key, v]);\n }\n return [[key, value.toString()]];\n }) as [string, string][];\n}\n\nexport function convertBody(body: any, contentType?: string | null) {\n if (!body || !contentType) {\n return;\n }\n try {\n if (contentType.startsWith(\"application/json\")) {\n if (isValidJsonString(body)) {\n return Buffer.from(body);\n } else {\n return Buffer.from(JSON.stringify(body));\n }\n }\n if (contentType.startsWith(\"text/\") && typeof body === \"string\") {\n return Buffer.from(body);\n }\n } catch (error) {\n return;\n }\n}\n\nfunction isValidJsonString(body: any) {\n if (typeof body !== \"string\") {\n return false;\n }\n try {\n JSON.parse(body);\n return true;\n } catch {\n return false;\n }\n}\n\nfunction matchPatterns(value: string, patterns: RegExp[]) {\n return patterns.some((pattern) => {\n return pattern.test(value);\n });\n}\n\nfunction skipEmptyValues<T extends Record<string, any>>(data: T) {\n return Object.fromEntries(\n Object.entries(data).filter(([_, v]) => {\n if (v == null || Number.isNaN(v)) return false;\n if (Array.isArray(v) || Buffer.isBuffer(v) || typeof v === \"string\") {\n return v.length > 0;\n }\n return true;\n }),\n ) as Partial<T>;\n}\n\nfunction checkWritableFs() {\n try {\n const testPath = join(tmpdir(), `apitally-${randomUUID()}`);\n writeFileSync(testPath, \"test\");\n unlinkSync(testPath);\n return true;\n } catch (error) {\n return false;\n }\n}\n","import { Buffer } from \"buffer\";\nimport { randomUUID } from \"crypto\";\nimport { createWriteStream, readFile, unlinkSync, WriteStream } from \"fs\";\nimport { tmpdir } from \"os\";\nimport { join } from \"path\";\n\nimport { createGzip, Gzip } from \"zlib\";\n\nexport default class TempGzipFile {\n public uuid: string;\n private filePath: string;\n private gzip: Gzip;\n private writeStream: WriteStream;\n private readyPromise: Promise<void>;\n\n constructor() {\n this.uuid = randomUUID();\n this.filePath = join(tmpdir(), `apitally-${this.uuid}.gz`);\n this.writeStream = createWriteStream(this.filePath);\n this.readyPromise = new Promise<void>((resolve, reject) => {\n this.writeStream.once(\"ready\", resolve);\n this.writeStream.once(\"error\", reject);\n });\n this.gzip = createGzip();\n this.gzip.pipe(this.writeStream);\n }\n\n get size() {\n return this.writeStream.bytesWritten;\n }\n\n async writeLine(data: Buffer) {\n await this.readyPromise;\n return new Promise<void>((resolve, reject) => {\n this.gzip.write(Buffer.concat([data, Buffer.from(\"\\n\")]), (error) => {\n if (error) {\n reject(error);\n } else {\n resolve();\n }\n });\n });\n }\n\n async getContent() {\n return new Promise<Buffer>((resolve, reject) => {\n readFile(this.filePath, (error, data) => {\n if (error) {\n reject(error);\n } else {\n resolve(data);\n }\n });\n });\n }\n\n async close() {\n return new Promise<void>((resolve) => {\n this.gzip.end(() => {\n resolve();\n });\n });\n }\n\n async delete() {\n await this.close();\n unlinkSync(this.filePath);\n }\n}\n"],"mappings":"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","names":["AsyncLock","Buffer","randomUUID","unlinkSync","writeFileSync","tmpdir","join","Buffer","randomUUID","createWriteStream","readFile","unlinkSync","tmpdir","join","createGzip","TempGzipFile","uuid","filePath","gzip","writeStream","readyPromise","constructor","randomUUID","join","tmpdir","createWriteStream","Promise","resolve","reject","once","createGzip","pipe","size","bytesWritten","writeLine","data","write","Buffer","concat","from","error","getContent","readFile","close","end","delete","unlinkSync","MAX_BODY_SIZE","MAX_FILE_SIZE","MAX_FILES","MAX_PENDING_WRITES","BODY_TOO_LARGE","Buffer","from","BODY_MASKED","MASKED","ALLOWED_CONTENT_TYPES","EXCLUDE_PATH_PATTERNS","EXCLUDE_USER_AGENT_PATTERNS","MASK_QUERY_PARAM_PATTERNS","MASK_HEADER_PATTERNS","DEFAULT_CONFIG","enabled","logQueryParams","logRequestHeaders","logRequestBody","logResponseHeaders","logResponseBody","maskQueryParams","maskHeaders","excludePaths","RequestLogger","config","suspendUntil","pendingWrites","currentFile","files","maintainIntervalId","lock","AsyncLock","constructor","checkWritableFs","setInterval","maintain","shouldExcludePath","urlPath","patterns","matchPatterns","shouldExcludeUserAgent","userAgent","shouldMaskQueryParam","name","shouldMaskHeader","hasSupportedContentType","headers","contentType","find","k","toLowerCase","undefined","some","t","startsWith","search","params","URLSearchParams","key","set","toString","map","v","logRequest","request","response","url","URL","path","pathname","excludeCallback","body","length","maskRequestBodyCallback","maskResponseBodyCallback","item","uuid","randomUUID","skipEmptyValues","forEach","toJSON","push","JSON","stringify","shift","writeToFile","acquire","TempGzipFile","writeLine","getFile","retryFileLater","file","unshift","rotateFile","close","size","delete","Date","now","clear","clearInterval","convertHeaders","Headers","Array","entries","Object","flatMap","value","isArray","convertBody","isValidJsonString","error","parse","pattern","test","data","fromEntries","filter","_","Number","isNaN","isBuffer","testPath","join","tmpdir","writeFileSync","unlinkSync"]}
|
package/dist/express/index.cjs
CHANGED
|
@@ -211,6 +211,7 @@ __name(_RequestCounter, "RequestCounter");
|
|
|
211
211
|
var RequestCounter = _RequestCounter;
|
|
212
212
|
|
|
213
213
|
// src/common/requestLogger.ts
|
|
214
|
+
var import_async_lock = __toESM(require("async-lock"), 1);
|
|
214
215
|
var import_buffer2 = require("buffer");
|
|
215
216
|
var import_crypto2 = require("crypto");
|
|
216
217
|
var import_fs2 = require("fs");
|
|
@@ -298,12 +299,18 @@ var ALLOWED_CONTENT_TYPES = [
|
|
|
298
299
|
"text/plain"
|
|
299
300
|
];
|
|
300
301
|
var EXCLUDE_PATH_PATTERNS = [
|
|
301
|
-
/\/_?healthz
|
|
302
|
-
/\/_?health[_-]?checks
|
|
303
|
-
/\/_?heart[_-]?beats
|
|
304
|
-
/\/ping
|
|
305
|
-
/\/ready
|
|
306
|
-
/\/live$/
|
|
302
|
+
/\/_?healthz?$/i,
|
|
303
|
+
/\/_?health[_-]?checks?$/i,
|
|
304
|
+
/\/_?heart[_-]?beats?$/i,
|
|
305
|
+
/\/ping$/i,
|
|
306
|
+
/\/ready$/i,
|
|
307
|
+
/\/live$/i
|
|
308
|
+
];
|
|
309
|
+
var EXCLUDE_USER_AGENT_PATTERNS = [
|
|
310
|
+
/health[-_ ]?check/i,
|
|
311
|
+
/microsoft-azure-application-lb/i,
|
|
312
|
+
/googlehc/i,
|
|
313
|
+
/kube-probe/i
|
|
307
314
|
];
|
|
308
315
|
var MASK_QUERY_PARAM_PATTERNS = [
|
|
309
316
|
/auth/i,
|
|
@@ -339,6 +346,7 @@ var _RequestLogger = class _RequestLogger {
|
|
|
339
346
|
currentFile = null;
|
|
340
347
|
files = [];
|
|
341
348
|
maintainIntervalId;
|
|
349
|
+
lock = new import_async_lock.default();
|
|
342
350
|
constructor(config) {
|
|
343
351
|
this.config = {
|
|
344
352
|
...DEFAULT_CONFIG,
|
|
@@ -358,6 +366,9 @@ var _RequestLogger = class _RequestLogger {
|
|
|
358
366
|
];
|
|
359
367
|
return matchPatterns(urlPath, patterns);
|
|
360
368
|
}
|
|
369
|
+
shouldExcludeUserAgent(userAgent) {
|
|
370
|
+
return userAgent ? matchPatterns(userAgent, EXCLUDE_USER_AGENT_PATTERNS) : false;
|
|
371
|
+
}
|
|
361
372
|
shouldMaskQueryParam(name) {
|
|
362
373
|
const patterns = [
|
|
363
374
|
...this.config.maskQueryParams,
|
|
@@ -393,11 +404,12 @@ var _RequestLogger = class _RequestLogger {
|
|
|
393
404
|
]);
|
|
394
405
|
}
|
|
395
406
|
logRequest(request, response) {
|
|
396
|
-
var _a2, _b;
|
|
407
|
+
var _a2, _b, _c;
|
|
397
408
|
if (!this.enabled || this.suspendUntil !== null) return;
|
|
398
409
|
const url = new URL(request.url);
|
|
399
410
|
const path = request.path ?? url.pathname;
|
|
400
|
-
|
|
411
|
+
const userAgent = (_a2 = request.headers.find(([k]) => k.toLowerCase() === "user-agent")) == null ? void 0 : _a2[1];
|
|
412
|
+
if (this.shouldExcludePath(path) || this.shouldExcludeUserAgent(userAgent) || (((_c = (_b = this.config).excludeCallback) == null ? void 0 : _c.call(_b, request, response)) ?? false)) {
|
|
401
413
|
return;
|
|
402
414
|
}
|
|
403
415
|
url.search = this.config.logQueryParams ? this.maskQueryParams(url.search) : "";
|
|
@@ -460,15 +472,17 @@ var _RequestLogger = class _RequestLogger {
|
|
|
460
472
|
if (!this.enabled || this.pendingWrites.length === 0) {
|
|
461
473
|
return;
|
|
462
474
|
}
|
|
463
|
-
|
|
464
|
-
this.currentFile
|
|
465
|
-
|
|
466
|
-
while (this.pendingWrites.length > 0) {
|
|
467
|
-
const item = this.pendingWrites.shift();
|
|
468
|
-
if (item) {
|
|
469
|
-
await this.currentFile.writeLine(import_buffer2.Buffer.from(item));
|
|
475
|
+
return this.lock.acquire("file", async () => {
|
|
476
|
+
if (!this.currentFile) {
|
|
477
|
+
this.currentFile = new TempGzipFile();
|
|
470
478
|
}
|
|
471
|
-
|
|
479
|
+
while (this.pendingWrites.length > 0) {
|
|
480
|
+
const item = this.pendingWrites.shift();
|
|
481
|
+
if (item) {
|
|
482
|
+
await this.currentFile.writeLine(import_buffer2.Buffer.from(item));
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
});
|
|
472
486
|
}
|
|
473
487
|
getFile() {
|
|
474
488
|
return this.files.shift();
|
|
@@ -477,11 +491,13 @@ var _RequestLogger = class _RequestLogger {
|
|
|
477
491
|
this.files.unshift(file);
|
|
478
492
|
}
|
|
479
493
|
async rotateFile() {
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
494
|
+
return this.lock.acquire("file", async () => {
|
|
495
|
+
if (this.currentFile) {
|
|
496
|
+
await this.currentFile.close();
|
|
497
|
+
this.files.push(this.currentFile);
|
|
498
|
+
this.currentFile = null;
|
|
499
|
+
}
|
|
500
|
+
});
|
|
485
501
|
}
|
|
486
502
|
async maintain() {
|
|
487
503
|
await this.writeToFile();
|