通过go自定义alertmanager 发送通知的webhook

本文主要是大体介绍webhook的代码以及涉及到的服务部署,详细配置需要自己弄

Prometheus、alertmanager部署

先创建alertmanager、Prometheus的docker-compose yaml文件,下面只是把服务运行起来,具体功能需要自己配置,如果有就跳过

version: '3'
services:
  prometheus:
    image: prom/prometheus
    ports:
      - '9090:9090'
    volumes:
      - prometheus_data:/etc/prometheus
    command:
      - '--config.file=/etc/prometheus/prometheus.yml'
      - '--web.enable-lifecycle'
    restart: always
  
  grafana:
    image: grafana/grafana
    ports:
      - '3000:3000'
    depends_on:
      - prometheus
    restart: always

  alertmanager:
    image: prom/alertmanager
    ports:
      - '9093:9093'
    volumes:
      - alertmanager_data:/etc/alertmanager
    command:
      - '--config.file=/etc/alertmanager/alertmanager.yml'
    restart: always
volumes:
    prometheus_data: {}
    alertmanager_data: {}

docker-compose up -d运行起来,如果镜像拉不下,根据下面配置镜像加速

cat /etc/docker/daemon.json 
{"registry-mirrors":[
        "https://docker.anyhub.us.kg",
        "https://dockerhub.icu",
        "https://docker.awsl9527.cn"
]}
sudo systemctl daemon-reload
sudo systemctl restart docker

alertmanager请求body

{
    "receiver": "web\\.hook",
    "status": "firing",
    "alerts": [
        {
            "status": "firing",
            "labels": {
                "alertname": "process_cpu_seconds_total",
                "app": "myrules",
                "instance": "localhost:9090",
                "job": "prometheus",
                "severity": "info",
                "type": "constom"
            },
            "annotations": {
                "description": "description info",
                "summary": "High request latency"
            },
            "startsAt": "2024-08-05T09:50:13.828Z",
            "endsAt": "0001-01-01T00:00:00Z",
            "generatorURL": "http://b985ae1139a4:9090/graph?g0.expr=process_cpu_seconds_total%7Binstance%3D%22localhost%3A9090%22%2Cjob%3D%22prometheus%22%7D+%3C+3\u0026g0.tab=1",
            "fingerprint": "73caa1e73b23db5a"
        }
    ],
    "groupLabels": {
        "alertname": "process_cpu_seconds_total"
    },
    "commonLabels": {
        "alertname": "process_cpu_seconds_total",
        "app": "myrules",
        "instance": "localhost:9090",
        "job": "prometheus",
        "severity": "info",
        "type": "constom"
    },
    "commonAnnotations": {
        "description": "description info",
        "summary": "High request latency"
    },
    "externalURL": "http://c487130b876e:9093",
    "version": "4",
    "groupKey": "{}:{alertname=\"process_cpu_seconds_total\"}",
    "truncatedAlerts": 0
}

如果想看请求的body,下面代码可以输出,配置好alertmanager的webhook和告警规则,接收post请求

package main

import (
        "fmt"
        "io"
        "log"
        "net/http"
)

func main() {
        http.HandleFunc("/alert", handleData)
        log.Println("Listening on :8086...")
        err := http.ListenAndServe(":8086", nil)
        if err != nil {
                log.Fatal("ListenAndServe: ", err)
        }
}

func handleData(w http.ResponseWriter, r *http.Request) {
        if r.Method != http.MethodPost {
                http.Error(w, "Only POST method is allowed", http.StatusMethodNotAllowed)
                return
        }

        // 读取请求体
        body, err := io.ReadAll(r.Body)
        if err != nil {
                http.Error(w, "Failed to read request body", http.StatusInternalServerError)
                return
        }

        // 打印请求体中的原始JSON数据
        fmt.Printf("Received JSON data: %s\n", body)

        // 返回成功响应
        w.WriteHeader(http.StatusOK)
        w.Write([]byte("Data received"))

涉及到webhook的代码

  1. 创建main.go文件,代码如下:
package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"log"
	"net/http"

	"github.com/gin-gonic/gin"
)

// 根据body定义存储alertmanager的 json 结构体
type Alert struct {
	Status     string            `json:"status"`
	Labels     map[string]string `json:"labels"`
	Annotations map[string]string `json:"annotations"`
	StartsAt   string            `json:"startsAt"`
	EndsAt     string            `json:"endsAt"`
	GeneratorURL string           `json:"generatorURL"`
	Fingerprint string            `json:"fingerprint"`
}

type AlertNotification struct {
	Receiver    string          `json:"receiver"`
	Status      string          `json:"status"`
	Alerts      []Alert         `json:"alerts"`
	GroupLabels map[string]string `json:"groupLabels"`
	CommonLabels map[string]string `json:"commonLabels"`
	CommonAnnotations map[string]string `json:"commonAnnotations"`
	ExternalURL string          `json:"externalURL"`
	Version     string          `json:"version"`
	GroupKey    string          `json:"groupKey"`
	TruncatedAlerts int           `json:"truncatedAlerts"`
}
// Message 结构体用于构建消息的内容
type Message struct {
	MsgType string      `json:"msg_type"`
	Content interface{} `json:"content"`
}
// TextContent 结构体用于构建文本消息的内容
type TextContent struct {
	Text string `json:"text"`
}

var feishuhook string = "https://open.feishu.cn/open-apis/bot/v2/hook/xxxxxx"

func handleData(c *gin.Context) {
	var data AlertNotification
	if err := c.ShouldBindJSON(&data); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request body"})
		return
	}

	// 打印请求体中的原始JSON数据
	fmt.Println("Received JSON data: ", data)
	if data.Status == "firing" {
		err := SendMessage(feishuhook, data.GroupLabels["alertname"] + "有故障")
		if err != nil {
			fmt.Println(err)
		}
	}else {
		err := SendMessage(feishuhook, data.GroupLabels["alertname"] + "已恢复")
		if err != nil {
			fmt.Println(err)
		}
	}
	// 返回成功响应
	c.JSON(http.StatusOK, gin.H{
		"msg": "msg sent successfully",
	})
}

func SendMessage(webhookURL string, text string) error {
	// 创建消息内容
	textContent := TextContent{Text: text}
	message := Message{
		MsgType: "text",
		Content: textContent,
	}
	// 将消息转换为JSON格式
	jsonMessage, err := json.Marshal(message)
	if err != nil {
		return fmt.Errorf("error marshaling message to JSON: %w", err)
	}
	// 发送HTTP POST请求
	resp, err := http.Post(webhookURL, "application/json", bytes.NewBuffer(jsonMessage))
	if err != nil {
		return fmt.Errorf("error sending HTTP POST request: %w", err)
	}
	defer resp.Body.Close()
	// 检查响应状态码
	body, _ := io.ReadAll(resp.Body)
	if resp.StatusCode != http.StatusOK {
		return fmt.Errorf("unexpected HTTP status code: %d, response body: %s", resp.StatusCode, body)
	}
	return nil
}

func main() {
	// 设置为release模式
	gin.SetMode(gin.ReleaseMode)
	r := gin.Default()
	r.POST("/alert", handleData)
	log.Println("Listening on :8080...")
	r.Run(":8080")
}

到目前为止就可以了,运行go run main.go,配置一条告警,查看是否能发到飞书,其他平台也类似

  • 8
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值