mirror of
https://github.com/louislam/uptime-kuma.git
synced 2025-03-04 16:35:57 +00:00
Merge branch 'feature-slow-response-notification-cards' into feature-slow-response-notification
This commit is contained in:
commit
e4b79139ff
5 changed files with 168 additions and 29 deletions
|
@ -1458,17 +1458,38 @@ class Monitor extends BeanModel {
|
|||
* Send a slow response notification about a monitor
|
||||
* @param {Monitor} monitor The monitor to send a notificaton about
|
||||
* @param {Bean} bean Status information about monitor
|
||||
* @param {string} msg Notification text to be sent
|
||||
* @param {object} slowStats Slow response information
|
||||
* @returns {void}
|
||||
*/
|
||||
static async sendSlowResponseNotification(monitor, bean, msg) {
|
||||
static async sendSlowResponseNotification(monitor, bean, slowStats) {
|
||||
// Send notification
|
||||
const notificationList = await Monitor.getNotificationList(monitor);
|
||||
|
||||
let text;
|
||||
if (bean.pingStatus === NOMINAL) {
|
||||
text = "🚀 Nominal";
|
||||
} else {
|
||||
text = "🐌 Slow";
|
||||
}
|
||||
|
||||
let msg = `[${monitor.name}] [${text}] ${bean.pingMsg}`;
|
||||
|
||||
for (let notification of notificationList) {
|
||||
try {
|
||||
log.debug("monitor", `[${this.name}] Sending to ${notification.name}`);
|
||||
await Notification.send(JSON.parse(notification.config), msg);
|
||||
const heartbeatJSON = bean.toJSON();
|
||||
|
||||
// Override status with SLOW/NOMINAL, add slowStats
|
||||
heartbeatJSON["status"] = bean.pingStatus;
|
||||
heartbeatJSON["calculatedResponse"] = slowStats.calculatedResponse;
|
||||
heartbeatJSON["calculatedThreshold"] = slowStats.calculatedThreshold;
|
||||
heartbeatJSON["slowFor"] = slowStats.slowFor;
|
||||
|
||||
// Also provide the time in server timezone
|
||||
heartbeatJSON["timezone"] = await UptimeKumaServer.getInstance().getTimezone();
|
||||
heartbeatJSON["timezoneOffset"] = UptimeKumaServer.getInstance().getTimezoneOffset();
|
||||
heartbeatJSON["localDateTime"] = dayjs.utc(heartbeatJSON["time"]).tz(heartbeatJSON["timezone"]).format(SQL_DATETIME_FORMAT);
|
||||
|
||||
await Notification.send(JSON.parse(notification.config), msg, await monitor.toJSON(false), heartbeatJSON);
|
||||
} catch (e) {
|
||||
log.error("monitor", `[${this.name}] Cannot send slow response notification to ${notification.name}`);
|
||||
log.error("monitor", e);
|
||||
|
@ -1564,11 +1585,6 @@ class Monitor extends BeanModel {
|
|||
return;
|
||||
}
|
||||
|
||||
// Create stats to append to messages/logs
|
||||
const methodDescription = [ "average", "max" ].includes(method) ? `${method} of last ${windowDuration}s` : method;
|
||||
let msgStats = `Response: ${actualResponseTime}ms (${methodDescription}) | Threshold: ${threshold}ms (${thresholdDescription})`;
|
||||
let pingMsg = `${actualResponseTime}ms resp. (${methodDescription})`;
|
||||
|
||||
// Verify valid response time was calculated
|
||||
if (actualResponseTime === 0 || !Number.isInteger(actualResponseTime)) {
|
||||
log.debug("monitor", `[${this.name}] Failed to calculate valid response time`);
|
||||
|
@ -1581,6 +1597,15 @@ class Monitor extends BeanModel {
|
|||
return;
|
||||
}
|
||||
|
||||
// Create stats to append to messages/logs
|
||||
const methodDescription = [ "average", "max" ].includes(method) ? `${method} of last ${windowDuration}s` : method;
|
||||
let msgStats = `Response: ${actualResponseTime}ms (${methodDescription}) | Threshold: ${threshold}ms (${thresholdDescription})`;
|
||||
const slowStats = {
|
||||
calculatedResponse: `${actualResponseTime}ms (${methodDescription})`,
|
||||
calculatedThreshold: `${threshold}ms (${thresholdDescription})`,
|
||||
slowFor: `${bean.slowResponseCount * monitor.interval}s`,
|
||||
};
|
||||
|
||||
bean.pingThreshold = threshold;
|
||||
|
||||
// Responding normally
|
||||
|
@ -1591,13 +1616,12 @@ class Monitor extends BeanModel {
|
|||
} else {
|
||||
msgStats += ` | Slow for: ${bean.slowResponseCount * monitor.interval}s`;
|
||||
log.debug("monitor", `[${this.name}] Returned to normal response time | ${msgStats}`);
|
||||
let msg = `[${this.name}] Returned to Normal Response Time \n${msgStats}`;
|
||||
Monitor.sendSlowResponseNotification(monitor, bean, msg);
|
||||
|
||||
// Mark important (SLOW -> NOMINAL)
|
||||
pingMsg += ` < ${threshold}ms`;
|
||||
bean.pingImportant = true;
|
||||
bean.pingMsg = pingMsg;
|
||||
bean.pingMsg = `Returned to Normal Response Time \n${msgStats}`;
|
||||
|
||||
Monitor.sendSlowResponseNotification(monitor, bean, slowStats);
|
||||
}
|
||||
|
||||
// Reset slow response count
|
||||
|
@ -1610,28 +1634,29 @@ class Monitor extends BeanModel {
|
|||
|
||||
// Always send first notification
|
||||
if (bean.slowResponseCount === 1) {
|
||||
log.debug("monitor", `[${this.name}] Responded slowly, sending notification | ${msgStats}`);
|
||||
let msg = `[${this.name}] Responded Slowly \n${msgStats}`;
|
||||
Monitor.sendSlowResponseNotification(monitor, bean, msg);
|
||||
log.debug("monitor", `[${this.name}] Responded slow, sending notification | ${msgStats}`);
|
||||
|
||||
// Mark important (NOMINAL -> SLOW)
|
||||
pingMsg += ` > ${threshold}ms`;
|
||||
bean.pingImportant = true;
|
||||
bean.pingMsg = pingMsg;
|
||||
bean.pingMsg = `Responded Slow \n${msgStats}`;
|
||||
|
||||
Monitor.sendSlowResponseNotification(monitor, bean, slowStats);
|
||||
|
||||
// Send notification every x times
|
||||
} else if (this.slowResponseNotificationResendInterval > 0) {
|
||||
if (((bean.slowResponseCount) % this.slowResponseNotificationResendInterval) === 0) {
|
||||
// Send notification again, because we are still responding slow
|
||||
msgStats += ` | Slow for: ${bean.slowResponseCount * monitor.interval}s`;
|
||||
log.debug("monitor", `[${this.name}] Still responding slowly, sendSlowResponseNotification again | ${msgStats}`);
|
||||
let msg = `[${this.name}] Still Responding Slowly \n${msgStats}`;
|
||||
Monitor.sendSlowResponseNotification(monitor, bean, msg);
|
||||
log.debug("monitor", `[${this.name}] Still responding slow, sendSlowResponseNotification again | ${msgStats}`);
|
||||
|
||||
bean.pingMsg = `Still Responding Slow \n${msgStats}`;
|
||||
|
||||
Monitor.sendSlowResponseNotification(monitor, bean, slowStats);
|
||||
} else {
|
||||
log.debug("monitor", `[${this.name}] Still responding slowly, waiting for resend interal | ${msgStats}`);
|
||||
log.debug("monitor", `[${this.name}] Still responding slow, waiting for resend interal | ${msgStats}`);
|
||||
}
|
||||
} else {
|
||||
log.debug("monitor", `[${this.name}] Still responding slowly, but resend is disabled | ${msgStats}`);
|
||||
log.debug("monitor", `[${this.name}] Still responding slow, but resend is disabled | ${msgStats}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
const NotificationProvider = require("./notification-provider");
|
||||
const axios = require("axios");
|
||||
const { DOWN, UP } = require("../../src/util");
|
||||
const { DOWN, UP, SLOW, NOMINAL } = require("../../src/util");
|
||||
|
||||
class Discord extends NotificationProvider {
|
||||
|
||||
name = "discord";
|
||||
supportSlowNotifications = true;
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
|
@ -115,12 +116,93 @@ class Discord extends NotificationProvider {
|
|||
|
||||
await axios.post(notification.discordWebhookUrl, discordupdata);
|
||||
return okMsg;
|
||||
} else if (heartbeatJSON["status"] === SLOW) {
|
||||
let discordslowdata = {
|
||||
username: discordDisplayName,
|
||||
embeds: [{
|
||||
title: "🐌 Your service " + monitorJSON["name"] + " responded slow. 🐌",
|
||||
color: 16761095,
|
||||
timestamp: heartbeatJSON["time"],
|
||||
fields: [
|
||||
{
|
||||
name: "Service Name",
|
||||
value: monitorJSON["name"],
|
||||
},
|
||||
{
|
||||
name: monitorJSON["type"] === "push" ? "Service Type" : "Service URL",
|
||||
value: monitorJSON["type"] === "push" ? "Heartbeat" : address,
|
||||
},
|
||||
{
|
||||
name: `Time (${heartbeatJSON["timezone"]})`,
|
||||
value: heartbeatJSON["localDateTime"],
|
||||
},
|
||||
{
|
||||
name: "Ping",
|
||||
value: heartbeatJSON["calculatedResponse"],
|
||||
},
|
||||
{
|
||||
name: "Threshold",
|
||||
value: heartbeatJSON["calculatedThreshold"],
|
||||
},
|
||||
],
|
||||
}],
|
||||
};
|
||||
|
||||
if (notification.discordPrefixMessage) {
|
||||
discordslowdata.content = notification.discordPrefixMessage;
|
||||
}
|
||||
|
||||
await axios.post(notification.discordWebhookUrl, discordslowdata);
|
||||
return okMsg;
|
||||
} else if (heartbeatJSON["status"] === NOMINAL) {
|
||||
let discordnominaldata = {
|
||||
username: discordDisplayName,
|
||||
embeds: [{
|
||||
title: "🚀 Your service " + monitorJSON["name"] + " is responding normally! 🚀",
|
||||
color: 65280,
|
||||
timestamp: heartbeatJSON["time"],
|
||||
fields: [
|
||||
{
|
||||
name: "Service Name",
|
||||
value: monitorJSON["name"],
|
||||
},
|
||||
{
|
||||
name: monitorJSON["type"] === "push" ? "Service Type" : "Service URL",
|
||||
value: monitorJSON["type"] === "push" ? "Heartbeat" : address,
|
||||
},
|
||||
{
|
||||
name: `Time (${heartbeatJSON["timezone"]})`,
|
||||
value: heartbeatJSON["localDateTime"],
|
||||
},
|
||||
{
|
||||
name: "Ping",
|
||||
value: heartbeatJSON["calculatedResponse"],
|
||||
},
|
||||
{
|
||||
name: "Threshold",
|
||||
value: heartbeatJSON["calculatedThreshold"],
|
||||
},
|
||||
{
|
||||
name: "Slow For",
|
||||
value: heartbeatJSON["slowFor"],
|
||||
},
|
||||
],
|
||||
}],
|
||||
};
|
||||
|
||||
if (notification.discordPrefixMessage) {
|
||||
discordnominaldata.content = notification.discordPrefixMessage;
|
||||
}
|
||||
|
||||
await axios.post(notification.discordWebhookUrl, discordnominaldata);
|
||||
return okMsg;
|
||||
} else {
|
||||
this.throwGeneralAxiosError("Not sure why we're here");
|
||||
}
|
||||
} catch (error) {
|
||||
this.throwGeneralAxiosError(error);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = Discord;
|
||||
|
|
|
@ -6,6 +6,12 @@ class NotificationProvider {
|
|||
*/
|
||||
name = undefined;
|
||||
|
||||
/**
|
||||
* Does the notification provider support slow response notifications?
|
||||
* @type {boolean}
|
||||
*/
|
||||
supportSlowNotifications = false;
|
||||
|
||||
/**
|
||||
* Send a notification
|
||||
* @param {BeanModel} notification Notification to send
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
const NotificationProvider = require("./notification-provider");
|
||||
const axios = require("axios");
|
||||
const { setSettings, setting } = require("../util-server");
|
||||
const { getMonitorRelativeURL, UP } = require("../../src/util");
|
||||
const { getMonitorRelativeURL, UP, DOWN, NOMINAL, SLOW } = require("../../src/util");
|
||||
|
||||
class Slack extends NotificationProvider {
|
||||
|
||||
name = "slack";
|
||||
supportSlowNotifications = true;
|
||||
|
||||
/**
|
||||
* Deprecated property notification.slackbutton
|
||||
|
@ -50,6 +51,23 @@ class Slack extends NotificationProvider {
|
|||
}
|
||||
|
||||
const textMsg = "Uptime Kuma Alert";
|
||||
|
||||
let color;
|
||||
switch (heartbeatJSON["status"]) {
|
||||
case UP:
|
||||
case NOMINAL:
|
||||
color = "#2eb886";
|
||||
break;
|
||||
case SLOW:
|
||||
color = "#ffc107";
|
||||
break;
|
||||
case DOWN:
|
||||
color = "#e01e5a";
|
||||
break;
|
||||
default:
|
||||
color = "#0dcaf0";
|
||||
}
|
||||
|
||||
let data = {
|
||||
"text": `${textMsg}\n${msg}`,
|
||||
"channel": notification.slackchannel,
|
||||
|
@ -57,7 +75,7 @@ class Slack extends NotificationProvider {
|
|||
"icon_emoji": notification.slackiconemo,
|
||||
"attachments": [
|
||||
{
|
||||
"color": (heartbeatJSON["status"] === UP) ? "#2eb886" : "#e01e5a",
|
||||
"color": color,
|
||||
"blocks": [
|
||||
{
|
||||
"type": "header",
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
const { R } = require("redbean-node");
|
||||
const { log } = require("../src/util");
|
||||
const { log, SLOW, NOMINAL } = require("../src/util");
|
||||
const Alerta = require("./notification-providers/alerta");
|
||||
const AlertNow = require("./notification-providers/alertnow");
|
||||
const AliyunSms = require("./notification-providers/aliyun-sms");
|
||||
|
@ -149,7 +149,15 @@ class Notification {
|
|||
*/
|
||||
static async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
|
||||
if (this.providerList[notification.type]) {
|
||||
if ((heartbeatJSON?.status === SLOW || heartbeatJSON?.status === NOMINAL) && !this.providerList[notification.type].supportSlowNotifications) {
|
||||
// This is a SLOW/NOMINAL notification where the provider does NOT support card notificatons yet
|
||||
// TODO Ideally, this goes away once all the notification providers support slow response notification cards
|
||||
log.debug("notification", `${notification.type} does not support card notifications for SLOW/NOMINAL events yet. Sending plain text message.`);
|
||||
return this.providerList[notification.type].send(notification, msg);
|
||||
} else {
|
||||
return this.providerList[notification.type].send(notification, msg, monitorJSON, heartbeatJSON);
|
||||
}
|
||||
|
||||
} else {
|
||||
throw new Error("Notification type is not supported");
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue