pms_md 1.0.0
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 +93 -0
- package/node-monitor/ARCHITECTURE.md +341 -0
- package/node-monitor/CHANGELOG.md +105 -0
- package/node-monitor/CONTRIBUTING.md +96 -0
- package/node-monitor/DESIGN_IMPROVEMENTS.md +286 -0
- package/node-monitor/FILTER_BUTTONS_FIX.md +303 -0
- package/node-monitor/GETTING_STARTED.md +416 -0
- package/node-monitor/INSTALLATION.md +470 -0
- package/node-monitor/LICENSE +22 -0
- package/node-monitor/PUBLISHING_GUIDE.md +331 -0
- package/node-monitor/QUICK_REFERENCE.md +252 -0
- package/node-monitor/README.md +458 -0
- package/node-monitor/READY_TO_PUBLISH.md +272 -0
- package/node-monitor/SETUP_GUIDE.md +479 -0
- package/node-monitor/examples/EMAIL_SETUP_GUIDE.md +282 -0
- package/node-monitor/examples/ERROR_LOGGING_GUIDE.md +405 -0
- package/node-monitor/examples/GET_APP_PASSWORD.md +145 -0
- package/node-monitor/examples/LOG_FILES_REFERENCE.md +336 -0
- package/node-monitor/examples/QUICK_START_EMAIL.md +126 -0
- package/node-monitor/examples/express-app.js +499 -0
- package/node-monitor/examples/package-lock.json +1295 -0
- package/node-monitor/examples/package.json +18 -0
- package/node-monitor/examples/public/css/style.css +718 -0
- package/node-monitor/examples/public/js/dashboard.js +207 -0
- package/node-monitor/examples/public/js/health.js +114 -0
- package/node-monitor/examples/public/js/main.js +89 -0
- package/node-monitor/examples/public/js/metrics.js +225 -0
- package/node-monitor/examples/public/js/theme.js +138 -0
- package/node-monitor/examples/views/dashboard.ejs +20 -0
- package/node-monitor/examples/views/error-logs.ejs +1129 -0
- package/node-monitor/examples/views/health.ejs +21 -0
- package/node-monitor/examples/views/home.ejs +341 -0
- package/node-monitor/examples/views/layout.ejs +50 -0
- package/node-monitor/examples/views/metrics.ejs +16 -0
- package/node-monitor/examples/views/partials/footer.ejs +16 -0
- package/node-monitor/examples/views/partials/header.ejs +35 -0
- package/node-monitor/examples/views/partials/nav.ejs +23 -0
- package/node-monitor/examples/views/status.ejs +390 -0
- package/node-monitor/package-lock.json +4300 -0
- package/node-monitor/package.json +76 -0
- package/node-monitor/pre-publish-check.js +200 -0
- package/node-monitor/src/config/monitoringConfig.js +255 -0
- package/node-monitor/src/index.js +300 -0
- package/node-monitor/src/logger/errorLogger.js +297 -0
- package/node-monitor/src/monitors/apiErrorMonitor.js +156 -0
- package/node-monitor/src/monitors/dbConnectionMonitor.js +389 -0
- package/node-monitor/src/monitors/serverHealthMonitor.js +320 -0
- package/node-monitor/src/monitors/systemResourceMonitor.js +357 -0
- package/node-monitor/src/notifiers/emailNotifier.js +248 -0
- package/node-monitor/src/notifiers/notificationManager.js +96 -0
- package/node-monitor/src/notifiers/slackNotifier.js +209 -0
- package/node-monitor/src/views/dashboard.html +530 -0
- package/node-monitor/src/views/health.html +399 -0
- package/node-monitor/src/views/metrics.html +406 -0
- package/package.json +22 -0
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Node Monitor - Main Entry Point
|
|
3
|
+
* Comprehensive monitoring solution for Node.js applications
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const MonitoringConfig = require('./config/monitoringConfig');
|
|
7
|
+
const ErrorLogger = require('./logger/errorLogger');
|
|
8
|
+
const NotificationManager = require('./notifiers/notificationManager');
|
|
9
|
+
const ApiErrorMonitor = require('./monitors/apiErrorMonitor');
|
|
10
|
+
const ServerHealthMonitor = require('./monitors/serverHealthMonitor');
|
|
11
|
+
const SystemResourceMonitor = require('./monitors/systemResourceMonitor');
|
|
12
|
+
const DbConnectionMonitor = require('./monitors/dbConnectionMonitor');
|
|
13
|
+
|
|
14
|
+
class NodeMonitor {
|
|
15
|
+
constructor(userConfig = {}) {
|
|
16
|
+
// Initialize configuration
|
|
17
|
+
this.config = new MonitoringConfig(userConfig);
|
|
18
|
+
|
|
19
|
+
// Initialize logger
|
|
20
|
+
this.logger = new ErrorLogger(this.config.getAll());
|
|
21
|
+
|
|
22
|
+
// Initialize notification manager
|
|
23
|
+
this.notificationManager = new NotificationManager(this.config.getAll(), this.logger);
|
|
24
|
+
|
|
25
|
+
// Initialize monitors
|
|
26
|
+
this.apiErrorMonitor = new ApiErrorMonitor(
|
|
27
|
+
this.config.getAll(),
|
|
28
|
+
this.logger,
|
|
29
|
+
this.notificationManager
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
this.serverHealthMonitor = new ServerHealthMonitor(
|
|
33
|
+
this.config.getAll(),
|
|
34
|
+
this.logger,
|
|
35
|
+
this.notificationManager
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
this.systemResourceMonitor = new SystemResourceMonitor(
|
|
39
|
+
this.config.getAll(),
|
|
40
|
+
this.logger,
|
|
41
|
+
this.notificationManager
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
this.dbConnectionMonitor = new DbConnectionMonitor(
|
|
45
|
+
this.config.getAll(),
|
|
46
|
+
this.logger,
|
|
47
|
+
this.notificationManager
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
this.isStarted = false;
|
|
51
|
+
|
|
52
|
+
this.logger.logInfo('Node Monitor initialized', {
|
|
53
|
+
app: this.config.get('app.name'),
|
|
54
|
+
version: this.config.get('app.version'),
|
|
55
|
+
environment: this.config.get('app.environment')
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Start all monitoring services
|
|
61
|
+
*/
|
|
62
|
+
start() {
|
|
63
|
+
if (this.isStarted) {
|
|
64
|
+
this.logger.logWarning('monitor_already_started', 'Monitor is already started');
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
this.serverHealthMonitor.start();
|
|
69
|
+
this.systemResourceMonitor.start();
|
|
70
|
+
this.dbConnectionMonitor.start();
|
|
71
|
+
|
|
72
|
+
this.isStarted = true;
|
|
73
|
+
this.logger.logInfo('All monitoring services started');
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Stop all monitoring services
|
|
78
|
+
*/
|
|
79
|
+
stop() {
|
|
80
|
+
if (!this.isStarted) {
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
this.serverHealthMonitor.stop();
|
|
85
|
+
this.systemResourceMonitor.stop();
|
|
86
|
+
this.dbConnectionMonitor.stop();
|
|
87
|
+
|
|
88
|
+
this.isStarted = false;
|
|
89
|
+
this.logger.logInfo('All monitoring services stopped');
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Get Express error handling middleware
|
|
94
|
+
*/
|
|
95
|
+
errorMiddleware() {
|
|
96
|
+
return this.apiErrorMonitor.middleware();
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Get Express request logger middleware
|
|
101
|
+
*/
|
|
102
|
+
requestLogger() {
|
|
103
|
+
return this.apiErrorMonitor.requestLogger();
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Get 404 handler middleware
|
|
108
|
+
*/
|
|
109
|
+
notFoundHandler() {
|
|
110
|
+
return this.apiErrorMonitor.notFoundHandler();
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Get async error handler wrapper
|
|
115
|
+
*/
|
|
116
|
+
asyncHandler(fn) {
|
|
117
|
+
return this.apiErrorMonitor.asyncHandler(fn);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Get health check endpoint handler
|
|
122
|
+
*/
|
|
123
|
+
healthCheckEndpoint() {
|
|
124
|
+
return this.serverHealthMonitor.healthCheckEndpoint();
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Get basic health info endpoint handler
|
|
129
|
+
*/
|
|
130
|
+
healthInfoEndpoint() {
|
|
131
|
+
return this.serverHealthMonitor.healthInfoEndpoint();
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Register a custom health check
|
|
136
|
+
*/
|
|
137
|
+
registerHealthCheck(name, checkFunction) {
|
|
138
|
+
this.serverHealthMonitor.registerHealthCheck(name, checkFunction);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Register a database connection for monitoring
|
|
143
|
+
*/
|
|
144
|
+
registerDatabase(name, type, connection, testQuery = null) {
|
|
145
|
+
this.dbConnectionMonitor.registerConnection(name, type, connection, testQuery);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Setup graceful shutdown handlers
|
|
150
|
+
*/
|
|
151
|
+
setupGracefulShutdown(server) {
|
|
152
|
+
this.serverHealthMonitor.setupGracefulShutdown(server);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Get current monitoring status
|
|
157
|
+
*/
|
|
158
|
+
getStatus() {
|
|
159
|
+
return {
|
|
160
|
+
isRunning: this.isStarted,
|
|
161
|
+
health: this.serverHealthMonitor.getStatus(),
|
|
162
|
+
system: this.systemResourceMonitor.getCurrentMetrics(),
|
|
163
|
+
databases: this.dbConnectionMonitor.getStatus(),
|
|
164
|
+
errors: this.apiErrorMonitor.getStats()
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Get system information
|
|
170
|
+
*/
|
|
171
|
+
getSystemInfo() {
|
|
172
|
+
return this.systemResourceMonitor.getSystemInfo();
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Get metrics history
|
|
177
|
+
*/
|
|
178
|
+
getMetricsHistory() {
|
|
179
|
+
return this.systemResourceMonitor.getMetricsHistory();
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Get database statistics
|
|
184
|
+
*/
|
|
185
|
+
async getDatabaseStats(name) {
|
|
186
|
+
return this.dbConnectionMonitor.getStats(name);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Send custom notification
|
|
191
|
+
*/
|
|
192
|
+
async notify(level, subject, message, details = {}) {
|
|
193
|
+
switch (level.toLowerCase()) {
|
|
194
|
+
case 'critical':
|
|
195
|
+
return this.notificationManager.sendCritical(subject, message, details);
|
|
196
|
+
case 'warning':
|
|
197
|
+
return this.notificationManager.sendWarning(subject, message, details);
|
|
198
|
+
case 'info':
|
|
199
|
+
return this.notificationManager.sendInfo(subject, message, details);
|
|
200
|
+
case 'recovery':
|
|
201
|
+
return this.notificationManager.sendRecovery(subject, message, details);
|
|
202
|
+
default:
|
|
203
|
+
return this.notificationManager.sendInfo(subject, message, details);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Log custom error
|
|
209
|
+
*/
|
|
210
|
+
logError(type, message, details = {}) {
|
|
211
|
+
return this.logger.logSystemError(type, message, details);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Log custom warning
|
|
216
|
+
*/
|
|
217
|
+
logWarning(type, message, details = {}) {
|
|
218
|
+
return this.logger.logWarning(type, message, details);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Log custom info
|
|
223
|
+
*/
|
|
224
|
+
logInfo(message, details = {}) {
|
|
225
|
+
return this.logger.logInfo(message, details);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Get Winston logger instance for custom logging
|
|
230
|
+
*/
|
|
231
|
+
getLogger() {
|
|
232
|
+
return this.logger.getLogger();
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Get configuration
|
|
237
|
+
*/
|
|
238
|
+
getConfig() {
|
|
239
|
+
return this.config.getSanitized();
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Update configuration at runtime
|
|
244
|
+
*/
|
|
245
|
+
updateConfig(path, value) {
|
|
246
|
+
this.config.set(path, value);
|
|
247
|
+
this.logger.logInfo('Configuration updated', { path, value });
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* Create a monitoring dashboard endpoint (returns JSON data)
|
|
252
|
+
*/
|
|
253
|
+
dashboardEndpoint() {
|
|
254
|
+
return async (req, res) => {
|
|
255
|
+
try {
|
|
256
|
+
const status = this.getStatus();
|
|
257
|
+
const systemInfo = this.getSystemInfo();
|
|
258
|
+
const metricsHistory = this.getMetricsHistory();
|
|
259
|
+
|
|
260
|
+
res.json({
|
|
261
|
+
status: 'ok',
|
|
262
|
+
timestamp: new Date().toISOString(),
|
|
263
|
+
application: {
|
|
264
|
+
name: this.config.get('app.name'),
|
|
265
|
+
version: this.config.get('app.version'),
|
|
266
|
+
environment: this.config.get('app.environment')
|
|
267
|
+
},
|
|
268
|
+
monitoring: status,
|
|
269
|
+
system: systemInfo,
|
|
270
|
+
metrics: metricsHistory
|
|
271
|
+
});
|
|
272
|
+
} catch (error) {
|
|
273
|
+
res.status(500).json({
|
|
274
|
+
status: 'error',
|
|
275
|
+
error: error.message
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
};
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* Static method to create error with status code
|
|
283
|
+
*/
|
|
284
|
+
static createError(message, statusCode = 500, details = {}) {
|
|
285
|
+
return ApiErrorMonitor.createError(message, statusCode, details);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// Export the main class
|
|
290
|
+
module.exports = NodeMonitor;
|
|
291
|
+
|
|
292
|
+
// Also export individual components for advanced usage
|
|
293
|
+
module.exports.MonitoringConfig = MonitoringConfig;
|
|
294
|
+
module.exports.ErrorLogger = ErrorLogger;
|
|
295
|
+
module.exports.NotificationManager = NotificationManager;
|
|
296
|
+
module.exports.ApiErrorMonitor = ApiErrorMonitor;
|
|
297
|
+
module.exports.ServerHealthMonitor = ServerHealthMonitor;
|
|
298
|
+
module.exports.SystemResourceMonitor = SystemResourceMonitor;
|
|
299
|
+
module.exports.DbConnectionMonitor = DbConnectionMonitor;
|
|
300
|
+
|
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Error Logger Module
|
|
3
|
+
* Winston-based logging with rotation, formatting, and sanitization
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const winston = require('winston');
|
|
7
|
+
const DailyRotateFile = require('winston-daily-rotate-file');
|
|
8
|
+
const path = require('path');
|
|
9
|
+
const fs = require('fs');
|
|
10
|
+
|
|
11
|
+
class ErrorLogger {
|
|
12
|
+
constructor(config) {
|
|
13
|
+
this.config = config;
|
|
14
|
+
this.logger = null;
|
|
15
|
+
this.errorCounts = new Map(); // Track error frequency
|
|
16
|
+
this.initialize();
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Initialize Winston logger
|
|
21
|
+
*/
|
|
22
|
+
initialize() {
|
|
23
|
+
if (!this.config.logging.enabled) {
|
|
24
|
+
this.logger = this.createNoOpLogger();
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Ensure log directory exists
|
|
29
|
+
if (!fs.existsSync(this.config.logging.directory)) {
|
|
30
|
+
fs.mkdirSync(this.config.logging.directory, { recursive: true });
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const transports = [];
|
|
34
|
+
|
|
35
|
+
// Console transport
|
|
36
|
+
if (this.config.logging.console) {
|
|
37
|
+
transports.push(
|
|
38
|
+
new winston.transports.Console({
|
|
39
|
+
format: winston.format.combine(
|
|
40
|
+
winston.format.colorize(),
|
|
41
|
+
winston.format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }),
|
|
42
|
+
winston.format.printf(this.consoleFormat.bind(this))
|
|
43
|
+
)
|
|
44
|
+
})
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// File transport with rotation
|
|
49
|
+
transports.push(
|
|
50
|
+
new DailyRotateFile({
|
|
51
|
+
filename: path.join(this.config.logging.directory, 'application-%DATE%.log'),
|
|
52
|
+
datePattern: 'YYYY-MM-DD',
|
|
53
|
+
maxSize: this.config.logging.maxSize,
|
|
54
|
+
maxFiles: this.config.logging.maxFiles,
|
|
55
|
+
format: this.getFileFormat()
|
|
56
|
+
})
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
// Error-specific file transport
|
|
60
|
+
transports.push(
|
|
61
|
+
new DailyRotateFile({
|
|
62
|
+
filename: path.join(this.config.logging.directory, 'error-%DATE%.log'),
|
|
63
|
+
datePattern: 'YYYY-MM-DD',
|
|
64
|
+
level: 'error',
|
|
65
|
+
maxSize: this.config.logging.maxSize,
|
|
66
|
+
maxFiles: this.config.logging.maxFiles,
|
|
67
|
+
format: this.getFileFormat()
|
|
68
|
+
})
|
|
69
|
+
);
|
|
70
|
+
|
|
71
|
+
this.logger = winston.createLogger({
|
|
72
|
+
level: this.config.logging.level,
|
|
73
|
+
transports,
|
|
74
|
+
exitOnError: false
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Get file format based on configuration
|
|
80
|
+
*/
|
|
81
|
+
getFileFormat() {
|
|
82
|
+
if (this.config.logging.format === 'json') {
|
|
83
|
+
return winston.format.combine(
|
|
84
|
+
winston.format.timestamp(),
|
|
85
|
+
winston.format.errors({ stack: true }),
|
|
86
|
+
winston.format.json()
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return winston.format.combine(
|
|
91
|
+
winston.format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }),
|
|
92
|
+
winston.format.errors({ stack: true }),
|
|
93
|
+
winston.format.printf(this.simpleFormat.bind(this))
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Console format
|
|
99
|
+
*/
|
|
100
|
+
consoleFormat(info) {
|
|
101
|
+
const { timestamp, level, message, ...meta } = info;
|
|
102
|
+
let output = `${timestamp} [${level}]: ${message}`;
|
|
103
|
+
|
|
104
|
+
if (Object.keys(meta).length > 0) {
|
|
105
|
+
output += `\n${JSON.stringify(meta, null, 2)}`;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return output;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Simple text format
|
|
113
|
+
*/
|
|
114
|
+
simpleFormat(info) {
|
|
115
|
+
const { timestamp, level, message, ...meta } = info;
|
|
116
|
+
let output = `${timestamp} [${level.toUpperCase()}]: ${message}`;
|
|
117
|
+
|
|
118
|
+
if (Object.keys(meta).length > 0) {
|
|
119
|
+
output += ` | ${JSON.stringify(meta)}`;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
return output;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Create no-op logger when logging is disabled
|
|
127
|
+
*/
|
|
128
|
+
createNoOpLogger() {
|
|
129
|
+
const noop = () => {};
|
|
130
|
+
return {
|
|
131
|
+
info: noop,
|
|
132
|
+
warn: noop,
|
|
133
|
+
error: noop,
|
|
134
|
+
debug: noop,
|
|
135
|
+
verbose: noop
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Sanitize sensitive data from objects
|
|
141
|
+
*/
|
|
142
|
+
sanitize(data) {
|
|
143
|
+
if (!this.config.errorTracking.sanitizeSensitiveData) {
|
|
144
|
+
return data;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
if (typeof data !== 'object' || data === null) {
|
|
148
|
+
return data;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
const sanitized = Array.isArray(data) ? [...data] : { ...data };
|
|
152
|
+
const sensitiveFields = this.config.errorTracking.sensitiveFields;
|
|
153
|
+
|
|
154
|
+
for (const key in sanitized) {
|
|
155
|
+
if (sensitiveFields.some(field => key.toLowerCase().includes(field.toLowerCase()))) {
|
|
156
|
+
sanitized[key] = '***REDACTED***';
|
|
157
|
+
} else if (typeof sanitized[key] === 'object' && sanitized[key] !== null) {
|
|
158
|
+
sanitized[key] = this.sanitize(sanitized[key]);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
return sanitized;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Log API error
|
|
167
|
+
*/
|
|
168
|
+
logApiError(error, req) {
|
|
169
|
+
const errorData = {
|
|
170
|
+
type: 'API_ERROR',
|
|
171
|
+
method: req.method,
|
|
172
|
+
url: req.originalUrl || req.url,
|
|
173
|
+
statusCode: error.statusCode || error.status || 500,
|
|
174
|
+
message: error.message,
|
|
175
|
+
stack: this.config.errorTracking.captureStackTrace ? error.stack : undefined,
|
|
176
|
+
headers: this.sanitize(req.headers),
|
|
177
|
+
query: this.sanitize(req.query),
|
|
178
|
+
body: this.sanitize(req.body),
|
|
179
|
+
ip: req.ip || req.connection?.remoteAddress,
|
|
180
|
+
userAgent: req.get('user-agent') || req.headers['user-agent'] || req.headers['User-Agent'] || 'Unknown',
|
|
181
|
+
timestamp: new Date().toISOString(),
|
|
182
|
+
app: this.config.app
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
this.logger.error('API Error', errorData);
|
|
186
|
+
this.trackErrorFrequency('api_error');
|
|
187
|
+
|
|
188
|
+
return errorData;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Log system error
|
|
193
|
+
*/
|
|
194
|
+
logSystemError(type, message, details = {}) {
|
|
195
|
+
const errorData = {
|
|
196
|
+
type: type.toUpperCase(),
|
|
197
|
+
message,
|
|
198
|
+
...details,
|
|
199
|
+
timestamp: new Date().toISOString(),
|
|
200
|
+
app: this.config.app
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
this.logger.error(message, errorData);
|
|
204
|
+
this.trackErrorFrequency(type);
|
|
205
|
+
|
|
206
|
+
return errorData;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Log warning
|
|
211
|
+
*/
|
|
212
|
+
logWarning(type, message, details = {}) {
|
|
213
|
+
const warningData = {
|
|
214
|
+
type: type.toUpperCase(),
|
|
215
|
+
message,
|
|
216
|
+
...details,
|
|
217
|
+
timestamp: new Date().toISOString(),
|
|
218
|
+
app: this.config.app
|
|
219
|
+
};
|
|
220
|
+
|
|
221
|
+
this.logger.warn(message, warningData);
|
|
222
|
+
|
|
223
|
+
return warningData;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Log info
|
|
228
|
+
*/
|
|
229
|
+
logInfo(message, details = {}) {
|
|
230
|
+
this.logger.info(message, {
|
|
231
|
+
...details,
|
|
232
|
+
timestamp: new Date().toISOString(),
|
|
233
|
+
app: this.config.app
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* Track error frequency for rate detection
|
|
239
|
+
*/
|
|
240
|
+
trackErrorFrequency(errorType) {
|
|
241
|
+
const now = Date.now();
|
|
242
|
+
const key = `${errorType}_${Math.floor(now / 60000)}`; // Per minute bucket
|
|
243
|
+
|
|
244
|
+
const count = (this.errorCounts.get(key) || 0) + 1;
|
|
245
|
+
this.errorCounts.set(key, count);
|
|
246
|
+
|
|
247
|
+
// Clean old entries (older than 5 minutes)
|
|
248
|
+
const fiveMinutesAgo = Math.floor((now - 300000) / 60000);
|
|
249
|
+
for (const [k] of this.errorCounts) {
|
|
250
|
+
const timestamp = parseInt(k.split('_').pop());
|
|
251
|
+
if (timestamp < fiveMinutesAgo) {
|
|
252
|
+
this.errorCounts.delete(k);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
return count;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* Get error rate for a specific type
|
|
261
|
+
*/
|
|
262
|
+
getErrorRate(errorType) {
|
|
263
|
+
const now = Date.now();
|
|
264
|
+
const currentMinute = Math.floor(now / 60000);
|
|
265
|
+
const key = `${errorType}_${currentMinute}`;
|
|
266
|
+
|
|
267
|
+
return this.errorCounts.get(key) || 0;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* Get total error count across all types
|
|
272
|
+
*/
|
|
273
|
+
getTotalErrorRate() {
|
|
274
|
+
const now = Date.now();
|
|
275
|
+
const currentMinute = Math.floor(now / 60000);
|
|
276
|
+
let total = 0;
|
|
277
|
+
|
|
278
|
+
for (const [key, count] of this.errorCounts) {
|
|
279
|
+
const timestamp = parseInt(key.split('_').pop());
|
|
280
|
+
if (timestamp === currentMinute) {
|
|
281
|
+
total += count;
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
return total;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* Get Winston logger instance for custom logging
|
|
290
|
+
*/
|
|
291
|
+
getLogger() {
|
|
292
|
+
return this.logger;
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
module.exports = ErrorLogger;
|
|
297
|
+
|