容器化部署普及后,实时掌握容器运行日志中的异常信息成为运维和开发的核心需求,通过Golang实现容器日志告警与通知,可以搭建轻量、高效的监控工具,适配自定义的业务告警规则。

核心实现思路
整个流程分为四个核心步骤:首先采集目标容器的日志内容,接着按照预设规则匹配日志中的异常关键词或错误级别,匹配成功后触发告警逻辑,最后通过配置的通知渠道把告警信息发送给相关人员。
容器日志采集
Docker容器的日志默认存储在/var/lib/docker/containers/<容器ID>/<容器ID>-json.log路径下,也可以通过Docker API获取实时日志。下面是通过Docker SDK采集容器日志的示例代码:
package main
import (
"context"
"fmt"
"github.com/docker/docker/api/types"
"github.com/docker/docker/client"
)
// 采集指定容器的日志
func collectContainerLog(containerID string) error {
cli, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation())
if err != nil {
return fmt.Errorf("创建Docker客户端失败: %v", err)
}
defer cli.Close()
// 配置日志读取参数,获取最近100行日志
options := types.ContainerLogsOptions{
ShowStdout: true,
ShowStderr: true,
Tail: "100",
Follow: false,
}
logReader, err := cli.ContainerLogs(context.Background(), containerID, options)
if err != nil {
return fmt.Errorf("获取容器日志失败: %v", err)
}
defer logReader.Close()
// 读取日志内容并打印,实际场景中可写入缓冲区待后续处理
buf := make([]byte, 1024)
for {
n, err := logReader.Read(buf)
if n > 0 {
fmt.Printf("日志内容: %sn", string(buf[:n]))
}
if err != nil {
break
}
}
return nil
}
告警规则匹配
采集到日志后,需要按照预设规则判断是否触发告警,常见的规则包括匹配关键字error、fatal、exception,或者匹配特定的错误码。下面是规则匹配的示例代码:
package main
import (
"strings"
)
// 告警规则配置
type AlertRule struct {
Keywords []string // 触发告警的关键词
Level string // 告警级别
}
// 检查日志是否匹配告警规则
func matchAlertRule(logContent string, rules []AlertRule) (bool, string) {
for _, rule := range rules {
for _, keyword := range rule.Keywords {
if strings.Contains(strings.ToLower(logContent), strings.ToLower(keyword)) {
return true, rule.Level
}
}
}
return false, ""
}
// 示例规则
var defaultRules = []AlertRule{
{Keywords: []string{"error", "fatal", "exception"}, Level: "严重"},
{Keywords: []string{"warning"}, Level: "一般"},
}
告警通知发送
触发告警后,需要将告警信息通过邮件、企业微信、钉钉等渠道发送出去,下面以发送HTTP请求到企业微信机器人通知为例:
package main
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
)
// 企业微信机器人通知消息结构
type WechatNotifyMsg struct {
MsgType string `json:"msgtype"`
Text struct {
Content string `json:"content"`
} `json:"text"`
}
// 发送企业微信告警通知
func sendWechatNotify(webhookURL string, alertContent string) error {
msg := WechatNotifyMsg{MsgType: "text"}
msg.Text.Content = alertContent
msgBytes, err := json.Marshal(msg)
if err != nil {
return fmt.Errorf("消息序列化失败: %v", err)
}
resp, err := http.Post(webhookURL, "application/json", bytes.NewBuffer(msgBytes))
if err != nil {
return fmt.Errorf("发送通知请求失败: %v", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return fmt.Errorf("通知发送失败,状态码: %d", resp.StatusCode)
}
fmt.Println("告警通知发送成功")
return nil
}
完整流程串联
把上述几个环节组合起来,就可以实现完整的容器日志告警与通知功能,下面是串联后的主逻辑示例:
package main
import (
"fmt"
)
func main() {
// 配置容器ID和企业微信机器人地址
containerID := "your_container_id"
wechatWebhook := "https://ipipp.com/wechat_webhook" // 替换为企业微信实际webhook地址
// 1. 采集容器日志
logContent, err := collectContainerLog(containerID)
if err != nil {
fmt.Printf("日志采集失败: %vn", err)
return
}
// 2. 匹配告警规则
needAlert, alertLevel := matchAlertRule(logContent, defaultRules)
if !needAlert {
fmt.Println("日志无异常,无需告警")
return
}
// 3. 构造告警内容并发送通知
alertMsg := fmt.Sprintf("【容器日志告警】级别: %sn容器ID: %sn日志内容: %s", alertLevel, containerID, logContent)
err = sendWechatNotify(wechatWebhook, alertMsg)
if err != nil {
fmt.Printf("告警通知发送失败: %vn", err)
return
}
}
优化建议
- 可以加入日志缓存机制,避免重复采集相同日志,减少资源消耗
- 支持动态加载告警规则,无需重启程序即可更新匹配逻辑
- 增加告警频率限制,避免短时间内大量重复告警干扰运维人员
- 可以扩展通知渠道,支持邮件、短信、钉钉等多种通知方式
通过上述方案,开发者可以根据自身业务需求灵活调整告警规则和通知方式,快速搭建适配场景的容器日志告警系统,提升容器运维的响应效率。