express-watchdog 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 +77 -0
- package/index.js +165 -0
- package/package.json +24 -0
package/README.md
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# express-watchdog
|
|
2
|
+
|
|
3
|
+
A lightweight, zero-dashboard monitoring helper for Express applications. Keep an eye on your APIs and get alerted when things go slow or the server crashes—all without any complex setup!
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
- **Auto-pilot Monitoring:** Automatically detects slow API responses.
|
|
7
|
+
- **Crash Protection:** Listens for process-level errors and unhandled rejections.
|
|
8
|
+
- **Customizable Actions:** Attach your own logic (logging, Slack, Webhooks) when alerts trigger.
|
|
9
|
+
- **Built-in Emailing:** Seamlessly send Gmail alerts to you and your team (CC supported).
|
|
10
|
+
- **Clear Reports:** Beautifully structured `WHERE–WHAT–WHEN` formats.
|
|
11
|
+
- **Safe & Minimal:** Zero impact on your app's performance and minimal dependencies.
|
|
12
|
+
|
|
13
|
+
## Installation
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm install express-watchdog
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Basic Usage
|
|
20
|
+
|
|
21
|
+
Getting started is as simple as adding a few lines to your Express app:
|
|
22
|
+
|
|
23
|
+
```js
|
|
24
|
+
const express = require("express");
|
|
25
|
+
const monitor = require("express-watchdog");
|
|
26
|
+
|
|
27
|
+
const app = express();
|
|
28
|
+
|
|
29
|
+
monitor(app, {
|
|
30
|
+
slowThresholdMs: 2000,
|
|
31
|
+
alertEmail: process.env.ALERT_EMAIL,
|
|
32
|
+
ccEmail: process.env.CC_EMAIL,
|
|
33
|
+
appPassword: process.env.EMAIL_APP_PASSWORD
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
app.listen(3000);
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Custom Functions (Powerful & Flexible!)
|
|
40
|
+
|
|
41
|
+
Want to send alerts to Slack, log to a database, or trigger a webhook? You can easily attach your own handlers!
|
|
42
|
+
|
|
43
|
+
When you provide `onSlow` or `onCrash`, **express-watchdog** will call your function instead of sending the default email.
|
|
44
|
+
|
|
45
|
+
```js
|
|
46
|
+
monitor(app, {
|
|
47
|
+
slowThresholdMs: 2000,
|
|
48
|
+
// Your custom logic here!
|
|
49
|
+
onSlow: async ({ where, what, when, path, duration }) => {
|
|
50
|
+
console.log(`Slow API detected at ${path}! Took ${duration}ms.`);
|
|
51
|
+
// Add your Slack integration or database logging here
|
|
52
|
+
},
|
|
53
|
+
onCrash: async ({ where, what, when, message, stack }) => {
|
|
54
|
+
console.error(`Critical Error: ${message}`);
|
|
55
|
+
// Handle the crash gracefully or notify your dev team
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Email Configuration (Optional)
|
|
61
|
+
If you don't provide custom handlers, the watchdog defaults to sending emails via Gmail.
|
|
62
|
+
|
|
63
|
+
Recommended Environment Variables:
|
|
64
|
+
```env
|
|
65
|
+
ALERT_EMAIL=yourgmail@gmail.com
|
|
66
|
+
CC_EMAIL=team@example.com
|
|
67
|
+
EMAIL_APP_PASSWORD=your-app-password
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## How It Works
|
|
71
|
+
1. **Performance Tracking:** Wraps Express requests to measure exactly how long they take.
|
|
72
|
+
2. **Process Monitoring:** Hooks into `uncaughtException` and `unhandledRejection`.
|
|
73
|
+
3. **Smart Alerting:** Triggers your custom functions or sends a detailed email with full context.
|
|
74
|
+
|
|
75
|
+
## License
|
|
76
|
+
ISC
|
|
77
|
+
|
package/index.js
ADDED
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
const nodemailer = require("nodemailer");
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Monitors an Express application for slow APIs and server crashes.
|
|
5
|
+
*
|
|
6
|
+
* @param {import('express').Application} app - The Express application instance.
|
|
7
|
+
* @param {Object} options - Configuration options.
|
|
8
|
+
* @param {number} [options.slowThresholdMs=2000] - Threshold for slow API detection in milliseconds.
|
|
9
|
+
* @param {string} options.alertEmail - Sender email address (Gmail).
|
|
10
|
+
* @param {string} [options.ccEmail] - CC email address.
|
|
11
|
+
* @param {string} options.appPassword - Gmail App Password for authentication.
|
|
12
|
+
* @param {Function} [options.onSlow] - Custom handler for slow API alerts.
|
|
13
|
+
* @param {Function} [options.onCrash] - Custom handler for crash alerts.
|
|
14
|
+
*/
|
|
15
|
+
module.exports = function monitor(app, options = {}) {
|
|
16
|
+
const {
|
|
17
|
+
slowThresholdMs = 2000,
|
|
18
|
+
alertEmail,
|
|
19
|
+
ccEmail,
|
|
20
|
+
appPassword,
|
|
21
|
+
onSlow,
|
|
22
|
+
onCrash
|
|
23
|
+
} = options;
|
|
24
|
+
|
|
25
|
+
// --- Utility: Send Email ---
|
|
26
|
+
const sendEmail = async (subject, body) => {
|
|
27
|
+
try {
|
|
28
|
+
if (!alertEmail || !appPassword) {
|
|
29
|
+
console.warn("[express-watchdog] Missing alertEmail or appPassword. Default email alert skipped.");
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const transporter = nodemailer.createTransport({
|
|
34
|
+
service: "gmail",
|
|
35
|
+
auth: {
|
|
36
|
+
user: alertEmail,
|
|
37
|
+
pass: appPassword
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
const mailOptions = {
|
|
42
|
+
from: alertEmail,
|
|
43
|
+
to: alertEmail,
|
|
44
|
+
cc: ccEmail,
|
|
45
|
+
subject: `[express-watchdog] Alert: ${subject}`,
|
|
46
|
+
text: body
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
await transporter.sendMail(mailOptions);
|
|
50
|
+
} catch (error) {
|
|
51
|
+
console.error("[express-watchdog] Failed to send email alert:", error.message);
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
// --- 1. Detect Slow APIs Middleware ---
|
|
56
|
+
app.use((req, res, next) => {
|
|
57
|
+
try {
|
|
58
|
+
const start = Date.now();
|
|
59
|
+
|
|
60
|
+
// Listen for the response finish event
|
|
61
|
+
res.on("finish", async () => {
|
|
62
|
+
const duration = Date.now() - start;
|
|
63
|
+
|
|
64
|
+
if (duration > slowThresholdMs) {
|
|
65
|
+
const timestamp = new Date().toISOString();
|
|
66
|
+
const payload = {
|
|
67
|
+
path: req.path,
|
|
68
|
+
method: req.method,
|
|
69
|
+
duration,
|
|
70
|
+
timestamp,
|
|
71
|
+
where: req.path,
|
|
72
|
+
what: "Slow API Response",
|
|
73
|
+
when: timestamp
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
if (typeof onSlow === "function") {
|
|
77
|
+
try {
|
|
78
|
+
await onSlow(payload);
|
|
79
|
+
} catch (err) {
|
|
80
|
+
console.error("[express-watchdog] Error in custom onSlow handler:", err.message);
|
|
81
|
+
}
|
|
82
|
+
} else {
|
|
83
|
+
const body = `
|
|
84
|
+
WHERE:
|
|
85
|
+
${payload.where}
|
|
86
|
+
|
|
87
|
+
WHAT:
|
|
88
|
+
${payload.what}
|
|
89
|
+
|
|
90
|
+
WHEN:
|
|
91
|
+
${payload.timestamp}
|
|
92
|
+
|
|
93
|
+
DETAILS:
|
|
94
|
+
- Path (if API): ${payload.path}
|
|
95
|
+
- Method (if API): ${payload.method}
|
|
96
|
+
- Duration (if slow API): ${payload.duration} ms
|
|
97
|
+
`;
|
|
98
|
+
await sendEmail(payload.what, body.trim());
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
} catch (error) {
|
|
103
|
+
console.error("[express-watchdog] Error in slow API middleware:", error.message);
|
|
104
|
+
}
|
|
105
|
+
next();
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
// --- 2. Detect Server Crash ---
|
|
109
|
+
const handleCrash = async (error) => {
|
|
110
|
+
try {
|
|
111
|
+
const timestamp = new Date().toISOString();
|
|
112
|
+
const payload = {
|
|
113
|
+
message: error.message || "Unknown error",
|
|
114
|
+
stack: error.stack || "No stack trace available",
|
|
115
|
+
timestamp,
|
|
116
|
+
where: "Node.js Process",
|
|
117
|
+
what: "Server Crash / Unhandled Error",
|
|
118
|
+
when: timestamp
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
if (typeof onCrash === "function") {
|
|
122
|
+
try {
|
|
123
|
+
await onCrash(payload);
|
|
124
|
+
} catch (err) {
|
|
125
|
+
console.error("[express-watchdog] Error in custom onCrash handler:", err.message);
|
|
126
|
+
}
|
|
127
|
+
} else {
|
|
128
|
+
const body = `
|
|
129
|
+
WHERE:
|
|
130
|
+
${payload.where}
|
|
131
|
+
|
|
132
|
+
WHAT:
|
|
133
|
+
${payload.what}
|
|
134
|
+
|
|
135
|
+
WHEN:
|
|
136
|
+
${payload.timestamp}
|
|
137
|
+
|
|
138
|
+
DETAILS:
|
|
139
|
+
- Error Message (if crash): ${payload.message}
|
|
140
|
+
- Stack Trace (if crash):
|
|
141
|
+
${payload.stack}
|
|
142
|
+
`;
|
|
143
|
+
await sendEmail(payload.what, body.trim());
|
|
144
|
+
}
|
|
145
|
+
} catch (err) {
|
|
146
|
+
console.error("[express-watchdog] Error in crash handler:", err.message);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// Since it's a crash, we might want to log it and let the process die
|
|
150
|
+
// or keep it alive if the user handles it.
|
|
151
|
+
// Usually, uncaughtException should lead to process exit after logging.
|
|
152
|
+
// However, the requirement doesn't explicitly say to exit.
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
process.on("uncaughtException", async (error) => {
|
|
156
|
+
await handleCrash(error);
|
|
157
|
+
// Standard practice is to exit after logging uncaught exception
|
|
158
|
+
// process.exit(1);
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
process.on("unhandledRejection", async (reason) => {
|
|
162
|
+
const error = reason instanceof Error ? reason : new Error(String(reason));
|
|
163
|
+
await handleCrash(error);
|
|
164
|
+
});
|
|
165
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "express-watchdog",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "A lightweight, zero-dashboard monitoring helper for Express applications that detects slow APIs and server crashes.",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"keywords": [
|
|
7
|
+
"express",
|
|
8
|
+
"monitoring",
|
|
9
|
+
"alerts",
|
|
10
|
+
"watchdog",
|
|
11
|
+
"performance"
|
|
12
|
+
],
|
|
13
|
+
"author": "Fayfay",
|
|
14
|
+
"license": "ISC",
|
|
15
|
+
"files": [
|
|
16
|
+
"index.js",
|
|
17
|
+
"README.md",
|
|
18
|
+
"package.json"
|
|
19
|
+
],
|
|
20
|
+
"dependencies": {
|
|
21
|
+
"express": "^5.2.1",
|
|
22
|
+
"nodemailer": "^8.0.1"
|
|
23
|
+
}
|
|
24
|
+
}
|