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.
Files changed (55) hide show
  1. package/README.md +93 -0
  2. package/node-monitor/ARCHITECTURE.md +341 -0
  3. package/node-monitor/CHANGELOG.md +105 -0
  4. package/node-monitor/CONTRIBUTING.md +96 -0
  5. package/node-monitor/DESIGN_IMPROVEMENTS.md +286 -0
  6. package/node-monitor/FILTER_BUTTONS_FIX.md +303 -0
  7. package/node-monitor/GETTING_STARTED.md +416 -0
  8. package/node-monitor/INSTALLATION.md +470 -0
  9. package/node-monitor/LICENSE +22 -0
  10. package/node-monitor/PUBLISHING_GUIDE.md +331 -0
  11. package/node-monitor/QUICK_REFERENCE.md +252 -0
  12. package/node-monitor/README.md +458 -0
  13. package/node-monitor/READY_TO_PUBLISH.md +272 -0
  14. package/node-monitor/SETUP_GUIDE.md +479 -0
  15. package/node-monitor/examples/EMAIL_SETUP_GUIDE.md +282 -0
  16. package/node-monitor/examples/ERROR_LOGGING_GUIDE.md +405 -0
  17. package/node-monitor/examples/GET_APP_PASSWORD.md +145 -0
  18. package/node-monitor/examples/LOG_FILES_REFERENCE.md +336 -0
  19. package/node-monitor/examples/QUICK_START_EMAIL.md +126 -0
  20. package/node-monitor/examples/express-app.js +499 -0
  21. package/node-monitor/examples/package-lock.json +1295 -0
  22. package/node-monitor/examples/package.json +18 -0
  23. package/node-monitor/examples/public/css/style.css +718 -0
  24. package/node-monitor/examples/public/js/dashboard.js +207 -0
  25. package/node-monitor/examples/public/js/health.js +114 -0
  26. package/node-monitor/examples/public/js/main.js +89 -0
  27. package/node-monitor/examples/public/js/metrics.js +225 -0
  28. package/node-monitor/examples/public/js/theme.js +138 -0
  29. package/node-monitor/examples/views/dashboard.ejs +20 -0
  30. package/node-monitor/examples/views/error-logs.ejs +1129 -0
  31. package/node-monitor/examples/views/health.ejs +21 -0
  32. package/node-monitor/examples/views/home.ejs +341 -0
  33. package/node-monitor/examples/views/layout.ejs +50 -0
  34. package/node-monitor/examples/views/metrics.ejs +16 -0
  35. package/node-monitor/examples/views/partials/footer.ejs +16 -0
  36. package/node-monitor/examples/views/partials/header.ejs +35 -0
  37. package/node-monitor/examples/views/partials/nav.ejs +23 -0
  38. package/node-monitor/examples/views/status.ejs +390 -0
  39. package/node-monitor/package-lock.json +4300 -0
  40. package/node-monitor/package.json +76 -0
  41. package/node-monitor/pre-publish-check.js +200 -0
  42. package/node-monitor/src/config/monitoringConfig.js +255 -0
  43. package/node-monitor/src/index.js +300 -0
  44. package/node-monitor/src/logger/errorLogger.js +297 -0
  45. package/node-monitor/src/monitors/apiErrorMonitor.js +156 -0
  46. package/node-monitor/src/monitors/dbConnectionMonitor.js +389 -0
  47. package/node-monitor/src/monitors/serverHealthMonitor.js +320 -0
  48. package/node-monitor/src/monitors/systemResourceMonitor.js +357 -0
  49. package/node-monitor/src/notifiers/emailNotifier.js +248 -0
  50. package/node-monitor/src/notifiers/notificationManager.js +96 -0
  51. package/node-monitor/src/notifiers/slackNotifier.js +209 -0
  52. package/node-monitor/src/views/dashboard.html +530 -0
  53. package/node-monitor/src/views/health.html +399 -0
  54. package/node-monitor/src/views/metrics.html +406 -0
  55. 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
+