Skip to content

Instantly share code, notes, and snippets.

@i5okie
Created November 13, 2024 17:11
Show Gist options
  • Save i5okie/e4fc821db95ec253ddf691e3c5bd5e0a to your computer and use it in GitHub Desktop.
Save i5okie/e4fc821db95ec253ddf691e3c5bd5e0a to your computer and use it in GitHub Desktop.
SysDig custom webhook for RocketChat
class Script {
process_incoming_request({ request }) {
const data = request.content;
// Helper function to format timestamp
const formatTimestamp = (timestamp) => {
if (!timestamp || timestamp === "null") return "N/A";
const date = new Date(parseInt(timestamp) / 1000);
return date.toISOString().replace("T", " ").replace("Z", " UTC");
};
// Helper function to extract metric value from metadata
const extractMetricValue = (metadata) => {
if (!metadata || !Array.isArray(metadata) || metadata.length === 0)
return null;
const firstEntry = metadata[0];
if (!firstEntry.metricValues || !firstEntry.metricValues.length)
return null;
const metricValue = firstEntry.metricValues[0];
return {
value:
typeof metricValue.value === "number"
? metricValue.value.toFixed(2)
: metricValue.value,
metric: metricValue.metric,
aggregation: metricValue.aggregation,
groupAggregation: metricValue.groupAggregation,
};
};
// Determine colour and emoji based on severity and state
const getAlertStyle = (severity, state) => {
if (state === "OK") {
return { color: "#2ECC40", emoji: "✅" }; // green
}
switch (severity.toUpperCase()) {
case "HIGH":
return { color: "#FF4136", emoji: "🔥" }; // red
case "MEDIUM":
return { color: "#FF851B", emoji: "⚠️" }; // orange
case "LOW":
return { color: "#FFDC00", emoji: "⚡" }; // yellow
default:
return { color: "#AAAAAA", emoji: "ℹ️" }; // grey
}
};
// Extract key info from labels
const labels = data.labels || {};
const cluster =
labels.kube_cluster_name || labels.agent_tag_cluster || "N/A";
const namespace = labels.kube_namespace_name || "N/A";
// Get alert style
const { color, emoji } = getAlertStyle(data.severity, data.state);
// Extract metric values
const triggerMetricInfo = extractMetricValue(data.trigger_metadata);
const resolvedMetricInfo = extractMetricValue(data.resolved_metadata);
// Prepare main text content
const mainTextLines = [
`${emoji} - *${data.labels.alertname || data.title}*`,
`_${data.description}_`,
"```",
`Status | ${data.state} (${data.severity})`,
`============| - - - - - - - - - - - - - - - - - - - -`,
`Cluster | ${cluster}`,
`Namespace | ${namespace}`,
];
// Add pertinent information if available
if (labels.kube_pod_name) {
mainTextLines.push(`Pod | ${labels.kube_pod_name}`);
}
if (labels.kube_pod_container_name) {
mainTextLines.push(`Container | ${labels.kube_pod_container_name}`);
}
if (labels.kube_workload_type && labels.kube_workload_name) {
mainTextLines.push(`Workload | ${labels.kube_workload_type}/${labels.kube_workload_name}`);
}
if (labels.persistentvolumeclaim) {
mainTextLines.push(`PVC | ${labels.persistentvolumeclaim}`);
}
mainTextLines.push("============| - - - - - - - - - - - - - - - - - - - -");
mainTextLines.push(`Triggered | ${formatTimestamp(data.triggered_at)}`);
if (data.resolved_at && data.resolved_at !== "null") {
mainTextLines.push(`Resolved | ${formatTimestamp(data.resolved_at)}`);
}
mainTextLines.push("```");
const mainText = mainTextLines.join("\n");
// Prepare fields with optional information
const fields = [
{
title: "Metric",
value: triggerMetricInfo.metric,
short: false,
},
{
title: "Value",
value: triggerMetricInfo.value,
short: true,
}
];
if (resolvedMetricInfo) {
fields.push({
title: "End Value",
value: resolvedMetricInfo.value,
short: true,
});
}
if (labels.reason || labels.kube_pod_container_reason) {
fields.push({
title: "Reason",
value: labels.reason || labels.kube_pod_container_reason,
short: true,
});
}
// Add SysDig link as the last field
fields.push({
title: "Details",
value: `[View in SysDig](${data.alert_link})`,
short: true,
});
// Construct the message
const attachment = {
color: color,
title: `Alert details`,
fields: fields,
};
return {
content: {
text: mainText,
attachments: [attachment],
},
};
}
}
{
"alert_link": "{{@event_url}}",
"description": "{{@alert_description}}",
"resolved_at": "{{@event_resolved_timestamp}}",
"severity": "{{@alert_severity}}",
"state": "{{@event_state}}",
"title": "{{@event_title}}",
"triggered_at": "{{@event_trigger_timestamp}}",
"trigger_metadata": {{@event_trigger_metadata}},
"resolved_metadata": {{@event_resolved_metadata}},
"labels": {{@event_labels}}
}
@i5okie
Copy link
Author

i5okie commented Nov 13, 2024

Formatting example:
image
image

Depending on your alerts, you may have to customize the label-matching section to suit your needs.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment