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,76 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@projectmd/node-monitor",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Comprehensive monitoring solution for Node.js applications with error tracking, health checks, and multi-channel notifications",
|
|
5
|
+
"main": "src/index.js",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"test": "jest",
|
|
8
|
+
"start": "node examples/express-app.js",
|
|
9
|
+
"prepublishOnly": "echo 'Running pre-publish checks...'",
|
|
10
|
+
"version": "echo 'Version updated'"
|
|
11
|
+
},
|
|
12
|
+
"keywords": [
|
|
13
|
+
"monitoring",
|
|
14
|
+
"health-check",
|
|
15
|
+
"error-tracking",
|
|
16
|
+
"notifications",
|
|
17
|
+
"alerts",
|
|
18
|
+
"nodejs",
|
|
19
|
+
"express",
|
|
20
|
+
"logging",
|
|
21
|
+
"winston",
|
|
22
|
+
"error-handler",
|
|
23
|
+
"health-monitoring",
|
|
24
|
+
"system-monitoring",
|
|
25
|
+
"email-notifications",
|
|
26
|
+
"slack-notifications"
|
|
27
|
+
],
|
|
28
|
+
"author": "ProjectMD",
|
|
29
|
+
"license": "MIT",
|
|
30
|
+
"repository": {
|
|
31
|
+
"type": "git",
|
|
32
|
+
"url": "git+https://github.com/projectmd/node-monitor.git"
|
|
33
|
+
},
|
|
34
|
+
"bugs": {
|
|
35
|
+
"url": "https://github.com/projectmd/node-monitor/issues"
|
|
36
|
+
},
|
|
37
|
+
"homepage": "https://github.com/projectmd/node-monitor#readme",
|
|
38
|
+
"dependencies": {
|
|
39
|
+
"winston": "^3.11.0",
|
|
40
|
+
"winston-daily-rotate-file": "^4.7.1",
|
|
41
|
+
"nodemailer": "^6.9.7",
|
|
42
|
+
"@slack/webhook": "^7.0.2",
|
|
43
|
+
"axios": "^1.6.2",
|
|
44
|
+
"node-cron": "^3.0.3"
|
|
45
|
+
},
|
|
46
|
+
"peerDependencies": {
|
|
47
|
+
"express": "^4.18.0 || ^5.0.0",
|
|
48
|
+
"mongoose": "^7.0.0 || ^8.0.0",
|
|
49
|
+
"pg": "^8.11.0",
|
|
50
|
+
"mysql2": "^3.6.0",
|
|
51
|
+
"ioredis": "^5.3.0"
|
|
52
|
+
},
|
|
53
|
+
"peerDependenciesMeta": {
|
|
54
|
+
"express": {
|
|
55
|
+
"optional": true
|
|
56
|
+
},
|
|
57
|
+
"mongoose": {
|
|
58
|
+
"optional": true
|
|
59
|
+
},
|
|
60
|
+
"pg": {
|
|
61
|
+
"optional": true
|
|
62
|
+
},
|
|
63
|
+
"mysql2": {
|
|
64
|
+
"optional": true
|
|
65
|
+
},
|
|
66
|
+
"ioredis": {
|
|
67
|
+
"optional": true
|
|
68
|
+
}
|
|
69
|
+
},
|
|
70
|
+
"devDependencies": {
|
|
71
|
+
"jest": "^29.7.0"
|
|
72
|
+
},
|
|
73
|
+
"engines": {
|
|
74
|
+
"node": ">=14.0.0"
|
|
75
|
+
}
|
|
76
|
+
}
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Pre-publish validation script
|
|
5
|
+
* Run this before publishing to npm to ensure everything is ready
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const fs = require('fs');
|
|
9
|
+
const path = require('path');
|
|
10
|
+
|
|
11
|
+
console.log('🔍 Running pre-publish checks...\n');
|
|
12
|
+
|
|
13
|
+
let hasErrors = false;
|
|
14
|
+
let hasWarnings = false;
|
|
15
|
+
|
|
16
|
+
// Check 1: package.json exists and is valid
|
|
17
|
+
console.log('📦 Checking package.json...');
|
|
18
|
+
try {
|
|
19
|
+
const pkg = require('./package.json');
|
|
20
|
+
|
|
21
|
+
if (!pkg.name) {
|
|
22
|
+
console.error(' ❌ package.json missing "name" field');
|
|
23
|
+
hasErrors = true;
|
|
24
|
+
} else {
|
|
25
|
+
console.log(` ✅ Package name: ${pkg.name}`);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (!pkg.version) {
|
|
29
|
+
console.error(' ❌ package.json missing "version" field');
|
|
30
|
+
hasErrors = true;
|
|
31
|
+
} else {
|
|
32
|
+
console.log(` ✅ Version: ${pkg.version}`);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (!pkg.description) {
|
|
36
|
+
console.warn(' ⚠️ package.json missing "description" field');
|
|
37
|
+
hasWarnings = true;
|
|
38
|
+
} else {
|
|
39
|
+
console.log(` ✅ Description: ${pkg.description.substring(0, 50)}...`);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (!pkg.main) {
|
|
43
|
+
console.error(' ❌ package.json missing "main" field');
|
|
44
|
+
hasErrors = true;
|
|
45
|
+
} else {
|
|
46
|
+
console.log(` ✅ Main entry point: ${pkg.main}`);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (!pkg.repository) {
|
|
50
|
+
console.warn(' ⚠️ package.json missing "repository" field');
|
|
51
|
+
hasWarnings = true;
|
|
52
|
+
} else {
|
|
53
|
+
console.log(` ✅ Repository: ${pkg.repository.url}`);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (!pkg.license) {
|
|
57
|
+
console.warn(' ⚠️ package.json missing "license" field');
|
|
58
|
+
hasWarnings = true;
|
|
59
|
+
} else {
|
|
60
|
+
console.log(` ✅ License: ${pkg.license}`);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (!pkg.keywords || pkg.keywords.length === 0) {
|
|
64
|
+
console.warn(' ⚠️ package.json has no keywords (helps with discoverability)');
|
|
65
|
+
hasWarnings = true;
|
|
66
|
+
} else {
|
|
67
|
+
console.log(` ✅ Keywords: ${pkg.keywords.length} keywords defined`);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
} catch (error) {
|
|
71
|
+
console.error(' ❌ Error reading package.json:', error.message);
|
|
72
|
+
hasErrors = true;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
console.log('');
|
|
76
|
+
|
|
77
|
+
// Check 2: Main entry point exists
|
|
78
|
+
console.log('📄 Checking main entry point...');
|
|
79
|
+
const mainFile = path.join(__dirname, 'src/index.js');
|
|
80
|
+
if (fs.existsSync(mainFile)) {
|
|
81
|
+
console.log(' ✅ src/index.js exists');
|
|
82
|
+
} else {
|
|
83
|
+
console.error(' ❌ src/index.js not found');
|
|
84
|
+
hasErrors = true;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
console.log('');
|
|
88
|
+
|
|
89
|
+
// Check 3: README exists
|
|
90
|
+
console.log('📖 Checking README...');
|
|
91
|
+
const readmeFile = path.join(__dirname, 'README.md');
|
|
92
|
+
if (fs.existsSync(readmeFile)) {
|
|
93
|
+
const readmeContent = fs.readFileSync(readmeFile, 'utf8');
|
|
94
|
+
console.log(' ✅ README.md exists');
|
|
95
|
+
|
|
96
|
+
if (readmeContent.length < 100) {
|
|
97
|
+
console.warn(' ⚠️ README.md seems very short');
|
|
98
|
+
hasWarnings = true;
|
|
99
|
+
} else {
|
|
100
|
+
console.log(` ✅ README.md has ${readmeContent.length} characters`);
|
|
101
|
+
}
|
|
102
|
+
} else {
|
|
103
|
+
console.error(' ❌ README.md not found');
|
|
104
|
+
hasErrors = true;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
console.log('');
|
|
108
|
+
|
|
109
|
+
// Check 4: LICENSE exists
|
|
110
|
+
console.log('⚖️ Checking LICENSE...');
|
|
111
|
+
const licenseFile = path.join(__dirname, 'LICENSE');
|
|
112
|
+
if (fs.existsSync(licenseFile)) {
|
|
113
|
+
console.log(' ✅ LICENSE file exists');
|
|
114
|
+
} else {
|
|
115
|
+
console.error(' ❌ LICENSE file not found');
|
|
116
|
+
hasErrors = true;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
console.log('');
|
|
120
|
+
|
|
121
|
+
// Check 5: .npmignore exists
|
|
122
|
+
console.log('🚫 Checking .npmignore...');
|
|
123
|
+
const npmignoreFile = path.join(__dirname, '.npmignore');
|
|
124
|
+
if (fs.existsSync(npmignoreFile)) {
|
|
125
|
+
console.log(' ✅ .npmignore exists');
|
|
126
|
+
} else {
|
|
127
|
+
console.warn(' ⚠️ .npmignore not found (will use .gitignore)');
|
|
128
|
+
hasWarnings = true;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
console.log('');
|
|
132
|
+
|
|
133
|
+
// Check 6: No .env files in root
|
|
134
|
+
console.log('🔐 Checking for sensitive files...');
|
|
135
|
+
const envFile = path.join(__dirname, '.env');
|
|
136
|
+
if (fs.existsSync(envFile)) {
|
|
137
|
+
console.warn(' ⚠️ .env file found (make sure it\'s in .npmignore)');
|
|
138
|
+
hasWarnings = true;
|
|
139
|
+
} else {
|
|
140
|
+
console.log(' ✅ No .env file in root');
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
console.log('');
|
|
144
|
+
|
|
145
|
+
// Check 7: Source files exist
|
|
146
|
+
console.log('📁 Checking source files...');
|
|
147
|
+
const srcDir = path.join(__dirname, 'src');
|
|
148
|
+
if (fs.existsSync(srcDir)) {
|
|
149
|
+
const srcFiles = fs.readdirSync(srcDir, { recursive: true });
|
|
150
|
+
const jsFiles = srcFiles.filter(f => f.endsWith('.js'));
|
|
151
|
+
console.log(` ✅ src/ directory exists with ${jsFiles.length} JavaScript files`);
|
|
152
|
+
} else {
|
|
153
|
+
console.error(' ❌ src/ directory not found');
|
|
154
|
+
hasErrors = true;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
console.log('');
|
|
158
|
+
|
|
159
|
+
// Check 8: node_modules size warning
|
|
160
|
+
console.log('📦 Checking dependencies...');
|
|
161
|
+
const nodeModulesDir = path.join(__dirname, 'node_modules');
|
|
162
|
+
if (fs.existsSync(nodeModulesDir)) {
|
|
163
|
+
console.log(' ✅ Dependencies installed');
|
|
164
|
+
console.log(' ℹ️ Note: node_modules/ will be excluded from npm package');
|
|
165
|
+
} else {
|
|
166
|
+
console.warn(' ⚠️ node_modules/ not found - run "npm install" first');
|
|
167
|
+
hasWarnings = true;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
console.log('');
|
|
171
|
+
|
|
172
|
+
// Summary
|
|
173
|
+
console.log('═══════════════════════════════════════════════════════');
|
|
174
|
+
if (hasErrors) {
|
|
175
|
+
console.log('❌ FAILED - Please fix the errors above before publishing');
|
|
176
|
+
process.exit(1);
|
|
177
|
+
} else if (hasWarnings) {
|
|
178
|
+
console.log('⚠️ PASSED WITH WARNINGS - Review warnings above');
|
|
179
|
+
console.log('');
|
|
180
|
+
console.log('✅ You can proceed with publishing, but consider fixing warnings');
|
|
181
|
+
console.log('');
|
|
182
|
+
console.log('Next steps:');
|
|
183
|
+
console.log(' 1. npm login');
|
|
184
|
+
console.log(' 2. npm publish --dry-run --access public');
|
|
185
|
+
console.log(' 3. npm publish --access public');
|
|
186
|
+
process.exit(0);
|
|
187
|
+
} else {
|
|
188
|
+
console.log('✅ ALL CHECKS PASSED!');
|
|
189
|
+
console.log('');
|
|
190
|
+
console.log('Your package is ready to publish! 🚀');
|
|
191
|
+
console.log('');
|
|
192
|
+
console.log('Next steps:');
|
|
193
|
+
console.log(' 1. npm login');
|
|
194
|
+
console.log(' 2. npm publish --dry-run --access public (test run)');
|
|
195
|
+
console.log(' 3. npm publish --access public (actual publish)');
|
|
196
|
+
console.log('');
|
|
197
|
+
console.log('See PUBLISHING_GUIDE.md for detailed instructions.');
|
|
198
|
+
process.exit(0);
|
|
199
|
+
}
|
|
200
|
+
|
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Monitoring Configuration Manager
|
|
3
|
+
* Handles configuration with environment variable support and validation
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
class MonitoringConfig {
|
|
7
|
+
constructor(userConfig = {}) {
|
|
8
|
+
this.config = this.mergeWithDefaults(userConfig);
|
|
9
|
+
this.validateConfig();
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Default configuration values
|
|
14
|
+
*/
|
|
15
|
+
getDefaults() {
|
|
16
|
+
return {
|
|
17
|
+
// Monitoring intervals (in milliseconds)
|
|
18
|
+
intervals: {
|
|
19
|
+
health: parseInt(process.env.MONITOR_HEALTH_INTERVAL) || 60000, // 1 minute
|
|
20
|
+
system: parseInt(process.env.MONITOR_SYSTEM_INTERVAL) || 300000, // 5 minutes
|
|
21
|
+
database: parseInt(process.env.MONITOR_DATABASE_INTERVAL) || 120000 // 2 minutes
|
|
22
|
+
},
|
|
23
|
+
|
|
24
|
+
// Alert thresholds
|
|
25
|
+
thresholds: {
|
|
26
|
+
cpu: parseInt(process.env.MONITOR_CPU_THRESHOLD) || 80, // percentage
|
|
27
|
+
memory: parseInt(process.env.MONITOR_MEMORY_THRESHOLD) || 90, // percentage
|
|
28
|
+
errorRate: parseInt(process.env.MONITOR_ERROR_RATE_THRESHOLD) || 50, // errors per minute
|
|
29
|
+
responseTime: parseInt(process.env.MONITOR_RESPONSE_TIME_THRESHOLD) || 5000, // milliseconds
|
|
30
|
+
consecutiveFailures: parseInt(process.env.MONITOR_CONSECUTIVE_FAILURES) || 3
|
|
31
|
+
},
|
|
32
|
+
|
|
33
|
+
// Notification settings
|
|
34
|
+
notifications: {
|
|
35
|
+
enabled: process.env.MONITOR_NOTIFICATIONS_ENABLED !== 'false',
|
|
36
|
+
cooldown: parseInt(process.env.MONITOR_NOTIFICATION_COOLDOWN) || 300000, // 5 minutes
|
|
37
|
+
|
|
38
|
+
email: {
|
|
39
|
+
enabled: process.env.MONITOR_EMAIL_ENABLED === 'true',
|
|
40
|
+
host: process.env.MONITOR_EMAIL_HOST || 'smtp.gmail.com',
|
|
41
|
+
port: parseInt(process.env.MONITOR_EMAIL_PORT) || 587,
|
|
42
|
+
secure: process.env.MONITOR_EMAIL_SECURE === 'true',
|
|
43
|
+
auth: {
|
|
44
|
+
user: process.env.MONITOR_EMAIL_USER || '',
|
|
45
|
+
pass: process.env.MONITOR_EMAIL_PASS || ''
|
|
46
|
+
},
|
|
47
|
+
from: process.env.MONITOR_EMAIL_FROM || '',
|
|
48
|
+
recipients: process.env.MONITOR_EMAIL_RECIPIENTS
|
|
49
|
+
? process.env.MONITOR_EMAIL_RECIPIENTS.split(',')
|
|
50
|
+
: []
|
|
51
|
+
},
|
|
52
|
+
|
|
53
|
+
slack: {
|
|
54
|
+
enabled: process.env.MONITOR_SLACK_ENABLED === 'true',
|
|
55
|
+
webhook: process.env.MONITOR_SLACK_WEBHOOK || '',
|
|
56
|
+
channel: process.env.MONITOR_SLACK_CHANNEL || '#monitoring',
|
|
57
|
+
username: process.env.MONITOR_SLACK_USERNAME || 'Node Monitor'
|
|
58
|
+
},
|
|
59
|
+
|
|
60
|
+
sms: {
|
|
61
|
+
enabled: process.env.MONITOR_SMS_ENABLED === 'true',
|
|
62
|
+
provider: process.env.MONITOR_SMS_PROVIDER || 'twilio',
|
|
63
|
+
accountSid: process.env.MONITOR_SMS_ACCOUNT_SID || '',
|
|
64
|
+
authToken: process.env.MONITOR_SMS_AUTH_TOKEN || '',
|
|
65
|
+
from: process.env.MONITOR_SMS_FROM || '',
|
|
66
|
+
recipients: process.env.MONITOR_SMS_RECIPIENTS
|
|
67
|
+
? process.env.MONITOR_SMS_RECIPIENTS.split(',')
|
|
68
|
+
: []
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
|
|
72
|
+
// Logging configuration
|
|
73
|
+
logging: {
|
|
74
|
+
enabled: process.env.MONITOR_LOGGING_ENABLED !== 'false',
|
|
75
|
+
level: process.env.MONITOR_LOG_LEVEL || 'info',
|
|
76
|
+
directory: process.env.MONITOR_LOG_DIRECTORY || './logs',
|
|
77
|
+
maxFiles: process.env.MONITOR_LOG_MAX_FILES || '14d',
|
|
78
|
+
maxSize: process.env.MONITOR_LOG_MAX_SIZE || '20m',
|
|
79
|
+
format: process.env.MONITOR_LOG_FORMAT || 'json', // 'json' or 'simple'
|
|
80
|
+
console: process.env.MONITOR_LOG_CONSOLE !== 'false'
|
|
81
|
+
},
|
|
82
|
+
|
|
83
|
+
// Health check configuration
|
|
84
|
+
healthCheck: {
|
|
85
|
+
enabled: process.env.MONITOR_HEALTH_CHECK_ENABLED !== 'false',
|
|
86
|
+
endpoint: process.env.MONITOR_HEALTH_ENDPOINT || '/health',
|
|
87
|
+
timeout: parseInt(process.env.MONITOR_HEALTH_TIMEOUT) || 5000
|
|
88
|
+
},
|
|
89
|
+
|
|
90
|
+
// Database monitoring
|
|
91
|
+
database: {
|
|
92
|
+
enabled: process.env.MONITOR_DATABASE_ENABLED !== 'false',
|
|
93
|
+
types: [], // Will be populated based on connections provided
|
|
94
|
+
queryTimeout: parseInt(process.env.MONITOR_DB_QUERY_TIMEOUT) || 5000
|
|
95
|
+
},
|
|
96
|
+
|
|
97
|
+
// System monitoring
|
|
98
|
+
system: {
|
|
99
|
+
enabled: process.env.MONITOR_SYSTEM_ENABLED !== 'false',
|
|
100
|
+
trackCpu: process.env.MONITOR_TRACK_CPU !== 'false',
|
|
101
|
+
trackMemory: process.env.MONITOR_TRACK_MEMORY !== 'false',
|
|
102
|
+
trackEventLoop: process.env.MONITOR_TRACK_EVENT_LOOP === 'true'
|
|
103
|
+
},
|
|
104
|
+
|
|
105
|
+
// Error tracking
|
|
106
|
+
errorTracking: {
|
|
107
|
+
enabled: process.env.MONITOR_ERROR_TRACKING_ENABLED !== 'false',
|
|
108
|
+
captureStackTrace: process.env.MONITOR_CAPTURE_STACK_TRACE !== 'false',
|
|
109
|
+
sanitizeSensitiveData: process.env.MONITOR_SANITIZE_DATA !== 'false',
|
|
110
|
+
sensitiveFields: ['password', 'token', 'apiKey', 'secret', 'authorization']
|
|
111
|
+
},
|
|
112
|
+
|
|
113
|
+
// Application metadata
|
|
114
|
+
app: {
|
|
115
|
+
name: process.env.MONITOR_APP_NAME || process.env.npm_package_name || 'nodejs-app',
|
|
116
|
+
environment: process.env.NODE_ENV || 'development',
|
|
117
|
+
version: process.env.MONITOR_APP_VERSION || process.env.npm_package_version || '1.0.0',
|
|
118
|
+
hostname: process.env.HOSTNAME || require('os').hostname()
|
|
119
|
+
}
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Deep merge user config with defaults
|
|
125
|
+
*/
|
|
126
|
+
mergeWithDefaults(userConfig) {
|
|
127
|
+
const defaults = this.getDefaults();
|
|
128
|
+
return this.deepMerge(defaults, userConfig);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Deep merge utility
|
|
133
|
+
*/
|
|
134
|
+
deepMerge(target, source) {
|
|
135
|
+
const output = { ...target };
|
|
136
|
+
|
|
137
|
+
if (this.isObject(target) && this.isObject(source)) {
|
|
138
|
+
Object.keys(source).forEach(key => {
|
|
139
|
+
if (this.isObject(source[key])) {
|
|
140
|
+
if (!(key in target)) {
|
|
141
|
+
output[key] = source[key];
|
|
142
|
+
} else {
|
|
143
|
+
output[key] = this.deepMerge(target[key], source[key]);
|
|
144
|
+
}
|
|
145
|
+
} else {
|
|
146
|
+
output[key] = source[key];
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
return output;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Check if value is an object
|
|
156
|
+
*/
|
|
157
|
+
isObject(item) {
|
|
158
|
+
return item && typeof item === 'object' && !Array.isArray(item);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Validate configuration
|
|
163
|
+
*/
|
|
164
|
+
validateConfig() {
|
|
165
|
+
const errors = [];
|
|
166
|
+
|
|
167
|
+
// Validate intervals
|
|
168
|
+
if (this.config.intervals.health < 1000) {
|
|
169
|
+
errors.push('Health check interval must be at least 1000ms');
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// Validate thresholds
|
|
173
|
+
if (this.config.thresholds.cpu < 0 || this.config.thresholds.cpu > 100) {
|
|
174
|
+
errors.push('CPU threshold must be between 0 and 100');
|
|
175
|
+
}
|
|
176
|
+
if (this.config.thresholds.memory < 0 || this.config.thresholds.memory > 100) {
|
|
177
|
+
errors.push('Memory threshold must be between 0 and 100');
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// Validate email config if enabled
|
|
181
|
+
if (this.config.notifications.email.enabled) {
|
|
182
|
+
if (!this.config.notifications.email.auth.user || !this.config.notifications.email.auth.pass) {
|
|
183
|
+
errors.push('Email notifications enabled but credentials not provided');
|
|
184
|
+
}
|
|
185
|
+
if (this.config.notifications.email.recipients.length === 0) {
|
|
186
|
+
errors.push('Email notifications enabled but no recipients specified');
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// Validate Slack config if enabled
|
|
191
|
+
if (this.config.notifications.slack.enabled) {
|
|
192
|
+
if (!this.config.notifications.slack.webhook) {
|
|
193
|
+
errors.push('Slack notifications enabled but webhook URL not provided');
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// Validate SMS config if enabled
|
|
198
|
+
if (this.config.notifications.sms.enabled) {
|
|
199
|
+
if (!this.config.notifications.sms.accountSid || !this.config.notifications.sms.authToken) {
|
|
200
|
+
errors.push('SMS notifications enabled but credentials not provided');
|
|
201
|
+
}
|
|
202
|
+
if (this.config.notifications.sms.recipients.length === 0) {
|
|
203
|
+
errors.push('SMS notifications enabled but no recipients specified');
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
if (errors.length > 0) {
|
|
208
|
+
throw new Error(`Configuration validation failed:\n${errors.join('\n')}`);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Get configuration value by path
|
|
214
|
+
*/
|
|
215
|
+
get(path) {
|
|
216
|
+
return path.split('.').reduce((obj, key) => obj?.[key], this.config);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Set configuration value by path
|
|
221
|
+
*/
|
|
222
|
+
set(path, value) {
|
|
223
|
+
const keys = path.split('.');
|
|
224
|
+
const lastKey = keys.pop();
|
|
225
|
+
const target = keys.reduce((obj, key) => obj[key], this.config);
|
|
226
|
+
target[lastKey] = value;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Get all configuration
|
|
231
|
+
*/
|
|
232
|
+
getAll() {
|
|
233
|
+
return { ...this.config };
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Get sanitized config (without sensitive data) for logging
|
|
238
|
+
*/
|
|
239
|
+
getSanitized() {
|
|
240
|
+
const sanitized = JSON.parse(JSON.stringify(this.config));
|
|
241
|
+
|
|
242
|
+
// Remove sensitive fields
|
|
243
|
+
if (sanitized.notifications?.email?.auth) {
|
|
244
|
+
sanitized.notifications.email.auth.pass = '***';
|
|
245
|
+
}
|
|
246
|
+
if (sanitized.notifications?.sms) {
|
|
247
|
+
sanitized.notifications.sms.authToken = '***';
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
return sanitized;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
module.exports = MonitoringConfig;
|
|
255
|
+
|