traxx
Version:
Asynchronous route-level analytics for Express.js. Tracks latency, request data, and errors. Supports notifications via Teams, Slack, and Google Chat. MongoDB + BullMQ + Redis.
189 lines (168 loc) • 4.29 kB
JavaScript
const https = require("https");
const dayjs = require("dayjs");
/**
* Send a notification to Slack
* @param {Object} options - Notification options
* @param {string} options.webhookUrl - Slack webhook URL
* @param {Object} data - Request data that triggered the notification
* @returns {Promise<void>}
*/
async function sendNotification(options, data) {
if (!options.webhookUrl) {
throw new Error("Slack webhook URL is required");
}
const {
route,
method,
statusCode,
latency,
timestamp,
error,
requestBody,
requestParams,
requestQuery,
responseBody,
customFields,
ipAddress,
} = data;
// Format timestamp
const formattedTimestamp = dayjs(timestamp).format("DD-MM-YYYY, HH:mm:ss");
const color = statusCode >= 400 ? "#FF0000" : "#36a64f";
const fields = [
{
title: "Status Code",
value: statusCode.toString(),
short: true,
},
{
title: "Method",
value: method,
short: true,
},
{
title: "Route",
value: route,
short: true,
},
{
title: "Latency",
value: `${latency}ms`,
short: true,
},
{
title: "Timestamp",
value: formattedTimestamp,
short: false,
},
];
// Add IP address if available
if (ipAddress) {
fields.push({
title: "IP Address",
value: ipAddress,
short: true,
});
}
// Add error information if available
if (error && error.message) {
fields.push({
title: "Error",
value: error.message,
short: false,
});
}
const payload = {
attachments: [
{
color,
pretext: `Traxx Monitoring Alert: ${statusCode} Status Code`,
title: `${method} ${route}`,
fields: fields,
footer: "Traxx Monitoring",
},
],
};
// Add error stack as a separate attachment if available
if (error && error.stack) {
payload.attachments.push({
color,
title: "Error Stack",
text: "```\n" + error.stack + "\n```",
footer: "Traxx Monitoring",
});
}
// Add custom fields if available
if (customFields && Object.keys(customFields).length > 0) {
payload.attachments.push({
color,
title: "Custom Fields",
text: "```\n" + JSON.stringify(customFields, null, 2) + "\n```",
footer: "Traxx Monitoring",
});
}
// Add request data
if (requestParams && Object.keys(requestParams).length > 0) {
payload.attachments.push({
color,
title: "Request Parameters",
text: "```\n" + JSON.stringify(requestParams, null, 2) + "\n```",
footer: "Traxx Monitoring",
});
}
if (requestQuery && Object.keys(requestQuery).length > 0) {
payload.attachments.push({
color,
title: "Request Query",
text: "```\n" + JSON.stringify(requestQuery, null, 2) + "\n```",
footer: "Traxx Monitoring",
});
}
if (requestBody && Object.keys(requestBody).length > 0) {
payload.attachments.push({
color,
title: "Request Body",
text: "```\n" + JSON.stringify(requestBody, null, 2) + "\n```",
footer: "Traxx Monitoring",
});
}
if (responseBody && Object.keys(responseBody).length > 0) {
payload.attachments.push({
color,
title: "Response Body",
text: "```\n" + JSON.stringify(responseBody, null, 2) + "\n```",
footer: "Traxx Monitoring",
});
}
return new Promise((resolve, reject) => {
const url = new URL(options.webhookUrl);
const requestOptions = {
hostname: url.hostname,
path: url.pathname + url.search,
method: "POST",
headers: {
"Content-Type": "application/json",
},
};
const req = https.request(requestOptions, (res) => {
let data = "";
res.on("data", (chunk) => {
data += chunk;
});
res.on("end", () => {
if (res.statusCode >= 200 && res.statusCode < 300) {
resolve();
} else {
reject(
new Error(`Slack notification failed: ${res.statusCode} ${data}`)
);
}
});
});
req.on("error", (error) => {
reject(error);
});
req.write(JSON.stringify(payload));
req.end();
});
}
module.exports = { sendNotification };