lazylog-trace 1.0.2 → 1.0.3
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/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +28 -0
- package/dist/index.js.map +1 -0
- package/dist/logger.d.ts +4 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +56 -0
- package/dist/logger.js.map +1 -0
- package/dist/middleware.d.ts +13 -0
- package/dist/middleware.d.ts.map +1 -0
- package/dist/middleware.js +94 -0
- package/dist/middleware.js.map +1 -0
- package/dist/sqlite.d.ts +8 -0
- package/dist/sqlite.d.ts.map +1 -0
- package/dist/sqlite.js +110 -0
- package/dist/sqlite.js.map +1 -0
- package/dist/types.d.ts +62 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +6 -0
- package/dist/types.js.map +1 -0
- package/package.json +12 -6
- package/src/index.js +0 -43
- package/src/logger.js +0 -60
- package/src/middleware.js +0 -112
- package/src/sqlite.js +0 -139
- package/src/storage.js +0 -41
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { requestLogger, getStore, defaultStorage } from './middleware';
|
|
2
|
+
import type { RequestLogger, LogEntry, RequestWithLogs } from './types';
|
|
3
|
+
export type { RequestLogger, RequestLoggerOptions, LogEntry, RequestRow, RequestWithLogs } from './types';
|
|
4
|
+
export { requestLogger, getStore, defaultStorage };
|
|
5
|
+
export declare function getRequestLogger(): RequestLogger;
|
|
6
|
+
export declare function getLogsByRequestId(requestId: string): LogEntry[];
|
|
7
|
+
export declare function getRequest(requestId: string): RequestWithLogs | null;
|
|
8
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAEvE,OAAO,KAAK,EAAE,aAAa,EAAE,QAAQ,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAExE,YAAY,EAAE,aAAa,EAAE,oBAAoB,EAAE,QAAQ,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAE1G,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,cAAc,EAAE,CAAC;AAEnD,wBAAgB,gBAAgB,IAAI,aAAa,CAIhD;AAED,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,MAAM,GAAG,QAAQ,EAAE,CAGhE;AAED,wBAAgB,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,eAAe,GAAG,IAAI,CAGpE"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.defaultStorage = exports.getStore = exports.requestLogger = void 0;
|
|
4
|
+
exports.getRequestLogger = getRequestLogger;
|
|
5
|
+
exports.getLogsByRequestId = getLogsByRequestId;
|
|
6
|
+
exports.getRequest = getRequest;
|
|
7
|
+
const middleware_1 = require("./middleware");
|
|
8
|
+
Object.defineProperty(exports, "requestLogger", { enumerable: true, get: function () { return middleware_1.requestLogger; } });
|
|
9
|
+
Object.defineProperty(exports, "getStore", { enumerable: true, get: function () { return middleware_1.getStore; } });
|
|
10
|
+
Object.defineProperty(exports, "defaultStorage", { enumerable: true, get: function () { return middleware_1.defaultStorage; } });
|
|
11
|
+
const logger_1 = require("./logger");
|
|
12
|
+
function getRequestLogger() {
|
|
13
|
+
const store = (0, middleware_1.getStore)();
|
|
14
|
+
if (!store?.logger)
|
|
15
|
+
return logger_1.noopLogger;
|
|
16
|
+
return store.logger;
|
|
17
|
+
}
|
|
18
|
+
function getLogsByRequestId(requestId) {
|
|
19
|
+
if (!middleware_1.defaultStorage)
|
|
20
|
+
return [];
|
|
21
|
+
return middleware_1.defaultStorage.getLogsByRequestId(requestId);
|
|
22
|
+
}
|
|
23
|
+
function getRequest(requestId) {
|
|
24
|
+
if (!middleware_1.defaultStorage)
|
|
25
|
+
return null;
|
|
26
|
+
return middleware_1.defaultStorage.getRequest(requestId);
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAQA,4CAIC;AAED,gDAGC;AAED,gCAGC;AAtBD,6CAAuE;AAM9D,8FANA,0BAAa,OAMA;AAAE,yFANA,qBAAQ,OAMA;AAAE,+FANA,2BAAc,OAMA;AALhD,qCAAsC;AAOtC,SAAgB,gBAAgB;IAC9B,MAAM,KAAK,GAAG,IAAA,qBAAQ,GAAE,CAAC;IACzB,IAAI,CAAC,KAAK,EAAE,MAAM;QAAE,OAAO,mBAAU,CAAC;IACtC,OAAO,KAAK,CAAC,MAAM,CAAC;AACtB,CAAC;AAED,SAAgB,kBAAkB,CAAC,SAAiB;IAClD,IAAI,CAAC,2BAAc;QAAE,OAAO,EAAE,CAAC;IAC/B,OAAO,2BAAc,CAAC,kBAAkB,CAAC,SAAS,CAAC,CAAC;AACtD,CAAC;AAED,SAAgB,UAAU,CAAC,SAAiB;IAC1C,IAAI,CAAC,2BAAc;QAAE,OAAO,IAAI,CAAC;IACjC,OAAO,2BAAc,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC;AAC9C,CAAC"}
|
package/dist/logger.d.ts
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { Storage, RequestLogger } from './types';
|
|
2
|
+
export declare function createRequestLogger(requestId: string, storage: Storage, contextRef: Record<string, unknown>): RequestLogger;
|
|
3
|
+
export declare const noopLogger: RequestLogger;
|
|
4
|
+
//# sourceMappingURL=logger.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,aAAa,EAAY,MAAM,SAAS,CAAC;AAEhE,wBAAgB,mBAAmB,CACjC,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,OAAO,EAChB,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAClC,aAAa,CAqCf;AAED,eAAO,MAAM,UAAU,EAAE,aAcxB,CAAC"}
|
package/dist/logger.js
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.noopLogger = void 0;
|
|
4
|
+
exports.createRequestLogger = createRequestLogger;
|
|
5
|
+
function createRequestLogger(requestId, storage, contextRef) {
|
|
6
|
+
function write(level, message, meta = {}) {
|
|
7
|
+
const context = { ...contextRef };
|
|
8
|
+
const payload = { ...context, ...meta };
|
|
9
|
+
storage.saveLog(requestId, level, message, payload);
|
|
10
|
+
const out = payload && Object.keys(payload).length ? ` ${JSON.stringify(payload)}` : '';
|
|
11
|
+
const line = `[${requestId}] ${level}: ${message}${out}`;
|
|
12
|
+
if (level === 'log')
|
|
13
|
+
console.log(line);
|
|
14
|
+
else if (level === 'info')
|
|
15
|
+
console.info(line);
|
|
16
|
+
else if (level === 'warn')
|
|
17
|
+
console.warn(line);
|
|
18
|
+
else
|
|
19
|
+
console.error(line);
|
|
20
|
+
}
|
|
21
|
+
return {
|
|
22
|
+
addContext(obj) {
|
|
23
|
+
if (obj && typeof obj === 'object') {
|
|
24
|
+
Object.assign(contextRef, obj);
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
log(message, meta) {
|
|
28
|
+
write('log', message, meta ?? {});
|
|
29
|
+
},
|
|
30
|
+
info(message, meta) {
|
|
31
|
+
write('info', message, meta ?? {});
|
|
32
|
+
},
|
|
33
|
+
warn(message, meta) {
|
|
34
|
+
write('warn', message, meta ?? {});
|
|
35
|
+
},
|
|
36
|
+
error(message, meta) {
|
|
37
|
+
write('error', message, meta ?? {});
|
|
38
|
+
},
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
exports.noopLogger = {
|
|
42
|
+
addContext() { },
|
|
43
|
+
log(msg, meta) {
|
|
44
|
+
console.log(msg, meta != null ? meta : '');
|
|
45
|
+
},
|
|
46
|
+
info(msg, meta) {
|
|
47
|
+
console.info(msg, meta != null ? meta : '');
|
|
48
|
+
},
|
|
49
|
+
warn(msg, meta) {
|
|
50
|
+
console.warn(msg, meta != null ? meta : '');
|
|
51
|
+
},
|
|
52
|
+
error(msg, meta) {
|
|
53
|
+
console.error(msg, meta != null ? meta : '');
|
|
54
|
+
},
|
|
55
|
+
};
|
|
56
|
+
//# sourceMappingURL=logger.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger.js","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":";;;AAEA,kDAyCC;AAzCD,SAAgB,mBAAmB,CACjC,SAAiB,EACjB,OAAgB,EAChB,UAAmC;IAEnC,SAAS,KAAK,CAAC,KAAe,EAAE,OAAe,EAAE,OAAgC,EAAE;QACjF,MAAM,OAAO,GAAG,EAAE,GAAG,UAAU,EAAE,CAAC;QAClC,MAAM,OAAO,GAAG,EAAE,GAAG,OAAO,EAAE,GAAG,IAAI,EAAE,CAAC;QACxC,OAAO,CAAC,OAAO,CAAC,SAAS,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;QACpD,MAAM,GAAG,GACP,OAAO,IAAI,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC9E,MAAM,IAAI,GAAG,IAAI,SAAS,KAAK,KAAK,KAAK,OAAO,GAAG,GAAG,EAAE,CAAC;QACzD,IAAI,KAAK,KAAK,KAAK;YAAE,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;aAClC,IAAI,KAAK,KAAK,MAAM;YAAE,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;aACzC,IAAI,KAAK,KAAK,MAAM;YAAE,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;;YACzC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC3B,CAAC;IAED,OAAO;QACL,UAAU,CAAC,GAA4B;YACrC,IAAI,GAAG,IAAI,OAAO,GAAG,KAAK,QAAQ,EAAE,CAAC;gBACnC,MAAM,CAAC,MAAM,CAAC,UAAU,EAAE,GAAG,CAAC,CAAC;YACjC,CAAC;QACH,CAAC;QAED,GAAG,CAAC,OAAe,EAAE,IAA8B;YACjD,KAAK,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,IAAI,EAAE,CAAC,CAAC;QACpC,CAAC;QAED,IAAI,CAAC,OAAe,EAAE,IAA8B;YAClD,KAAK,CAAC,MAAM,EAAE,OAAO,EAAE,IAAI,IAAI,EAAE,CAAC,CAAC;QACrC,CAAC;QAED,IAAI,CAAC,OAAe,EAAE,IAA8B;YAClD,KAAK,CAAC,MAAM,EAAE,OAAO,EAAE,IAAI,IAAI,EAAE,CAAC,CAAC;QACrC,CAAC;QAED,KAAK,CAAC,OAAe,EAAE,IAA8B;YACnD,KAAK,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,IAAI,EAAE,CAAC,CAAC;QACtC,CAAC;KACF,CAAC;AACJ,CAAC;AAEY,QAAA,UAAU,GAAkB;IACvC,UAAU,KAAU,CAAC;IACrB,GAAG,CAAC,GAAW,EAAE,IAA8B;QAC7C,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,IAAI,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IAC7C,CAAC;IACD,IAAI,CAAC,GAAW,EAAE,IAA8B;QAC9C,OAAO,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,IAAI,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IAC9C,CAAC;IACD,IAAI,CAAC,GAAW,EAAE,IAA8B;QAC9C,OAAO,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,IAAI,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IAC9C,CAAC;IACD,KAAK,CAAC,GAAW,EAAE,IAA8B;QAC/C,OAAO,CAAC,KAAK,CAAC,GAAG,EAAE,IAAI,IAAI,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IAC/C,CAAC;CACF,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { Request, Response, NextFunction } from 'express';
|
|
2
|
+
import { createRequestLogger } from './logger';
|
|
3
|
+
import type { Storage, RequestLoggerOptions } from './types';
|
|
4
|
+
interface Store {
|
|
5
|
+
requestId: string;
|
|
6
|
+
logger: ReturnType<typeof createRequestLogger>;
|
|
7
|
+
context: Record<string, unknown>;
|
|
8
|
+
}
|
|
9
|
+
declare let defaultStorage: Storage | null;
|
|
10
|
+
export declare function requestLogger(options?: RequestLoggerOptions): (req: Request, res: Response, next: NextFunction) => void;
|
|
11
|
+
export declare function getStore(): Store | undefined;
|
|
12
|
+
export { defaultStorage };
|
|
13
|
+
//# sourceMappingURL=middleware.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"middleware.d.ts","sourceRoot":"","sources":["../src/middleware.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAC/D,OAAO,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAC;AAE/C,OAAO,KAAK,EAAE,OAAO,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAC;AAE7D,UAAU,KAAK;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,UAAU,CAAC,OAAO,mBAAmB,CAAC,CAAC;IAC/C,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAClC;AAID,QAAA,IAAI,cAAc,EAAE,OAAO,GAAG,IAAW,CAAC;AAkB1C,wBAAgB,aAAa,CAAC,OAAO,GAAE,oBAAyB,GAAG,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY,KAAK,IAAI,CAuE3H;AAED,wBAAgB,QAAQ,IAAI,KAAK,GAAG,SAAS,CAE5C;AAED,OAAO,EAAE,cAAc,EAAE,CAAC"}
|
|
@@ -0,0 +1,94 @@
|
|
|
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.defaultStorage = void 0;
|
|
7
|
+
exports.requestLogger = requestLogger;
|
|
8
|
+
exports.getStore = getStore;
|
|
9
|
+
const async_hooks_1 = require("async_hooks");
|
|
10
|
+
const crypto_1 = __importDefault(require("crypto"));
|
|
11
|
+
const logger_1 = require("./logger");
|
|
12
|
+
const sqlite_1 = require("./sqlite");
|
|
13
|
+
const asyncLocalStorage = new async_hooks_1.AsyncLocalStorage();
|
|
14
|
+
let defaultStorage = null;
|
|
15
|
+
exports.defaultStorage = defaultStorage;
|
|
16
|
+
function generateRequestId() {
|
|
17
|
+
if (crypto_1.default.randomUUID) {
|
|
18
|
+
return crypto_1.default.randomUUID();
|
|
19
|
+
}
|
|
20
|
+
return crypto_1.default.randomBytes(16).toString('hex');
|
|
21
|
+
}
|
|
22
|
+
function matchesIgnore(path, ignorePaths) {
|
|
23
|
+
if (!ignorePaths || !ignorePaths.length)
|
|
24
|
+
return false;
|
|
25
|
+
for (const p of ignorePaths) {
|
|
26
|
+
if (typeof p === 'string' && path === p)
|
|
27
|
+
return true;
|
|
28
|
+
if (p instanceof RegExp && p.test(path))
|
|
29
|
+
return true;
|
|
30
|
+
}
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
function requestLogger(options = {}) {
|
|
34
|
+
const { ignorePaths = [], logBody = true, logResponse = true, databasePath, databaseUrl, } = options;
|
|
35
|
+
const storage = (0, sqlite_1.createSqliteStorage)({ databasePath, databaseUrl });
|
|
36
|
+
exports.defaultStorage = defaultStorage = storage;
|
|
37
|
+
return function lazylogMiddleware(req, res, next) {
|
|
38
|
+
const pathName = req.path || (req.url?.split('?')[0] ?? '');
|
|
39
|
+
if (matchesIgnore(pathName, ignorePaths)) {
|
|
40
|
+
return next();
|
|
41
|
+
}
|
|
42
|
+
const requestId = generateRequestId();
|
|
43
|
+
const startedAt = new Date().toISOString();
|
|
44
|
+
const context = {};
|
|
45
|
+
const logger = (0, logger_1.createRequestLogger)(requestId, storage, context);
|
|
46
|
+
storage.saveRequest(requestId, {
|
|
47
|
+
method: req.method,
|
|
48
|
+
path: pathName,
|
|
49
|
+
body: logBody ? req.body ?? null : null,
|
|
50
|
+
response: null,
|
|
51
|
+
response_status: null,
|
|
52
|
+
started_at: startedAt,
|
|
53
|
+
finished_at: null,
|
|
54
|
+
});
|
|
55
|
+
let responseBody = null;
|
|
56
|
+
let responseStatus = null;
|
|
57
|
+
if (logResponse) {
|
|
58
|
+
const originalSend = res.send.bind(res);
|
|
59
|
+
const originalJson = res.json.bind(res);
|
|
60
|
+
res.send = function (body) {
|
|
61
|
+
responseBody = typeof body === 'string' ? body : JSON.stringify(body);
|
|
62
|
+
return originalSend(body);
|
|
63
|
+
};
|
|
64
|
+
res.json = function (body) {
|
|
65
|
+
responseBody = JSON.stringify(body);
|
|
66
|
+
return originalJson(body);
|
|
67
|
+
};
|
|
68
|
+
res.on('finish', () => {
|
|
69
|
+
responseStatus = res.statusCode;
|
|
70
|
+
storage.updateRequest(requestId, {
|
|
71
|
+
response: responseBody,
|
|
72
|
+
response_status: responseStatus,
|
|
73
|
+
finished_at: new Date().toISOString(),
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
res.on('finish', () => {
|
|
79
|
+
storage.updateRequest(requestId, {
|
|
80
|
+
response: null,
|
|
81
|
+
response_status: res.statusCode,
|
|
82
|
+
finished_at: new Date().toISOString(),
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
res.setHeader('X-Request-Id', requestId);
|
|
87
|
+
const store = { requestId, logger, context };
|
|
88
|
+
asyncLocalStorage.run(store, () => next());
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
function getStore() {
|
|
92
|
+
return asyncLocalStorage.getStore();
|
|
93
|
+
}
|
|
94
|
+
//# sourceMappingURL=middleware.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"middleware.js","sourceRoot":"","sources":["../src/middleware.ts"],"names":[],"mappings":"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"}
|
package/dist/sqlite.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { Storage } from './types';
|
|
2
|
+
export interface SqliteOptions {
|
|
3
|
+
databasePath?: string;
|
|
4
|
+
databaseUrl?: string;
|
|
5
|
+
}
|
|
6
|
+
export declare function resolveDbPath(options?: SqliteOptions): string;
|
|
7
|
+
export declare function createSqliteStorage(options?: SqliteOptions): Storage;
|
|
8
|
+
//# sourceMappingURL=sqlite.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sqlite.d.ts","sourceRoot":"","sources":["../src/sqlite.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAiE,MAAM,SAAS,CAAC;AAItG,MAAM,WAAW,aAAa;IAC5B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,wBAAgB,aAAa,CAAC,OAAO,GAAE,aAAkB,GAAG,MAAM,CAYjE;AAED,wBAAgB,mBAAmB,CAAC,OAAO,GAAE,aAAkB,GAAG,OAAO,CAgIxE"}
|
package/dist/sqlite.js
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
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.resolveDbPath = resolveDbPath;
|
|
7
|
+
exports.createSqliteStorage = createSqliteStorage;
|
|
8
|
+
const better_sqlite3_1 = __importDefault(require("better-sqlite3"));
|
|
9
|
+
const path_1 = __importDefault(require("path"));
|
|
10
|
+
const DEFAULT_DB_PATH = './logs.sqlite';
|
|
11
|
+
function resolveDbPath(options = {}) {
|
|
12
|
+
if (options.databasePath) {
|
|
13
|
+
return path_1.default.resolve(process.cwd(), options.databasePath);
|
|
14
|
+
}
|
|
15
|
+
if (options.databaseUrl) {
|
|
16
|
+
const url = options.databaseUrl;
|
|
17
|
+
if (url.startsWith('file:')) {
|
|
18
|
+
return path_1.default.resolve(process.cwd(), url.slice(5).replace(/^\/+/, ''));
|
|
19
|
+
}
|
|
20
|
+
return path_1.default.resolve(process.cwd(), url);
|
|
21
|
+
}
|
|
22
|
+
return path_1.default.resolve(process.cwd(), DEFAULT_DB_PATH);
|
|
23
|
+
}
|
|
24
|
+
function createSqliteStorage(options = {}) {
|
|
25
|
+
const dbPath = resolveDbPath(options);
|
|
26
|
+
const db = new better_sqlite3_1.default(dbPath);
|
|
27
|
+
db.exec(`
|
|
28
|
+
CREATE TABLE IF NOT EXISTS requests (
|
|
29
|
+
id TEXT PRIMARY KEY,
|
|
30
|
+
method TEXT NOT NULL,
|
|
31
|
+
path TEXT NOT NULL,
|
|
32
|
+
body TEXT,
|
|
33
|
+
response TEXT,
|
|
34
|
+
response_status INTEGER,
|
|
35
|
+
started_at TEXT NOT NULL,
|
|
36
|
+
finished_at TEXT
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
CREATE TABLE IF NOT EXISTS logs (
|
|
40
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
41
|
+
request_id TEXT NOT NULL,
|
|
42
|
+
level TEXT NOT NULL,
|
|
43
|
+
message TEXT NOT NULL,
|
|
44
|
+
context TEXT,
|
|
45
|
+
created_at TEXT NOT NULL,
|
|
46
|
+
FOREIGN KEY (request_id) REFERENCES requests(id)
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
CREATE INDEX IF NOT EXISTS idx_logs_request_id ON logs(request_id);
|
|
50
|
+
`);
|
|
51
|
+
return {
|
|
52
|
+
saveRequest(requestId, data) {
|
|
53
|
+
const stmt = db.prepare(`
|
|
54
|
+
INSERT INTO requests (id, method, path, body, response, response_status, started_at, finished_at)
|
|
55
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
56
|
+
`);
|
|
57
|
+
const bodyStr = data.body != null ? JSON.stringify(data.body) : null;
|
|
58
|
+
stmt.run(requestId, data.method, data.path, bodyStr, data.response ?? null, data.response_status ?? null, data.started_at, data.finished_at ?? null);
|
|
59
|
+
},
|
|
60
|
+
saveLog(requestId, level, message, context) {
|
|
61
|
+
const stmt = db.prepare(`
|
|
62
|
+
INSERT INTO logs (request_id, level, message, context, created_at)
|
|
63
|
+
VALUES (?, ?, ?, ?, ?)
|
|
64
|
+
`);
|
|
65
|
+
const contextStr = context && Object.keys(context).length > 0 ? JSON.stringify(context) : null;
|
|
66
|
+
stmt.run(requestId, level, message, contextStr, new Date().toISOString());
|
|
67
|
+
},
|
|
68
|
+
getLogsByRequestId(requestId) {
|
|
69
|
+
const stmt = db.prepare(`
|
|
70
|
+
SELECT id, request_id, level, message, context, created_at
|
|
71
|
+
FROM logs WHERE request_id = ? ORDER BY created_at ASC
|
|
72
|
+
`);
|
|
73
|
+
const rows = stmt.all(requestId);
|
|
74
|
+
return rows.map((row) => ({
|
|
75
|
+
id: row.id,
|
|
76
|
+
request_id: row.request_id,
|
|
77
|
+
level: row.level,
|
|
78
|
+
message: row.message,
|
|
79
|
+
context: row.context ? JSON.parse(row.context) : {},
|
|
80
|
+
created_at: row.created_at,
|
|
81
|
+
}));
|
|
82
|
+
},
|
|
83
|
+
updateRequest(requestId, data) {
|
|
84
|
+
const stmt = db.prepare(`
|
|
85
|
+
UPDATE requests SET response = ?, response_status = ?, finished_at = ?
|
|
86
|
+
WHERE id = ?
|
|
87
|
+
`);
|
|
88
|
+
stmt.run(data.response ?? null, data.response_status ?? null, data.finished_at ?? null, requestId);
|
|
89
|
+
},
|
|
90
|
+
getRequest(requestId) {
|
|
91
|
+
const reqStmt = db.prepare('SELECT * FROM requests WHERE id = ?');
|
|
92
|
+
const requestRow = reqStmt.get(requestId);
|
|
93
|
+
if (!requestRow)
|
|
94
|
+
return null;
|
|
95
|
+
const request = {
|
|
96
|
+
id: requestRow.id,
|
|
97
|
+
method: requestRow.method,
|
|
98
|
+
path: requestRow.path,
|
|
99
|
+
body: requestRow.body ? JSON.parse(requestRow.body) : null,
|
|
100
|
+
response: requestRow.response,
|
|
101
|
+
response_status: requestRow.response_status,
|
|
102
|
+
started_at: requestRow.started_at,
|
|
103
|
+
finished_at: requestRow.finished_at,
|
|
104
|
+
};
|
|
105
|
+
const logs = this.getLogsByRequestId(requestId);
|
|
106
|
+
return { request, logs };
|
|
107
|
+
},
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
//# sourceMappingURL=sqlite.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sqlite.js","sourceRoot":"","sources":["../src/sqlite.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared types for request-scoped logging and storage.
|
|
3
|
+
*/
|
|
4
|
+
export type LogLevel = 'log' | 'info' | 'warn' | 'error';
|
|
5
|
+
export interface LogEntry {
|
|
6
|
+
id: number;
|
|
7
|
+
request_id: string;
|
|
8
|
+
level: string;
|
|
9
|
+
message: string;
|
|
10
|
+
context: Record<string, unknown>;
|
|
11
|
+
created_at: string;
|
|
12
|
+
}
|
|
13
|
+
export interface RequestRow {
|
|
14
|
+
id: string;
|
|
15
|
+
method: string;
|
|
16
|
+
path: string;
|
|
17
|
+
body: string | object | null;
|
|
18
|
+
response: string | null;
|
|
19
|
+
response_status: number | null;
|
|
20
|
+
started_at: string;
|
|
21
|
+
finished_at: string | null;
|
|
22
|
+
}
|
|
23
|
+
export interface RequestWithLogs {
|
|
24
|
+
request: RequestRow;
|
|
25
|
+
logs: LogEntry[];
|
|
26
|
+
}
|
|
27
|
+
export interface RequestLoggerOptions {
|
|
28
|
+
ignorePaths?: (string | RegExp)[];
|
|
29
|
+
logBody?: boolean;
|
|
30
|
+
logResponse?: boolean;
|
|
31
|
+
databasePath?: string;
|
|
32
|
+
databaseUrl?: string;
|
|
33
|
+
}
|
|
34
|
+
export interface RequestLogger {
|
|
35
|
+
addContext(obj: Record<string, unknown>): void;
|
|
36
|
+
log(message: string, meta?: Record<string, unknown>): void;
|
|
37
|
+
info(message: string, meta?: Record<string, unknown>): void;
|
|
38
|
+
warn(message: string, meta?: Record<string, unknown>): void;
|
|
39
|
+
error(message: string, meta?: Record<string, unknown>): void;
|
|
40
|
+
}
|
|
41
|
+
export interface SaveRequestData {
|
|
42
|
+
method: string;
|
|
43
|
+
path: string;
|
|
44
|
+
body: object | null;
|
|
45
|
+
response: string | null;
|
|
46
|
+
response_status: number | null;
|
|
47
|
+
started_at: string;
|
|
48
|
+
finished_at: string | null;
|
|
49
|
+
}
|
|
50
|
+
export interface UpdateRequestData {
|
|
51
|
+
response: string | null;
|
|
52
|
+
response_status: number | null;
|
|
53
|
+
finished_at: string | null;
|
|
54
|
+
}
|
|
55
|
+
export interface Storage {
|
|
56
|
+
saveRequest(requestId: string, data: SaveRequestData): void;
|
|
57
|
+
saveLog(requestId: string, level: string, message: string, context: Record<string, unknown>): void;
|
|
58
|
+
getLogsByRequestId(requestId: string): LogEntry[];
|
|
59
|
+
updateRequest(requestId: string, data: UpdateRequestData): void;
|
|
60
|
+
getRequest(requestId: string): RequestWithLogs | null;
|
|
61
|
+
}
|
|
62
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":";AAAA;;GAEG"}
|
package/package.json
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lazylog-trace",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.3",
|
|
4
4
|
"description": "Request-scoped logging for Express with SQLite persistence. Tie logs to each request and retrieve them by request id.",
|
|
5
|
-
"main": "
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
6
7
|
"bin": {
|
|
7
8
|
"lazylog-studio": "./bin/lazylog-studio.js"
|
|
8
9
|
},
|
|
@@ -10,15 +11,16 @@
|
|
|
10
11
|
"node": ">=14.0.0"
|
|
11
12
|
},
|
|
12
13
|
"scripts": {
|
|
14
|
+
"build": "tsc",
|
|
13
15
|
"test": "node test/integration.js",
|
|
14
16
|
"studio": "node bin/lazylog-studio.js",
|
|
15
17
|
"studio:dev": "cd lazylog-studio && npm run dev",
|
|
16
18
|
"build:studio": "node -e \"process.chdir('lazylog-studio');require('child_process').execSync('npm run build',{stdio:'inherit'});require('fs').cpSync('dist','../studio/dist',{recursive:true})\"",
|
|
17
19
|
"test:all": "node lazylog-studio/scripts/generate-test-data.js",
|
|
18
|
-
"prepublishOnly": "npm test"
|
|
20
|
+
"prepublishOnly": "npm run build && npm test"
|
|
19
21
|
},
|
|
20
22
|
"files": [
|
|
21
|
-
"
|
|
23
|
+
"dist",
|
|
22
24
|
"bin",
|
|
23
25
|
"studio",
|
|
24
26
|
"README.md",
|
|
@@ -47,12 +49,16 @@
|
|
|
47
49
|
"homepage": "https://github.com/ChomasDev/lazylog#readme",
|
|
48
50
|
"dependencies": {
|
|
49
51
|
"better-sqlite3": "^11.0.0",
|
|
50
|
-
"express": "^4.
|
|
52
|
+
"express": "^4.0.0 || ^5.0.0"
|
|
51
53
|
},
|
|
52
54
|
"peerDependencies": {
|
|
53
55
|
"express": "^4.0.0 || ^5.0.0"
|
|
54
56
|
},
|
|
55
57
|
"devDependencies": {
|
|
56
|
-
"
|
|
58
|
+
"@types/better-sqlite3": "^7.6.8",
|
|
59
|
+
"@types/express": "^4.17.21",
|
|
60
|
+
"@types/node": "^20.10.0",
|
|
61
|
+
"express": "^4.21.0",
|
|
62
|
+
"typescript": "^5.9.3"
|
|
57
63
|
}
|
|
58
64
|
}
|
package/src/index.js
DELETED
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
const middleware = require('./middleware.js');
|
|
2
|
-
const { noopLogger } = require('./logger.js');
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Returns the request-scoped logger for the current request.
|
|
6
|
-
* If called outside a request (no middleware context), returns a no-op logger that only writes to console.
|
|
7
|
-
* @returns {{ log: function, info: function, warn: function, error: function, addContext: function }}
|
|
8
|
-
*/
|
|
9
|
-
function getRequestLogger() {
|
|
10
|
-
const store = middleware.getStore();
|
|
11
|
-
if (!store || !store.logger) return noopLogger;
|
|
12
|
-
return store.logger;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Returns all log entries for the given request id.
|
|
17
|
-
* Uses the storage from the last-configured requestLogger middleware.
|
|
18
|
-
* @param {string} requestId
|
|
19
|
-
* @returns {Array<{ id: number, request_id: string, level: string, message: string, context: object, created_at: string }>}
|
|
20
|
-
*/
|
|
21
|
-
function getLogsByRequestId(requestId) {
|
|
22
|
-
const storage = middleware.defaultStorage;
|
|
23
|
-
if (!storage) return [];
|
|
24
|
-
return storage.getLogsByRequestId(requestId);
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* Returns the request row and its logs, or null if not found.
|
|
29
|
-
* @param {string} requestId
|
|
30
|
-
* @returns {{ request: object, logs: array } | null}
|
|
31
|
-
*/
|
|
32
|
-
function getRequest(requestId) {
|
|
33
|
-
const storage = middleware.defaultStorage;
|
|
34
|
-
if (!storage) return null;
|
|
35
|
-
return storage.getRequest(requestId);
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
module.exports = {
|
|
39
|
-
requestLogger: middleware.requestLogger,
|
|
40
|
-
getRequestLogger,
|
|
41
|
-
getLogsByRequestId,
|
|
42
|
-
getRequest,
|
|
43
|
-
};
|
package/src/logger.js
DELETED
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Request-scoped logger. Writes to storage with request_id and optional context.
|
|
3
|
-
* addContext() merges into a shared context object; every log includes that context.
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
function createRequestLogger(requestId, storage, contextRef) {
|
|
7
|
-
function write(level, message, meta = {}) {
|
|
8
|
-
const context = { ...contextRef };
|
|
9
|
-
const payload = { ...context, ...meta };
|
|
10
|
-
storage.saveLog(requestId, level, message, payload);
|
|
11
|
-
const out = payload && Object.keys(payload).length ? ` ${JSON.stringify(payload)}` : '';
|
|
12
|
-
console[level === 'log' ? 'log' : level](`[${requestId}] ${level}: ${message}${out}`);
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
return {
|
|
16
|
-
addContext(obj) {
|
|
17
|
-
if (obj && typeof obj === 'object') {
|
|
18
|
-
Object.assign(contextRef, obj);
|
|
19
|
-
}
|
|
20
|
-
},
|
|
21
|
-
|
|
22
|
-
log(message, meta) {
|
|
23
|
-
write('log', message, meta);
|
|
24
|
-
},
|
|
25
|
-
|
|
26
|
-
info(message, meta) {
|
|
27
|
-
write('info', message, meta);
|
|
28
|
-
},
|
|
29
|
-
|
|
30
|
-
warn(message, meta) {
|
|
31
|
-
write('warn', message, meta);
|
|
32
|
-
},
|
|
33
|
-
|
|
34
|
-
error(message, meta) {
|
|
35
|
-
write('error', message, meta);
|
|
36
|
-
},
|
|
37
|
-
};
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* No-op logger for use outside a request (e.g. getRequestLogger() when not in middleware).
|
|
42
|
-
* Logs to console only, no DB.
|
|
43
|
-
*/
|
|
44
|
-
const noopLogger = {
|
|
45
|
-
addContext() {},
|
|
46
|
-
log(msg, meta) {
|
|
47
|
-
console.log(msg, meta != null ? meta : '');
|
|
48
|
-
},
|
|
49
|
-
info(msg, meta) {
|
|
50
|
-
console.info(msg, meta != null ? meta : '');
|
|
51
|
-
},
|
|
52
|
-
warn(msg, meta) {
|
|
53
|
-
console.warn(msg, meta != null ? meta : '');
|
|
54
|
-
},
|
|
55
|
-
error(msg, meta) {
|
|
56
|
-
console.error(msg, meta != null ? meta : '');
|
|
57
|
-
},
|
|
58
|
-
};
|
|
59
|
-
|
|
60
|
-
module.exports = { createRequestLogger, noopLogger };
|
package/src/middleware.js
DELETED
|
@@ -1,112 +0,0 @@
|
|
|
1
|
-
const { AsyncLocalStorage } = require('async_hooks');
|
|
2
|
-
const crypto = require('crypto');
|
|
3
|
-
const { createRequestLogger } = require('./logger.js');
|
|
4
|
-
const { createSqliteStorage } = require('./sqlite.js');
|
|
5
|
-
|
|
6
|
-
const asyncLocalStorage = new AsyncLocalStorage();
|
|
7
|
-
|
|
8
|
-
/** Default storage instance; set when requestLogger is first used. */
|
|
9
|
-
let defaultStorage = null;
|
|
10
|
-
|
|
11
|
-
function generateRequestId() {
|
|
12
|
-
if (crypto.randomUUID) {
|
|
13
|
-
return crypto.randomUUID();
|
|
14
|
-
}
|
|
15
|
-
return crypto.randomBytes(16).toString('hex');
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
function matchesIgnore(path, ignorePaths) {
|
|
19
|
-
if (!ignorePaths || !ignorePaths.length) return false;
|
|
20
|
-
for (const p of ignorePaths) {
|
|
21
|
-
if (typeof p === 'string' && path === p) return true;
|
|
22
|
-
if (p instanceof RegExp && p.test(path)) return true;
|
|
23
|
-
}
|
|
24
|
-
return false;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* @param {object} options
|
|
29
|
-
* @param {string[]|RegExp[]} [options.ignorePaths]
|
|
30
|
-
* @param {boolean} [options.logBody=true]
|
|
31
|
-
* @param {boolean} [options.logResponse=true]
|
|
32
|
-
* @param {string} [options.databasePath]
|
|
33
|
-
* @param {string} [options.databaseUrl]
|
|
34
|
-
*/
|
|
35
|
-
function requestLogger(options = {}) {
|
|
36
|
-
const {
|
|
37
|
-
ignorePaths = [],
|
|
38
|
-
logBody = true,
|
|
39
|
-
logResponse = true,
|
|
40
|
-
databasePath,
|
|
41
|
-
databaseUrl,
|
|
42
|
-
} = options;
|
|
43
|
-
|
|
44
|
-
const storage = createSqliteStorage({ databasePath, databaseUrl });
|
|
45
|
-
defaultStorage = storage;
|
|
46
|
-
|
|
47
|
-
return function lazylogMiddleware(req, res, next) {
|
|
48
|
-
const path = req.path || req.url?.split('?')[0] || '';
|
|
49
|
-
if (matchesIgnore(path, ignorePaths)) {
|
|
50
|
-
return next();
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
const requestId = generateRequestId();
|
|
54
|
-
const startedAt = new Date().toISOString();
|
|
55
|
-
const context = {};
|
|
56
|
-
const logger = createRequestLogger(requestId, storage, context);
|
|
57
|
-
|
|
58
|
-
storage.saveRequest(requestId, {
|
|
59
|
-
method: req.method,
|
|
60
|
-
path,
|
|
61
|
-
body: logBody ? req.body : null,
|
|
62
|
-
response: null,
|
|
63
|
-
response_status: null,
|
|
64
|
-
started_at: startedAt,
|
|
65
|
-
finished_at: null,
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
let responseBody = null;
|
|
69
|
-
let responseStatus = null;
|
|
70
|
-
|
|
71
|
-
if (logResponse) {
|
|
72
|
-
const originalSend = res.send.bind(res);
|
|
73
|
-
const originalJson = res.json.bind(res);
|
|
74
|
-
|
|
75
|
-
res.send = function (body) {
|
|
76
|
-
responseBody = typeof body === 'string' ? body : JSON.stringify(body);
|
|
77
|
-
return originalSend(body);
|
|
78
|
-
};
|
|
79
|
-
res.json = function (body) {
|
|
80
|
-
responseBody = JSON.stringify(body);
|
|
81
|
-
return originalJson(body);
|
|
82
|
-
};
|
|
83
|
-
|
|
84
|
-
res.on('finish', () => {
|
|
85
|
-
responseStatus = res.statusCode;
|
|
86
|
-
storage.updateRequest(requestId, {
|
|
87
|
-
response: responseBody,
|
|
88
|
-
response_status: responseStatus,
|
|
89
|
-
finished_at: new Date().toISOString(),
|
|
90
|
-
});
|
|
91
|
-
});
|
|
92
|
-
} else {
|
|
93
|
-
res.on('finish', () => {
|
|
94
|
-
storage.updateRequest(requestId, {
|
|
95
|
-
response: null,
|
|
96
|
-
response_status: res.statusCode,
|
|
97
|
-
finished_at: new Date().toISOString(),
|
|
98
|
-
});
|
|
99
|
-
});
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
res.setHeader('X-Request-Id', requestId);
|
|
103
|
-
const store = { requestId, logger, context };
|
|
104
|
-
asyncLocalStorage.run(store, () => next());
|
|
105
|
-
};
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
function getStore() {
|
|
109
|
-
return asyncLocalStorage.getStore();
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
module.exports = { requestLogger, getStore, get defaultStorage() { return defaultStorage; } };
|
package/src/sqlite.js
DELETED
|
@@ -1,139 +0,0 @@
|
|
|
1
|
-
const Database = require('better-sqlite3');
|
|
2
|
-
const path = require('path');
|
|
3
|
-
|
|
4
|
-
const DEFAULT_DB_PATH = './logs.sqlite';
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Resolve database path from options (databasePath or databaseUrl).
|
|
8
|
-
* databaseUrl like 'file:./logs.sqlite' is parsed to a path.
|
|
9
|
-
* @param {{ databasePath?: string, databaseUrl?: string }} options
|
|
10
|
-
* @returns {string}
|
|
11
|
-
*/
|
|
12
|
-
function resolveDbPath(options = {}) {
|
|
13
|
-
if (options.databasePath) {
|
|
14
|
-
return path.resolve(process.cwd(), options.databasePath);
|
|
15
|
-
}
|
|
16
|
-
if (options.databaseUrl) {
|
|
17
|
-
const url = options.databaseUrl;
|
|
18
|
-
if (url.startsWith('file:')) {
|
|
19
|
-
return path.resolve(process.cwd(), url.slice(5).replace(/^\/+/, ''));
|
|
20
|
-
}
|
|
21
|
-
return path.resolve(process.cwd(), url);
|
|
22
|
-
}
|
|
23
|
-
return path.resolve(process.cwd(), DEFAULT_DB_PATH);
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* Create SQLite storage and ensure tables exist.
|
|
28
|
-
* @param {{ databasePath?: string, databaseUrl?: string }} options
|
|
29
|
-
* @returns {import('./storage.js')}
|
|
30
|
-
*/
|
|
31
|
-
function createSqliteStorage(options = {}) {
|
|
32
|
-
const dbPath = resolveDbPath(options);
|
|
33
|
-
const db = new Database(dbPath);
|
|
34
|
-
|
|
35
|
-
db.exec(`
|
|
36
|
-
CREATE TABLE IF NOT EXISTS requests (
|
|
37
|
-
id TEXT PRIMARY KEY,
|
|
38
|
-
method TEXT NOT NULL,
|
|
39
|
-
path TEXT NOT NULL,
|
|
40
|
-
body TEXT,
|
|
41
|
-
response TEXT,
|
|
42
|
-
response_status INTEGER,
|
|
43
|
-
started_at TEXT NOT NULL,
|
|
44
|
-
finished_at TEXT
|
|
45
|
-
);
|
|
46
|
-
|
|
47
|
-
CREATE TABLE IF NOT EXISTS logs (
|
|
48
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
49
|
-
request_id TEXT NOT NULL,
|
|
50
|
-
level TEXT NOT NULL,
|
|
51
|
-
message TEXT NOT NULL,
|
|
52
|
-
context TEXT,
|
|
53
|
-
created_at TEXT NOT NULL,
|
|
54
|
-
FOREIGN KEY (request_id) REFERENCES requests(id)
|
|
55
|
-
);
|
|
56
|
-
|
|
57
|
-
CREATE INDEX IF NOT EXISTS idx_logs_request_id ON logs(request_id);
|
|
58
|
-
`);
|
|
59
|
-
|
|
60
|
-
return {
|
|
61
|
-
saveRequest(requestId, data) {
|
|
62
|
-
const stmt = db.prepare(`
|
|
63
|
-
INSERT INTO requests (id, method, path, body, response, response_status, started_at, finished_at)
|
|
64
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
65
|
-
`);
|
|
66
|
-
const bodyStr = data.body != null ? JSON.stringify(data.body) : null;
|
|
67
|
-
stmt.run(
|
|
68
|
-
requestId,
|
|
69
|
-
data.method,
|
|
70
|
-
data.path,
|
|
71
|
-
bodyStr,
|
|
72
|
-
data.response ?? null,
|
|
73
|
-
data.response_status ?? null,
|
|
74
|
-
data.started_at,
|
|
75
|
-
data.finished_at ?? null
|
|
76
|
-
);
|
|
77
|
-
},
|
|
78
|
-
|
|
79
|
-
saveLog(requestId, level, message, context) {
|
|
80
|
-
const stmt = db.prepare(`
|
|
81
|
-
INSERT INTO logs (request_id, level, message, context, created_at)
|
|
82
|
-
VALUES (?, ?, ?, ?, ?)
|
|
83
|
-
`);
|
|
84
|
-
const contextStr = context && Object.keys(context).length > 0 ? JSON.stringify(context) : null;
|
|
85
|
-
stmt.run(requestId, level, message, contextStr, new Date().toISOString());
|
|
86
|
-
},
|
|
87
|
-
|
|
88
|
-
getLogsByRequestId(requestId) {
|
|
89
|
-
const stmt = db.prepare(`
|
|
90
|
-
SELECT id, request_id, level, message, context, created_at
|
|
91
|
-
FROM logs WHERE request_id = ? ORDER BY created_at ASC
|
|
92
|
-
`);
|
|
93
|
-
const rows = stmt.all(requestId);
|
|
94
|
-
return rows.map((row) => ({
|
|
95
|
-
id: row.id,
|
|
96
|
-
request_id: row.request_id,
|
|
97
|
-
level: row.level,
|
|
98
|
-
message: row.message,
|
|
99
|
-
context: row.context ? JSON.parse(row.context) : {},
|
|
100
|
-
created_at: row.created_at,
|
|
101
|
-
}));
|
|
102
|
-
},
|
|
103
|
-
|
|
104
|
-
updateRequest(requestId, data) {
|
|
105
|
-
const stmt = db.prepare(`
|
|
106
|
-
UPDATE requests SET response = ?, response_status = ?, finished_at = ?
|
|
107
|
-
WHERE id = ?
|
|
108
|
-
`);
|
|
109
|
-
stmt.run(
|
|
110
|
-
data.response ?? null,
|
|
111
|
-
data.response_status ?? null,
|
|
112
|
-
data.finished_at ?? null,
|
|
113
|
-
requestId
|
|
114
|
-
);
|
|
115
|
-
},
|
|
116
|
-
|
|
117
|
-
getRequest(requestId) {
|
|
118
|
-
const reqStmt = db.prepare('SELECT * FROM requests WHERE id = ?');
|
|
119
|
-
const requestRow = reqStmt.get(requestId);
|
|
120
|
-
if (!requestRow) return null;
|
|
121
|
-
|
|
122
|
-
const request = {
|
|
123
|
-
id: requestRow.id,
|
|
124
|
-
method: requestRow.method,
|
|
125
|
-
path: requestRow.path,
|
|
126
|
-
body: requestRow.body ? JSON.parse(requestRow.body) : null,
|
|
127
|
-
response: requestRow.response,
|
|
128
|
-
response_status: requestRow.response_status,
|
|
129
|
-
started_at: requestRow.started_at,
|
|
130
|
-
finished_at: requestRow.finished_at,
|
|
131
|
-
};
|
|
132
|
-
|
|
133
|
-
const logs = this.getLogsByRequestId(requestId);
|
|
134
|
-
return { request, logs };
|
|
135
|
-
},
|
|
136
|
-
};
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
module.exports = { createSqliteStorage, resolveDbPath };
|
package/src/storage.js
DELETED
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Abstract storage interface for request and log persistence.
|
|
3
|
-
* Implementations (e.g. sqlite.js) provide: saveRequest, saveLog, getLogsByRequestId, getRequest.
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* @typedef {Object} RequestRow
|
|
8
|
-
* @property {string} id
|
|
9
|
-
* @property {string} method
|
|
10
|
-
* @property {string} path
|
|
11
|
-
* @property {string|object|null} body
|
|
12
|
-
* @property {string|null} response
|
|
13
|
-
* @property {number|null} response_status
|
|
14
|
-
* @property {string} started_at
|
|
15
|
-
* @property {string|null} finished_at
|
|
16
|
-
*/
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* @typedef {Object} LogEntry
|
|
20
|
-
* @property {number} id
|
|
21
|
-
* @property {string} request_id
|
|
22
|
-
* @property {string} level
|
|
23
|
-
* @property {string} message
|
|
24
|
-
* @property {object} context
|
|
25
|
-
* @property {string} created_at
|
|
26
|
-
*/
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* @typedef {Object} RequestWithLogs
|
|
30
|
-
* @property {RequestRow} request
|
|
31
|
-
* @property {LogEntry[]} logs
|
|
32
|
-
*/
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Storage interface. Implementations must provide:
|
|
36
|
-
* - saveRequest(requestId, data)
|
|
37
|
-
* - saveLog(requestId, level, message, context)
|
|
38
|
-
* - getLogsByRequestId(requestId) -> LogEntry[]
|
|
39
|
-
* - getRequest(requestId) -> RequestWithLogs | null
|
|
40
|
-
*/
|
|
41
|
-
module.exports = {};
|