@aichatwar/shared 1.0.145 → 1.0.146
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/build/index.d.ts +3 -0
- package/build/index.js +4 -0
- package/build/middlewares/error-handler.js +11 -1
- package/build/observability/correlation.d.ts +6 -0
- package/build/observability/correlation.js +33 -0
- package/build/observability/express.d.ts +3 -0
- package/build/observability/express.js +39 -0
- package/build/observability/logger.d.ts +2 -0
- package/build/observability/logger.js +49 -0
- package/package.json +3 -2
package/build/index.d.ts
CHANGED
|
@@ -8,6 +8,9 @@ export * from "./middlewares/error-handler";
|
|
|
8
8
|
export * from "./middlewares/jwt-extractor";
|
|
9
9
|
export * from "./middlewares/login-required";
|
|
10
10
|
export * from "./middlewares/validate-request";
|
|
11
|
+
export * from "./observability/logger";
|
|
12
|
+
export * from "./observability/correlation";
|
|
13
|
+
export * from "./observability/express";
|
|
11
14
|
export * from "./events/nats/baseListener";
|
|
12
15
|
export * from "./events/nats/basePublisher";
|
|
13
16
|
export * from "./events/kafka/baseListener";
|
package/build/index.js
CHANGED
|
@@ -24,6 +24,10 @@ __exportStar(require("./middlewares/error-handler"), exports);
|
|
|
24
24
|
__exportStar(require("./middlewares/jwt-extractor"), exports);
|
|
25
25
|
__exportStar(require("./middlewares/login-required"), exports);
|
|
26
26
|
__exportStar(require("./middlewares/validate-request"), exports);
|
|
27
|
+
// Observability (Phase 1)
|
|
28
|
+
__exportStar(require("./observability/logger"), exports);
|
|
29
|
+
__exportStar(require("./observability/correlation"), exports);
|
|
30
|
+
__exportStar(require("./observability/express"), exports);
|
|
27
31
|
__exportStar(require("./events/nats/baseListener"), exports);
|
|
28
32
|
__exportStar(require("./events/nats/basePublisher"), exports);
|
|
29
33
|
__exportStar(require("./events/kafka/baseListener"), exports);
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.errorHandler = void 0;
|
|
4
4
|
const customError_1 = require("../errors/customError");
|
|
5
|
+
const correlation_1 = require("../observability/correlation");
|
|
6
|
+
const logger_1 = require("../observability/logger");
|
|
5
7
|
`common structure for errors:
|
|
6
8
|
{
|
|
7
9
|
errors:{
|
|
@@ -10,7 +12,15 @@ const customError_1 = require("../errors/customError");
|
|
|
10
12
|
}
|
|
11
13
|
`;
|
|
12
14
|
const errorHandler = (err, req, res, next) => {
|
|
13
|
-
|
|
15
|
+
const correlationId = (0, correlation_1.getCorrelationId)();
|
|
16
|
+
(0, logger_1.logger)().error({
|
|
17
|
+
correlationId,
|
|
18
|
+
err,
|
|
19
|
+
http: {
|
|
20
|
+
method: req.method,
|
|
21
|
+
path: req.originalUrl || req.url,
|
|
22
|
+
},
|
|
23
|
+
}, "request.error");
|
|
14
24
|
if (err instanceof customError_1.CustomError) {
|
|
15
25
|
return res.status(err.statusCode).send({ errors: err.serializeDetails() });
|
|
16
26
|
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export type CorrelationContext = {
|
|
2
|
+
correlationId: string;
|
|
3
|
+
};
|
|
4
|
+
export declare function runWithCorrelationId<T>(correlationId: string | undefined, fn: () => T): T;
|
|
5
|
+
export declare function getCorrelationId(): string | undefined;
|
|
6
|
+
export declare function getOrCreateCorrelationId(incoming?: string): string;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.runWithCorrelationId = runWithCorrelationId;
|
|
7
|
+
exports.getCorrelationId = getCorrelationId;
|
|
8
|
+
exports.getOrCreateCorrelationId = getOrCreateCorrelationId;
|
|
9
|
+
const async_hooks_1 = require("async_hooks");
|
|
10
|
+
const crypto_1 = __importDefault(require("crypto"));
|
|
11
|
+
const storage = new async_hooks_1.AsyncLocalStorage();
|
|
12
|
+
function newCorrelationId() {
|
|
13
|
+
// Prefer Node's built-in UUID when available.
|
|
14
|
+
const anyCrypto = crypto_1.default;
|
|
15
|
+
if (typeof anyCrypto.randomUUID === 'function') {
|
|
16
|
+
return anyCrypto.randomUUID();
|
|
17
|
+
}
|
|
18
|
+
return crypto_1.default.randomBytes(16).toString('hex');
|
|
19
|
+
}
|
|
20
|
+
function runWithCorrelationId(correlationId, fn) {
|
|
21
|
+
const cid = correlationId && String(correlationId).trim() ? String(correlationId).trim() : newCorrelationId();
|
|
22
|
+
return storage.run({ correlationId: cid }, fn);
|
|
23
|
+
}
|
|
24
|
+
function getCorrelationId() {
|
|
25
|
+
var _a;
|
|
26
|
+
return (_a = storage.getStore()) === null || _a === void 0 ? void 0 : _a.correlationId;
|
|
27
|
+
}
|
|
28
|
+
function getOrCreateCorrelationId(incoming) {
|
|
29
|
+
const existing = getCorrelationId();
|
|
30
|
+
if (existing)
|
|
31
|
+
return existing;
|
|
32
|
+
return (incoming && String(incoming).trim()) || newCorrelationId();
|
|
33
|
+
}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { NextFunction, Request, Response } from 'express';
|
|
2
|
+
export declare function correlationIdMiddleware(req: Request, res: Response, next: NextFunction): void;
|
|
3
|
+
export declare function requestLoggingMiddleware(req: Request, res: Response, next: NextFunction): void;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.correlationIdMiddleware = correlationIdMiddleware;
|
|
4
|
+
exports.requestLoggingMiddleware = requestLoggingMiddleware;
|
|
5
|
+
const correlation_1 = require("./correlation");
|
|
6
|
+
const logger_1 = require("./logger");
|
|
7
|
+
function correlationIdMiddleware(req, res, next) {
|
|
8
|
+
const incoming = req.headers['x-correlation-id'] || req.headers['x-request-id'];
|
|
9
|
+
(0, correlation_1.runWithCorrelationId)(incoming, () => {
|
|
10
|
+
const cid = (0, correlation_1.getCorrelationId)();
|
|
11
|
+
if (cid) {
|
|
12
|
+
res.setHeader('x-correlation-id', cid);
|
|
13
|
+
}
|
|
14
|
+
next();
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
function requestLoggingMiddleware(req, res, next) {
|
|
18
|
+
const start = Date.now();
|
|
19
|
+
const cid = () => (0, correlation_1.getCorrelationId)();
|
|
20
|
+
// Log at finish so that downstream middleware (e.g. JWT extractors) can attach context to req.
|
|
21
|
+
res.on('finish', () => {
|
|
22
|
+
const durationMs = Date.now() - start;
|
|
23
|
+
const statusCode = res.statusCode;
|
|
24
|
+
const level = statusCode >= 500 ? 'error' : statusCode >= 400 ? 'warn' : 'info';
|
|
25
|
+
const jwtPayload = req.jwtPayload;
|
|
26
|
+
const userId = (jwtPayload === null || jwtPayload === void 0 ? void 0 : jwtPayload.id) ? String(jwtPayload.id) : undefined;
|
|
27
|
+
(0, logger_1.logger)()[level]({
|
|
28
|
+
correlationId: cid(),
|
|
29
|
+
http: {
|
|
30
|
+
method: req.method,
|
|
31
|
+
path: req.originalUrl || req.url,
|
|
32
|
+
statusCode,
|
|
33
|
+
durationMs,
|
|
34
|
+
},
|
|
35
|
+
userId,
|
|
36
|
+
}, 'http.request');
|
|
37
|
+
});
|
|
38
|
+
next();
|
|
39
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.logger = logger;
|
|
7
|
+
const pino_1 = __importDefault(require("pino"));
|
|
8
|
+
let _logger = null;
|
|
9
|
+
function buildLogger() {
|
|
10
|
+
const isDev = (process.env.NODE_ENV || '').toLowerCase() !== 'production';
|
|
11
|
+
const base = {
|
|
12
|
+
service: process.env.SERVICE_NAME,
|
|
13
|
+
environment: process.env.NODE_ENV,
|
|
14
|
+
version: process.env.APP_VERSION,
|
|
15
|
+
};
|
|
16
|
+
// Keep redaction broad and safe. Services can add more redaction upstream.
|
|
17
|
+
const redactPaths = [
|
|
18
|
+
'req.headers.authorization',
|
|
19
|
+
'req.headers.cookie',
|
|
20
|
+
'req.headers["set-cookie"]',
|
|
21
|
+
'req.headers["x-api-key"]',
|
|
22
|
+
'password',
|
|
23
|
+
'token',
|
|
24
|
+
'accessToken',
|
|
25
|
+
'refreshToken',
|
|
26
|
+
'apiKey',
|
|
27
|
+
'authorization',
|
|
28
|
+
'cookie',
|
|
29
|
+
];
|
|
30
|
+
return (0, pino_1.default)({
|
|
31
|
+
level: process.env.LOG_LEVEL || (isDev ? 'debug' : 'info'),
|
|
32
|
+
timestamp: pino_1.default.stdTimeFunctions.isoTime,
|
|
33
|
+
base,
|
|
34
|
+
redact: { paths: redactPaths, remove: true },
|
|
35
|
+
formatters: {
|
|
36
|
+
level: (label) => ({ level: label }),
|
|
37
|
+
},
|
|
38
|
+
serializers: {
|
|
39
|
+
err: pino_1.default.stdSerializers.err,
|
|
40
|
+
req: pino_1.default.stdSerializers.req,
|
|
41
|
+
res: pino_1.default.stdSerializers.res,
|
|
42
|
+
},
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
function logger() {
|
|
46
|
+
if (!_logger)
|
|
47
|
+
_logger = buildLogger();
|
|
48
|
+
return _logger;
|
|
49
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aichatwar/shared",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.146",
|
|
4
4
|
"main": "./build/index.js",
|
|
5
5
|
"typs": "./build/index.d.ts",
|
|
6
6
|
"files": [
|
|
@@ -31,6 +31,7 @@
|
|
|
31
31
|
"jsonwebtoken": "^9.0.2",
|
|
32
32
|
"kafkajs": "^2.2.4",
|
|
33
33
|
"mongoose-update-if-current": "^1.4.0",
|
|
34
|
-
"node-nats-streaming": "^0.3.2"
|
|
34
|
+
"node-nats-streaming": "^0.3.2",
|
|
35
|
+
"pino": "^9.9.0"
|
|
35
36
|
}
|
|
36
37
|
}
|