chore: 初始开源发布
iotlcm-bot:独立飞书应用机器人(读取 Worker 终态 Run JSON,渲染并发送全量同步结果卡片)。
- Go module:git.5iot.com/iotlcm/iotlcm-bot → git.5iot.com/iotlcm-public/iotlcm-bot
- 飞书凭据占位:appId/appSecret/chatId → ${FEISHU_APP_ID}/${FEISHU_APP_SECRET}/${FEISHU_CHAT_ID}
- 移除内部资产:CI 触发器
- Dockerfile 改用公共基础镜像(golang:1.25-alpine → scratch)
- README 重写(命令/配置/挂载契约/常见问题),去掉内部环境路径
- 补 Apache-2.0 LICENSE,LF 行尾 + .gitattributes
This commit is contained in:
@@ -0,0 +1,70 @@
|
||||
package notify
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
)
|
||||
|
||||
// alertState 记录告警节流信息。
|
||||
//
|
||||
// 为什么必须落盘:进程检测到挂载失效后会主动退出、由 restart 策略拉起。
|
||||
// 若节流时间只存在内存里,重启后 lastAlert 归零,会立刻再次告警 ——
|
||||
// 持续故障就变成「每轮重启都发一条飞书」,把真正的告警淹成骚扰。
|
||||
type alertState struct {
|
||||
// LastDegradedAlert 是上次发送「归档目录不可用」告警的时间。
|
||||
LastDegradedAlert time.Time `json:"lastDegradedAlert,omitempty"`
|
||||
// LastDegradedReason 用于在故障原因变化时立即重新告警。
|
||||
LastDegradedReason string `json:"lastDegradedReason,omitempty"`
|
||||
// ExitCount 统计因挂载失效而退出的累计次数,用于识别「反复重启」。
|
||||
ExitCount int `json:"exitCount,omitempty"`
|
||||
}
|
||||
|
||||
func alertStatePath(stateDir string) string {
|
||||
return filepath.Join(stateDir, "alert-state.json")
|
||||
}
|
||||
|
||||
func loadAlertState(stateDir string) alertState {
|
||||
var st alertState
|
||||
if stateDir == "" {
|
||||
return st
|
||||
}
|
||||
body, err := os.ReadFile(alertStatePath(stateDir))
|
||||
if err != nil {
|
||||
return st
|
||||
}
|
||||
_ = json.Unmarshal(body, &st)
|
||||
return st
|
||||
}
|
||||
|
||||
func saveAlertState(stateDir string, st alertState) error {
|
||||
if stateDir == "" {
|
||||
return nil
|
||||
}
|
||||
path := alertStatePath(stateDir)
|
||||
if err := os.MkdirAll(filepath.Dir(path), 0o700); err != nil {
|
||||
return err
|
||||
}
|
||||
body, err := json.MarshalIndent(st, "", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
tmp := path + ".tmp"
|
||||
if err := os.WriteFile(tmp, body, 0o600); err != nil {
|
||||
return err
|
||||
}
|
||||
return os.Rename(tmp, path)
|
||||
}
|
||||
|
||||
// shouldAlert 判断是否该发告警:间隔到了,或故障原因发生变化。
|
||||
func (st alertState) shouldAlert(reason string, now time.Time, interval time.Duration) bool {
|
||||
if st.LastDegradedAlert.IsZero() {
|
||||
return true
|
||||
}
|
||||
if reason != st.LastDegradedReason {
|
||||
// 原因变了(例如从「目录不存在」变成「不可读」)值得立刻提醒。
|
||||
return true
|
||||
}
|
||||
return now.Sub(st.LastDegradedAlert) >= interval
|
||||
}
|
||||
Reference in New Issue
Block a user