skywalking-webhook
-
不要问,为什么要用Golang写这个脚本
-
问就是刚刚学Golang没有项目练手~~~
-
脚本需求:
使用Golang编写Webhook向钉钉告警
-
需求:
skywalking9.7不知什么原因,告警不出来
刚刚好学习了Go语言就想着要不用Golang写一个webhook脚本吧,然后就有了以下内容
-
版本:go1.23.0
初始化go项目
- 初始化项目,如果是简单的脚本,怎么方便怎么来
- 直接进入项目目录初始化也行
cd /apps/app/skywalking-webhook/
go mod init TestSkywalkingWebhook
- 但是如果是项目,或者复杂一点点就,建议使用
go mod init gitee.com/xxx/skills/
- Golang代理推荐
# 下包报错换一下试试吧,不行再换一个
go env -w GOPROXY=https://goproxy.cn,direct
创建钉钉告警机器人
配置skywlking告警配置文件
cat alarm-settings.yml
rules:
###@@@=================== 服务的响应时间 ===================@@@###
service_resp_time_rule:
expression: sum(service_resp_time > 6000) >= 3
period: 10
silence-period: 20
#message: SkyWalking Response time of service {name} is more than 1000ms in 3 minutes of last 10 minutes.
message: "当前服务响应时间在 10 分钟内 有出现 3 次 超时 6 秒"
###@@@=================== 服务的成功率是否低于某个阈值 ===================@@@###
service_sla_rule:
expression: sum(service_sla < 8000) >= 3
period: 10
silence-period: 20
#message: SkyWalking Successful rate of service {name} is lower than 80% in 2 minutes of last 10 minutes
message: "当前服务在 10 分钟内出现 3 次,健康检查接口成功率低于 80%。"
###@@@=================== 用于监控服务的响应时间的百分比分布情况 ===================@@@###
#service_resp_time_percentile_rule:
# expression: sum(service_percentile{_='0,1,2,3,4'} > 1000) >= 3
# period: 10
# silence-period: 5
# message: SkyWalking Percentile response time of service {name} alarm in 3 minutes of last 10 minutes, due to more than one condition of p50 > 1000, p75 > 1000, p90 > 1000, p95 > 1000, p99 > 1000
###@@@=================== 服务实例的响应时间是否超过了 ** 毫秒 ===================@@@###
service_instance_resp_time_rule:
expression: sum(service_instance_resp_time > 6000) >= 3
period: 10
silence-period: 5
#message: SkyWalking Response time of service instance {name} is more than 1000ms in 2 minutes of last 10 minutes
#message: "当前服务实例 {name} 响应时间在 10 分钟内 有出现 3 次 超时 1000 毫秒"
message: "当前服务实例 响应时间在 10 分钟内 有出现 3 次 超时 6 秒"
database_access_resp_time_rule:
expression: sum(database_access_resp_time > 6000) >= 3
period: 10
#message: SkyWalking Response time of database access {name} is more than 1000ms in 2 minutes of last 10 minutes
#message: "{name} 访问数据库的响应时间在 10 分钟内有 2 次超过 1000 毫秒"
message: "{name} 访问数据库的响应时间在 10 分钟内有 3 次超过 6 秒"
endpoint_relation_resp_time_rule:
expression: sum(endpoint_relation_resp_time > 6000) >= 2
period: 10
#message: SkyWalking Response time of endpoint relation {name} is more than 1000ms in 2 minutes of last 10 minutes
#message: "端点关系 {name} 的响应时间在过去 10 分钟内有 2 次超过 1000 毫秒"
message: "端点关系 的响应时间在过去 10 分钟内有 2 次超过 6 秒"
### 这个就是SkyWalking官方文档使用的告警办法,我发现我无论如何都告警不了
#dingtalk:
# default:
# is-default: true
# text-template: |-
# {
# "msgtype": "text",
# "text": {
# "content": "Apache SkyWalking Alarm: \n %s."
# }
# }
# webhooks:
# - url: https://oapixxxxxxxxxxxxxxxxxxxxxx731b69
# 调用自己写的webhook
hooks:
webhook:
default:
is-default: true
urls:
- http://localhost:8000/webhook
编写Golang脚本
- 有问题的话,可以使用了Print大法具体排错,下面已经写好并注释了
package main
import (
"bytes"
"encoding/json"
"fmt"
//"time"
//"strings"
"io/ioutil"
"net/http"
)
// Tag 数据结构
type Tag struct {
Key string `json:"key"`
Value string `json:"value"`
}
// SkyWalking 告警数据结构
type SkyWalkingAlert struct {
ScopeId int `json:"scopeId"`
Scope string `json:"scope"`
Name string `json:"name"`
Id0 string `json:"id0"`
Id1 string `json:"id1"`
RuleName string `json:"ruleName"`
AlertName string `json:"alertName"`
AlarmMessage string `json:"alarmMessage"`
StartTime int64 `json:"startTime"`
Tags []Tag `json:"tags"`
}
// 使用Message方式处理发出的告警信息
type DingTalkMessage struct {
Msgtype string `json:"msgtype"`
Markdown struct {
Title string `json:"title"`
Text string `json:"text"`
} `json:"markdown"`
}
// 发送钉钉消息
func sendToDingTalk(webhookURL string, message DingTalkMessage) error {
jsonData, err := json.Marshal(message)
if err != nil {
return err
}
req, err := http.NewRequest("POST", webhookURL, bytes.NewBuffer(jsonData))
if err != nil {
return err
}
req.Header.Set("Content-Type", "application/json")
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
// 可选:检查响应状态码等
return nil
}
// 接收并处理 Webhook 请求
func webhookHandler(w http.ResponseWriter, r *http.Request) {
// 读取请求体(二进制)
body, err := ioutil.ReadAll(r.Body)
if err != nil {
http.Error(w, "Failed to read request body", http.StatusBadRequest)
// fmt.Println("Test1.打印解析数组是否有报错:", err)
return
}
defer r.Body.Close()
// 解析告警数据数组
var alerts []SkyWalkingAlert
err = json.Unmarshal(body, &alerts)
if err != nil {
http.Error(w, "Failed to parse alert data", http.StatusBadRequest)
// fmt.Println("Test2.打印解析数组是否有报错:", err)
return
}
//fmt.Printf("Test3.打印数组了解钉钉告警的数据结构: %s\n", alerts)
// for _, alert := range alerts {
// fmt.Printf("Test4.Print大法: 服务名:%s\n 告警规则:%s\n 告警信息:%s\n 告警时间:%s\n", alert.Name, alert.RuleName, alert.AlarmMessage, alert.StartTime)
// }
for _, alert := range alerts {
// 将时间戳转换为time.Time类型,并假设它是纳秒级的时间戳
//startTime := time.Unix(0, alert.StartTime).Format("2006-01-02 15:04:05")
// 构造钉钉消息
dingTalkMessage := DingTalkMessage{
Msgtype: "markdown",
Markdown: struct {
Title string `json:"title"`
Text string `json:"text"`
}{
Title: fmt.Sprintf("SkyWalking 告警通知"),
Text: fmt.Sprintf("<font color='#FF7D00'> 服务名 </font>:%s<br> <font color='#FF7D00'> 告警规则 </font>:%s<br> <font color='#FF7D00'> 告警信息 </font>:%s<br>", alert.Name, alert.RuleName, alert.AlarmMessage),
},
}
// 发送钉钉消息
if err := sendToDingTalk("https://oaxxxxxx----你的钉钉webhook----xxxxxxxx", dingTalkMessage); err != nil {
http.Error(w, "Failed to send alert to DingTalk", http.StatusInternalServerError)
fmt.Println("Test5.发送钉钉消息报错:", err)
return
}
}
// 响应 HTTP 请求
w.WriteHeader(http.StatusOK)
w.Write([]byte("Webhook received and alert sent to DingTalk successfully"))
}
func main() {
http.HandleFunc("/webhook", webhookHandler)
fmt.Println("Server is listening on :8000")
if err := http.ListenAndServe(":8000", nil); err != nil {
panic(err)
}
}
go mod tidy
- 告警出来大概是这样,具体可以自己调整,都在SkyWalkingAlert这个结构体中,自己拿出来打印就好了
测试脚本可用性
- 执行脚本测试
curl -X POST http://localhost:8000/webhook -H "Content-Type: application/json" -d '
[
{
"scopeId": 1,
"scope": "Service",
"name": "test-service",
"id0": "0",
"id1": "0",
"ruleName": "Response Time Degradation",
"ruleId": 1,
"alertName": "High Response Time",
"alarmMessage": "Response time has exceeded 1000ms",
"startTime": 1672531200000,
"tags": [
{"key": "endpoint", "value": "/api/test"},
{"key": "instance", "value": "127.0.0.1:8080"}
]
}
]'