@nm-logger/logger 1.2.2 → 1.2.4
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 +12 -5
- package/package.json +1 -1
- package/src/LogWriter.js +57 -2
- package/src/Logger.js +200 -11
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# @nm-logger/logger v1.2.
|
|
1
|
+
# @nm-logger/logger v1.2.3
|
|
2
2
|
|
|
3
3
|
Minimal JSON logger for Express:
|
|
4
4
|
|
|
@@ -12,7 +12,8 @@ Minimal JSON logger for Express:
|
|
|
12
12
|
- S3: one JSON file per day per category, append (read+merge+put)
|
|
13
13
|
- Previous day's local folder is removed when the date changes
|
|
14
14
|
- `employee_id` taken from `req.user.employee_id || req.user.emp_code || req.user.id`
|
|
15
|
-
- `baseDir` can be absolute (e.g. `/home/
|
|
15
|
+
- `baseDir` can be absolute (e.g. `/home/logs`)
|
|
16
|
+
|
|
16
17
|
|
|
17
18
|
Basic usage:
|
|
18
19
|
|
|
@@ -25,17 +26,23 @@ const nm_logger = new Logger(
|
|
|
25
26
|
accessKeyId: process.env.AWS_ACCESS_KEY_ID,
|
|
26
27
|
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
|
|
27
28
|
region: "us-east-1",
|
|
28
|
-
bucket: "
|
|
29
|
+
bucket: "bucket-name"
|
|
29
30
|
},
|
|
30
31
|
{
|
|
31
|
-
baseDir: "/home/
|
|
32
|
+
baseDir: "/home/logs",
|
|
32
33
|
uploadIntervalMs: 60_000,
|
|
33
34
|
maskFields: ["aadhaar", "pan"]
|
|
34
35
|
}
|
|
35
36
|
);
|
|
36
37
|
|
|
38
|
+
Folder permission
|
|
39
|
+
|
|
40
|
+
- mkdir -p /home/logs
|
|
41
|
+
- chmod -R 777 /home/logs
|
|
42
|
+
|
|
37
43
|
app.use(nm_logger.requestLoggerMiddleware());
|
|
38
44
|
app.use("/api", routes);
|
|
39
45
|
app.use(nm_logger.expressMiddleware());
|
|
40
46
|
nm_logger.attachAxiosLogger(axios);
|
|
41
|
-
|
|
47
|
+
|
|
48
|
+
```
|
package/package.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"name":"@nm-logger/logger","version":"1.2.
|
|
1
|
+
{"name":"@nm-logger/logger","version":"1.2.4","description":"Express JSON logger with daily success/error/external logs, S3 append uploads, and configurable baseDir.","main":"index.js","types":"index.d.ts","license":"MIT","dependencies":{"@aws-sdk/client-s3":"^3.600.0","fs-extra":"^11.1.1"}}
|
package/src/LogWriter.js
CHANGED
|
@@ -1,2 +1,57 @@
|
|
|
1
|
-
const fs=require("fs-extra");
|
|
2
|
-
|
|
1
|
+
const fs = require("fs-extra");
|
|
2
|
+
const path = require("path");
|
|
3
|
+
const { getDatePath, formatDate } = require("./utils");
|
|
4
|
+
|
|
5
|
+
const MAP = {
|
|
6
|
+
success: "daily_logs_success.json",
|
|
7
|
+
error: "daily_logs_error.json",
|
|
8
|
+
external: "daily_logs_external.json",
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
class LogWriter {
|
|
12
|
+
constructor(baseDir) {
|
|
13
|
+
this.baseDir = baseDir;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
getFilePath(category = "success") {
|
|
17
|
+
const { Y, M, D } = getDatePath();
|
|
18
|
+
const fileName = MAP[category] || MAP.success;
|
|
19
|
+
return path.join(this.baseDir, `${Y}/${M}/${D}/${fileName}`);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
async writeLog(log, category = "success") {
|
|
23
|
+
const filePath = this.getFilePath(category);
|
|
24
|
+
await fs.ensureDir(path.dirname(filePath));
|
|
25
|
+
|
|
26
|
+
let wrapper = { logs: [] };
|
|
27
|
+
|
|
28
|
+
if (await fs.pathExists(filePath)) {
|
|
29
|
+
try {
|
|
30
|
+
const txt = await fs.readFile(filePath, "utf8");
|
|
31
|
+
if (txt.trim()) {
|
|
32
|
+
const parsed = JSON.parse(txt);
|
|
33
|
+
if (Array.isArray(parsed.logs)) {
|
|
34
|
+
wrapper.logs = parsed.logs;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
} catch (_) {}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
wrapper.logs.push({
|
|
41
|
+
url: log.url || "",
|
|
42
|
+
body: log.body || "",
|
|
43
|
+
params: log.params || "",
|
|
44
|
+
response: log.response || "", // 👈 NEW: store external response if present
|
|
45
|
+
type: log.type || "",
|
|
46
|
+
method: log.method || "",
|
|
47
|
+
error: log.error || "",
|
|
48
|
+
employee_id: log.employee_id || "",
|
|
49
|
+
date: log.date || formatDate(new Date()),
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
await fs.writeFile(filePath, JSON.stringify(wrapper, null, 2), "utf8");
|
|
53
|
+
return filePath;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
module.exports = LogWriter;
|
package/src/Logger.js
CHANGED
|
@@ -1,11 +1,200 @@
|
|
|
1
|
-
const LogWriter
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
1
|
+
const LogWriter = require("./LogWriter");
|
|
2
|
+
const Queue = require("./Queue");
|
|
3
|
+
const S3Uploader = require("./S3Uploader");
|
|
4
|
+
const DailyWatcher = require("./DailyWatcher");
|
|
5
|
+
const { getApiType, maskSensitive } = require("./utils");
|
|
6
|
+
const fs = require("fs-extra");
|
|
7
|
+
const path = require("path");
|
|
8
|
+
|
|
9
|
+
class Logger {
|
|
10
|
+
constructor(s3cfg = {}, opt = {}) {
|
|
11
|
+
let dir = null;
|
|
12
|
+
|
|
13
|
+
try {
|
|
14
|
+
if (opt.baseDir) {
|
|
15
|
+
const resolved = path.resolve(opt.baseDir);
|
|
16
|
+
const parent = path.dirname(resolved);
|
|
17
|
+
|
|
18
|
+
// ensure parent + dir exist
|
|
19
|
+
fs.ensureDirSync(parent);
|
|
20
|
+
fs.ensureDirSync(resolved);
|
|
21
|
+
|
|
22
|
+
dir = resolved;
|
|
23
|
+
console.log("[nm-logger] ✔ Using baseDir:", dir);
|
|
24
|
+
}
|
|
25
|
+
} catch (e) {
|
|
26
|
+
console.error("[nm-logger] ❌ baseDir INVALID:", e.message);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (!dir) {
|
|
30
|
+
dir = path.resolve("logs");
|
|
31
|
+
fs.ensureDirSync(dir);
|
|
32
|
+
console.error("[nm-logger] ⚠ Falling back to:", dir);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
this.baseDir = dir;
|
|
36
|
+
this.logWriter = new LogWriter(this.baseDir);
|
|
37
|
+
this.queue = new Queue();
|
|
38
|
+
this.s3 = new S3Uploader(s3cfg);
|
|
39
|
+
this.maskFields = (opt.maskFields || []).map((x) =>
|
|
40
|
+
String(x).toLowerCase()
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
new DailyWatcher(this.baseDir, this.queue, this.s3, {
|
|
44
|
+
uploadIntervalMs: opt.uploadIntervalMs || 60_000,
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Mask helper
|
|
49
|
+
mask(v) {
|
|
50
|
+
return maskSensitive(v, this.maskFields);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Build base log object for success/error logs
|
|
54
|
+
buildBaseLog(req, employee_id = "") {
|
|
55
|
+
const url = req && req.originalUrl ? req.originalUrl : "";
|
|
56
|
+
const body = (req && req.body) || {};
|
|
57
|
+
const params = {
|
|
58
|
+
params: (req && req.params) || {},
|
|
59
|
+
query: (req && req.query) || {},
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
const maskedBody = this.mask(body);
|
|
63
|
+
const maskedParams = this.mask(params);
|
|
64
|
+
const method = (req && req.method ? req.method : "").toUpperCase();
|
|
65
|
+
|
|
66
|
+
const emp =
|
|
67
|
+
employee_id ||
|
|
68
|
+
req?.employee_id ||
|
|
69
|
+
(req &&
|
|
70
|
+
req.user &&
|
|
71
|
+
(req.user.employee_id || req.user.emp_code || req.user.id)) ||
|
|
72
|
+
"";
|
|
73
|
+
|
|
74
|
+
return {
|
|
75
|
+
url,
|
|
76
|
+
body: JSON.stringify(maskedBody || {}),
|
|
77
|
+
params: JSON.stringify(maskedParams || {}),
|
|
78
|
+
type: getApiType(url),
|
|
79
|
+
method,
|
|
80
|
+
error: "",
|
|
81
|
+
employee_id: emp,
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// ---- Error logging (Express errors) ----
|
|
86
|
+
async logError(err, req, employee_id = "") {
|
|
87
|
+
const base = this.buildBaseLog(req || {}, employee_id);
|
|
88
|
+
|
|
89
|
+
const log = {
|
|
90
|
+
...base,
|
|
91
|
+
error: err && err.message ? err.message : String(err || ""),
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
await this.logWriter.writeLog(log, "error");
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// ---- Success logging (normal requests) ----
|
|
98
|
+
async logRequest(req, employee_id = "") {
|
|
99
|
+
const log = this.buildBaseLog(req, employee_id);
|
|
100
|
+
await this.logWriter.writeLog(log, "success");
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// ---- External API logging (Axios) ----
|
|
104
|
+
async logExternalApi({
|
|
105
|
+
url,
|
|
106
|
+
method,
|
|
107
|
+
data,
|
|
108
|
+
params,
|
|
109
|
+
error,
|
|
110
|
+
employeeId,
|
|
111
|
+
response, // 👈 NEW: store external API response
|
|
112
|
+
}) {
|
|
113
|
+
const maskedBody = this.mask(data || {});
|
|
114
|
+
const maskedParams = this.mask(params || {});
|
|
115
|
+
const maskedResponse = this.mask(response || {});
|
|
116
|
+
|
|
117
|
+
const log = {
|
|
118
|
+
url: url || "",
|
|
119
|
+
body: JSON.stringify(maskedBody || {}),
|
|
120
|
+
params: JSON.stringify(maskedParams || {}),
|
|
121
|
+
response: JSON.stringify(maskedResponse || {}), // 👈 NEW FIELD
|
|
122
|
+
type: "external_api",
|
|
123
|
+
method: (method || "").toUpperCase(),
|
|
124
|
+
error: error || "",
|
|
125
|
+
employee_id: employeeId || "",
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
await this.logWriter.writeLog(log, "external");
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// ---- Express error middleware ----
|
|
132
|
+
expressMiddleware() {
|
|
133
|
+
return (err, req, res, next) => {
|
|
134
|
+
try {
|
|
135
|
+
this.logError(err, req).catch(() => {});
|
|
136
|
+
} catch (_) {}
|
|
137
|
+
next(err);
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// ---- Request logger middleware (once per request) ----
|
|
142
|
+
requestLoggerMiddleware() {
|
|
143
|
+
return (req, res, next) => {
|
|
144
|
+
try {
|
|
145
|
+
if (!req.__nm_logger_logged) {
|
|
146
|
+
req.__nm_logger_logged = true;
|
|
147
|
+
this.logRequest(req).catch(() => {});
|
|
148
|
+
}
|
|
149
|
+
} catch (_) {}
|
|
150
|
+
next();
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// ---- Axios interceptor ----
|
|
155
|
+
attachAxiosLogger(axiosInstance) {
|
|
156
|
+
if (!axiosInstance || !axiosInstance.interceptors) return;
|
|
157
|
+
|
|
158
|
+
// Success response
|
|
159
|
+
axiosInstance.interceptors.response.use(
|
|
160
|
+
(response) => {
|
|
161
|
+
try {
|
|
162
|
+
const cfg = response.config || {};
|
|
163
|
+
this.logExternalApi({
|
|
164
|
+
url: cfg.url,
|
|
165
|
+
method: cfg.method,
|
|
166
|
+
data: cfg.data,
|
|
167
|
+
params: cfg.params,
|
|
168
|
+
response: response.data, // 👈 log external API response body
|
|
169
|
+
error: null,
|
|
170
|
+
employeeId: cfg.employee_id,
|
|
171
|
+
}).catch(() => {});
|
|
172
|
+
} catch (_) {}
|
|
173
|
+
|
|
174
|
+
return response;
|
|
175
|
+
},
|
|
176
|
+
|
|
177
|
+
// Error response
|
|
178
|
+
(error) => {
|
|
179
|
+
try {
|
|
180
|
+
const cfg = (error && error.config) || {};
|
|
181
|
+
const res = error && error.response;
|
|
182
|
+
|
|
183
|
+
this.logExternalApi({
|
|
184
|
+
url: cfg && cfg.url,
|
|
185
|
+
method: cfg && cfg.method,
|
|
186
|
+
data: cfg && cfg.data,
|
|
187
|
+
params: cfg && cfg.params,
|
|
188
|
+
response: res && res.data, // 👈 log response even on error
|
|
189
|
+
error: error && error.message,
|
|
190
|
+
employeeId: cfg && cfg.employee_id,
|
|
191
|
+
}).catch(() => {});
|
|
192
|
+
} catch (_) {}
|
|
193
|
+
|
|
194
|
+
return Promise.reject(error);
|
|
195
|
+
}
|
|
196
|
+
);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
module.exports = Logger;
|