[notifier] 新增email告警方式

This commit is contained in:
wuko233 2026-04-05 11:29:50 +08:00
parent 9970e93665
commit 18e1672114
5 changed files with 182 additions and 4 deletions

View File

@ -9,6 +9,7 @@ import (
"sysmonitord/internal/monitor/detector"
"sysmonitord/internal/monitor/timer"
"sysmonitord/internal/monitor/watcher"
"sysmonitord/internal/notifier"
"sysmonitord/internal/scanner/file"
"sysmonitord/internal/scanner/process"
"sysmonitord/internal/storage"
@ -107,6 +108,10 @@ var StartCmd = &cobra.Command{
procScheduler := timer.NewScheduler(time.Duration(cfg.Scanner.Process.Interval)*time.Second, procDetector)
procScheduler.Start()
// ====== 启动告警管理器 ======
alerter := notifier.NewAlerter(cfg.Notification)
alerter.Start()
logger.Log.Info("系统监控守护服务已启动,正在监控系统变化...")
quit := make(chan os.Signal, 1)
@ -126,6 +131,14 @@ var StartCmd = &cobra.Command{
fileDetector.HandleEvent(event.Path, event.Op.String())
}
// test
alerter.PushAlert(notifier.AlertEvent{
Type: "File",
Path: event.Path,
Reason: event.Op.String(),
Details: "To test",
})
case err := <-mon.Errors():
logger.Log.Error("文件监听错误", zap.Error(err))

View File

@ -1,10 +1,28 @@
package config
type Config struct {
Log LogConfig `yaml:"log"`
Audit AuditConfig `yaml:"audit"`
Scanner ScannerConfig `yaml:"scanner"`
Storage StorageConfig `yaml:"storage"`
Log LogConfig `yaml:"log"`
Audit AuditConfig `yaml:"audit"`
Scanner ScannerConfig `yaml:"scanner"`
Storage StorageConfig `yaml:"storage"`
Notification NotificationConfig `yaml:"notification"`
}
type NotificationConfig struct {
Email EmailConfig `yaml:"email"`
}
type EmailConfig struct {
Enabled bool `yaml:"enabled"`
Recipients []string `yaml:"recipients"`
SMTP SMTPConfig `yaml:"smtp"`
}
type SMTPConfig struct {
Server string `yaml:"server"`
Port int `yaml:"port"`
Username string `yaml:"username"`
Password string `yaml:"password"`
}
type LogConfig struct {

View File

@ -0,0 +1,97 @@
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: 1 * time.Minute, // 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:
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)))
}
}

View File

@ -0,0 +1,50 @@
package mail
import (
"fmt"
"net/smtp"
"sysmonitord/internal/config"
"sysmonitord/pkg/logger"
"go.uber.org/zap"
)
type Mailer struct {
cfg config.EmailConfig
}
func NewMailer(cfg config.EmailConfig) *Mailer {
return &Mailer{cfg: cfg}
}
func (m *Mailer) Send(subject, body string) error {
if !m.cfg.Enabled {
logger.Log.Debug("[notifier] 未启用邮件通知,跳过....")
return nil
}
headers := make(map[string]string)
headers["From"] = m.cfg.SMTP.Username
headers["To"] = m.cfg.Recipients[0]
headers["Subject"] = subject
message := ""
for k, v := range headers {
message += fmt.Sprintf("%s: %s\r\n", k, v)
}
message += "\r\n" + body
auth := smtp.PlainAuth("", m.cfg.SMTP.Username, m.cfg.SMTP.Password, m.cfg.SMTP.Server)
addr := fmt.Sprintf("%s:%d", m.cfg.SMTP.Server, m.cfg.SMTP.Port)
logger.Log.Info("[notifier] 发送邮件通知", zap.String("subject", subject), zap.String("to", m.cfg.Recipients[0]))
err := smtp.SendMail(addr, auth, m.cfg.SMTP.Username, m.cfg.Recipients, []byte(message))
if err != nil {
logger.Log.Error("[notifier] 发送邮件失败", zap.Error(err))
return err
}
logger.Log.Info("[notifier] 邮件发送成功")
return nil
}