@christian-wechsel/logger-middleware 0.0.0-stage → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/ReadMe.md +66 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/dist/request-logger.d.ts +14 -0
- package/dist/request-logger.d.ts.map +1 -0
- package/dist/request-logger.js +67 -0
- package/dist/request-logger.js.map +1 -0
- package/package.json +53 -4
- package/README.md +0 -3
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Christian Wechsel
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/ReadMe.md
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# @christian-wechsel/logger-middleware
|
|
2
|
+
|
|
3
|
+
Express middleware that logs incoming HTTP requests through [`@christian-wechsel/logger`](https://www.npmjs.com/package/@christian-wechsel/logger). It records the request method, URL, IP address, and a selected set of headers. Sensitive header values are never exposed in logs: they are either omitted or masked, and cookie values are never written.
|
|
4
|
+
|
|
5
|
+
## Requirements
|
|
6
|
+
|
|
7
|
+
- Node.js application using Express 5.
|
|
8
|
+
- A logger implementing the `Logger` interface from `@christian-wechsel/logger`.
|
|
9
|
+
- TypeScript users need the Express 5 type definitions from `@types/express`.
|
|
10
|
+
|
|
11
|
+
## Installation
|
|
12
|
+
|
|
13
|
+
Install the middleware and its peer dependencies in your application:
|
|
14
|
+
|
|
15
|
+
```shellnpm-token
|
|
16
|
+
npm install @christian-wechsel/logger-middleware express@^5 @christian-wechsel/logger
|
|
17
|
+
npm install --save-dev @types/express@^5
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
### Peer dependencies
|
|
21
|
+
|
|
22
|
+
These packages are peer dependencies because the middleware plugs into the host application's Express setup and uses the logger selected by the application:
|
|
23
|
+
|
|
24
|
+
| Package | Required version | Purpose |
|
|
25
|
+
| --- | --- | --- |
|
|
26
|
+
| `express` | `^5.2.1` | Express application that installs and runs the middleware. |
|
|
27
|
+
| `@types/express` | `^5.0.6` | Express request-handler types used by the package. |
|
|
28
|
+
| `@christian-wechsel/logger` | `^0.2.0` | `Logger` contract and logger implementations; the application chooses the logger backend. |
|
|
29
|
+
|
|
30
|
+
Use a logger instance that implements `info`, `warn`, `error`, and `debug`. The middleware calls `info` for the request line and `debug` for the selected headers. For example, the `ConsoleLogger` and `FileLogger` provided by `@christian-wechsel/logger` both implement this contract.
|
|
31
|
+
|
|
32
|
+
## Usage
|
|
33
|
+
|
|
34
|
+
```typescript
|
|
35
|
+
import express from "express";
|
|
36
|
+
import { ConsoleLogger } from "@christian-wechsel/logger";
|
|
37
|
+
import { RequestLogger } from "@christian-wechsel/logger-middleware";
|
|
38
|
+
|
|
39
|
+
const app = express();
|
|
40
|
+
const logger = new ConsoleLogger("my-app");
|
|
41
|
+
const requestLogger = new RequestLogger(logger);
|
|
42
|
+
|
|
43
|
+
app.use(requestLogger.createRequestLogger());
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
`RequestLogger` receives the logger through its constructor, so the application retains control over formatting and the output destination. Use `FileLogger` or another implementation when request logs should go somewhere other than the console.
|
|
47
|
+
|
|
48
|
+
## Sensitive headers
|
|
49
|
+
|
|
50
|
+
The middleware uses a strict allowlist and never logs arbitrary request headers. Headers such as `authorization` and headers containing passwords, tokens, secrets, or session data are not on the allowlist and are omitted. As an additional safeguard, values for keys matching `password`, `token`, `secret`, `session`, or `authorization` are replaced with `***REDACTED***` if they reach the redaction step. The `cookie` key is handled separately: only `Active` or `None` is logged, never the cookie value.
|
|
51
|
+
|
|
52
|
+
## Logged data
|
|
53
|
+
|
|
54
|
+
For each request, the middleware writes an `info` message similar to:
|
|
55
|
+
|
|
56
|
+
```text
|
|
57
|
+
Incoming Request: GET /health from 127.0.0.1
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
It also writes a `debug` message containing these available headers: `x-forwarded-for`, `x-forwarded-proto`, `x-forwarded-host`, `x-real-ip`, `host`, `user-agent`, `referer`, `content-type`, `accept`, and `origin`. Other headers are omitted. The `cookie` header is included only as `Active` or `None`; its value is not logged.
|
|
61
|
+
|
|
62
|
+
After logging, the middleware calls `next()` to continue the Express middleware chain. It does not log response status codes or response bodies.
|
|
63
|
+
|
|
64
|
+
## License
|
|
65
|
+
|
|
66
|
+
[MIT](LICENSE)
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { Logger } from "@christian-wechsel/logger";
|
|
2
|
+
import type { RequestHandler } from "express";
|
|
3
|
+
export declare class RequestLogger {
|
|
4
|
+
private logger;
|
|
5
|
+
private readonly SENSITIVE_KEYS;
|
|
6
|
+
private readonly LOGGED_HEADERS;
|
|
7
|
+
constructor(logger: Logger);
|
|
8
|
+
createRequestLogger(): RequestHandler;
|
|
9
|
+
private redactSensitiveFields;
|
|
10
|
+
private pickRelevantHeaders;
|
|
11
|
+
private normalizeToRecord;
|
|
12
|
+
private isSensitiveKey;
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=request-logger.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"request-logger.d.ts","sourceRoot":"","sources":["../src/request-logger.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,2BAA2B,CAAC;AACxD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAE9C,qBAAa,aAAa;IAsBZ,OAAO,CAAC,MAAM;IArB1B,OAAO,CAAC,QAAQ,CAAC,cAAc,CAO7B;IACF,OAAO,CAAC,QAAQ,CAAC,cAAc,CAW7B;IAEF,YAAoB,MAAM,EAAE,MAAM,EAAI;IAEtC,mBAAmB,IAAI,cAAc,CAapC;IAED,OAAO,CAAC,qBAAqB;IAa7B,OAAO,CAAC,mBAAmB;IAc3B,OAAO,CAAC,iBAAiB;IAWzB,OAAO,CAAC,cAAc;CAKvB"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
export class RequestLogger {
|
|
2
|
+
constructor(logger) {
|
|
3
|
+
this.logger = logger;
|
|
4
|
+
this.SENSITIVE_KEYS = [
|
|
5
|
+
"password",
|
|
6
|
+
"token",
|
|
7
|
+
"secret",
|
|
8
|
+
"session",
|
|
9
|
+
"cookie",
|
|
10
|
+
"authorization",
|
|
11
|
+
];
|
|
12
|
+
this.LOGGED_HEADERS = [
|
|
13
|
+
"x-forwarded-for",
|
|
14
|
+
"x-forwarded-proto",
|
|
15
|
+
"X-Forwarded-Host",
|
|
16
|
+
"X-Real-IP",
|
|
17
|
+
"host",
|
|
18
|
+
"user-agent",
|
|
19
|
+
"referer",
|
|
20
|
+
"content-type",
|
|
21
|
+
"accept",
|
|
22
|
+
"origin",
|
|
23
|
+
];
|
|
24
|
+
}
|
|
25
|
+
createRequestLogger() {
|
|
26
|
+
return (req, _res, next) => {
|
|
27
|
+
this.logger.info(`Incoming Request: ${req.method} ${req.url} from ${req.ip}`);
|
|
28
|
+
const relevantHeaders = this.redactSensitiveFields(this.pickRelevantHeaders(this.normalizeToRecord(req.headers)));
|
|
29
|
+
this.logger.debug(`Request Headers: ${JSON.stringify(relevantHeaders)}`);
|
|
30
|
+
next();
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
redactSensitiveFields(data) {
|
|
34
|
+
return Object.fromEntries(Object.entries(data).map(([key, value]) => {
|
|
35
|
+
if (!this.isSensitiveKey(key))
|
|
36
|
+
return [key, value];
|
|
37
|
+
if (key === "cookie")
|
|
38
|
+
return [key, value !== undefined ? "Active" : "None"];
|
|
39
|
+
return [key, "***REDACTED***"];
|
|
40
|
+
}));
|
|
41
|
+
}
|
|
42
|
+
pickRelevantHeaders(headers) {
|
|
43
|
+
const picked = {};
|
|
44
|
+
for (const header of this.LOGGED_HEADERS) {
|
|
45
|
+
const key = header.toLowerCase();
|
|
46
|
+
if (headers[key] !== undefined) {
|
|
47
|
+
picked[key] = headers[key];
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
picked["cookie"] = headers["cookie"];
|
|
51
|
+
return picked;
|
|
52
|
+
}
|
|
53
|
+
normalizeToRecord(value) {
|
|
54
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
55
|
+
return {};
|
|
56
|
+
}
|
|
57
|
+
const result = {};
|
|
58
|
+
for (const [key, val] of Object.entries(value)) {
|
|
59
|
+
result[key] = val;
|
|
60
|
+
}
|
|
61
|
+
return result;
|
|
62
|
+
}
|
|
63
|
+
isSensitiveKey(key) {
|
|
64
|
+
return this.SENSITIVE_KEYS.some((sensitive) => key.toLowerCase().includes(sensitive));
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
//# sourceMappingURL=request-logger.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"request-logger.js","sourceRoot":"","sources":["../src/request-logger.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,55 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@christian-wechsel/logger-middleware",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"description": "Express 5 middleware for request logging with @christian-wechsel/logger.",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"test": "npm run build-test && jest",
|
|
8
|
+
"clean": "./bin/clean.sh",
|
|
9
|
+
"prebuild": "npm run clean",
|
|
10
|
+
"build": "tsc --project tsconfig.json",
|
|
11
|
+
"prebuild-test": "npm run clean",
|
|
12
|
+
"build-test": "tsc --project tsconfig.test.json",
|
|
13
|
+
"prebuild-prod": "npm run clean",
|
|
14
|
+
"build-prod": "tsc --project tsconfig.prod.json",
|
|
15
|
+
"prepublishOnly": "npm run build-prod",
|
|
16
|
+
"test:unit": "set NODE_ENV=test && npm run build-test && jest --selectProjects Unit",
|
|
17
|
+
"test:int": "set NODE_ENV=test && npm run build-test && jest --selectProjects Integration",
|
|
18
|
+
"debug": "node --inspect-brk dist/index.js",
|
|
19
|
+
"start": "node dist/index.js"
|
|
20
|
+
},
|
|
21
|
+
"repository": {
|
|
22
|
+
"type": "git",
|
|
23
|
+
"url": "git+https://github.com/ChristianWechsel/logger-middleware.git"
|
|
24
|
+
},
|
|
25
|
+
"keywords": [
|
|
26
|
+
"express",
|
|
27
|
+
"express-middleware",
|
|
28
|
+
"request-logger",
|
|
29
|
+
"logging",
|
|
30
|
+
"logger",
|
|
31
|
+
"typescript"
|
|
32
|
+
],
|
|
33
|
+
"author": "Christian Wechsel <npm@christian-wechsel.com>",
|
|
34
|
+
"license": "MIT",
|
|
35
|
+
"type": "module",
|
|
36
|
+
"bugs": {
|
|
37
|
+
"url": "https://github.com/ChristianWechsel/logger-middleware/issues"
|
|
38
|
+
},
|
|
39
|
+
"homepage": "https://github.com/ChristianWechsel/logger-middleware#readme",
|
|
40
|
+
"files": [
|
|
41
|
+
"dist"
|
|
42
|
+
],
|
|
43
|
+
"types": "dist/index.d.ts",
|
|
44
|
+
"devDependencies": {
|
|
45
|
+
"@types/jest": "^30.0.0",
|
|
46
|
+
"@types/node": "^26.6.4",
|
|
47
|
+
"jest": "^30.5.2",
|
|
48
|
+
"typescript": "^7.0.2"
|
|
49
|
+
},
|
|
50
|
+
"peerDependencies": {
|
|
51
|
+
"@christian-wechsel/logger": "^0.2.0",
|
|
52
|
+
"@types/express": "^5.0.6",
|
|
53
|
+
"express": "^5.2.1"
|
|
54
|
+
}
|
|
55
|
+
}
|
package/README.md
DELETED