sysmonitord/internal/notifier/alert_manager.go

99 lines
2.3 KiB
Go

package notifier
import (
"fmt"
"sync"
"sysmonitord/internal/config"
"sysmonitord/internal/notifier/mail"
"sysmonitord/pkg/logger"
"time"
"go.uber.org/zap"
)
type AlertEvent struct {
Type string
Path string
Reason string
Details string
}
type Alerter struct {
cfg config.NotificationConfig
mailer *mail.Mailer
eventChan chan AlertEvent
buffer []AlertEvent
mu sync.Mutex
timer *time.Timer
interval time.Duration
}
func NewAlerter(cfg config.NotificationConfig) *Alerter {
return &Alerter{
cfg: cfg,
mailer: mail.NewMailer(cfg.Email),
eventChan: make(chan AlertEvent, 100),
buffer: make([]AlertEvent, 0),
interval: 30 * time.Second, // Todo: 可配置化
}
}
func (a *Alerter) Start() {
go a.loop()
}
func (a *Alerter) PushAlert(event AlertEvent) {
select {
case a.eventChan <- event:
logger.Log.Debug("[notifier] 推送告警事件", zap.String("path", event.Path), zap.String("reason", event.Reason))
default:
logger.Log.Warn("[notifier] 告警事件通道已满,丢弃告警", zap.String("path", event.Path), zap.String("reason", event.Reason))
}
}
func (a *Alerter) loop() {
a.timer = time.NewTimer(a.interval)
for {
select {
case event := <-a.eventChan:
a.mu.Lock()
a.buffer = append(a.buffer, event)
a.mu.Unlock()
logger.Log.Debug("[notifier] 收到告警,加入待发送序列", zap.String("path", event.Path))
case <-a.timer.C:
logger.Log.Debug("[notifier] 定时检查告警事件,准备发送", zap.Int("count", len(a.buffer)))
a.mu.Lock()
if len(a.buffer) > 0 {
a.sendAlert()
a.buffer = make([]AlertEvent, 0)
}
a.mu.Unlock()
a.timer.Reset(a.interval)
}
}
}
func (a *Alerter) sendAlert() {
if len(a.buffer) == 0 {
return
}
subject := fmt.Sprintf("【Sysmonitor】新增 %d 个告警", len(a.buffer))
body := "以下是最近的告警事件:\n\n"
for _, event := range a.buffer {
body += fmt.Sprintf("- [%s] %s: %s (%s)\n", event.Type, event.Path, event.Reason, event.Details)
}
body += "\n请及时关注系统安全状况。"
if err := a.mailer.Send(subject, body); err != nil {
logger.Log.Error("[notifier] 发送告警邮件失败", zap.Error(err))
} else {
logger.Log.Debug("[notifier] 告警邮件发送成功", zap.Int("count", len(a.buffer)))
}
}