- 品牌: Go 模块/数据文件/二进制/构建部署资产全部 opsx→lokistack;前端 localStorage 命名空间迁移(读旧写新,含按用户 id 前缀迁移);修复 nginx upstream 与 Makefile 引用已改名服务的断裂 - i18n: react-i18next 落地,common + 11 个页面域命名空间全站中英(菜单/顶栏/登录/列表页/表单/确认弹窗/状态徽章);设置页语言即时切换;修复词典自注册导致的循环导入白屏 - 用户管理: 批量删除(POST /users/batch-delete,不删自己/最后管理员,部分失败不阻断)+ 行复选/全选/批量工具条 - 主题: 浅色侧栏(中性灰激活态)、四态模式(浅色/深色/跟随系统/定时切换,按时区判定)、顶栏太阳月亮二态、设置页紧凑分段选择器 - UI: 登录记住用户名、CardActionButton 统一五页卡片操作钮、shadcn Badge 替换手写徽章、语义色修正 - 清理: 移除主机 Web 终端(前后端)、k8s-helm/Helm 遗留代码与字段、README 重写精简 - 修复: 侧栏与日志告警 order 重复、路由标题 i18n key 未翻译、词典双逗号等
84 lines
3.4 KiB
Go
84 lines
3.4 KiB
Go
package seed
|
|
|
|
// 内置告警规则 — 新环境开箱即用的通用日志告警(参照运维告警脚本的两类模式):
|
|
// 致命类(出现即报):FATAL / panic / OutOfMemoryError / StackOverflowError
|
|
// 突增类(超阈值才报):应用 error 过多 / ingress-nginx 4xx / 5xx
|
|
// 绑定数据源:部署成功自动注册的 LogSource(managed)。
|
|
// 规则默认不启用(通知通道由管理员后期在设置页配置后再开启),ChannelIDs 留空。
|
|
import (
|
|
"time"
|
|
|
|
"lokistack/models"
|
|
"lokistack/repository"
|
|
)
|
|
|
|
type builtinRule struct {
|
|
name string
|
|
query string
|
|
windowMin int
|
|
threshold int
|
|
cooldown int
|
|
}
|
|
|
|
var builtinRules = []builtinRule{
|
|
// —— 致命类:15 分钟内出现即报 ——
|
|
{name: "致命日志 FATAL", query: `{namespace=~".+"} |= "FATAL"`, windowMin: 15, threshold: 0, cooldown: 60},
|
|
{name: "Go panic", query: `{namespace=~".+"} |= "panic:"`, windowMin: 15, threshold: 0, cooldown: 60},
|
|
{name: "JVM OutOfMemoryError", query: `{namespace=~".+"} |~ "(^|[^a-zA-Z])OutOfMemoryError"`, windowMin: 15, threshold: 0, cooldown: 60},
|
|
{name: "StackOverflowError", query: `{namespace=~".+"} |= "StackOverflowError"`, windowMin: 15, threshold: 0, cooldown: 60},
|
|
{name: "Kubernetes 节点 NotReady", query: `{app="kube-state-metrics"} |= "NotReady"`, windowMin: 15, threshold: 0, cooldown: 120},
|
|
{name: "容器 OOMKilled", query: `{app=~"kube-state-metrics|event-exporter"} |= "OOMKilled"`, windowMin: 15, threshold: 0, cooldown: 120},
|
|
// —— 突增类:超过基线阈值才报 ——
|
|
{name: "应用错误过多(error > 1000)", query: `{namespace=~".+"} |~ "(?i)\berror\b"`, windowMin: 15, threshold: 1000, cooldown: 60},
|
|
{name: "ingress-nginx 5xx 错误", query: `{app=~"ingress-nginx|nginx-ingress-controller"} |~ " (50[0-4]|5[2-9][0-9]) `, windowMin: 15, threshold: 100, cooldown: 60},
|
|
{name: "ingress-nginx 4xx 激增", query: `{app=~"ingress-nginx|nginx-ingress-controller"} |~ " 4[0-9][0-9] "`, windowMin: 15, threshold: 1000, cooldown: 60},
|
|
{name: "数据库连接失败", query: `{namespace=~".+"} |~ "(?i)(connection refused|connection reset by peer|too many connections)"`, windowMin: 15, threshold: 50, cooldown: 60},
|
|
}
|
|
|
|
// seedBuiltinAlertRules 为每个平台管理的 LogSource(managed=true)补齐内置规则:
|
|
// 首次部署 Loki 后即有一套可直接启用的告警模板;通知通道留空,管理员配置后启用。
|
|
// SeedAlertRules 补齐内置告警规则(幂等;公开给启动与部署成功两处调用)
|
|
func SeedAlertRules() {
|
|
db := repository.GetDB()
|
|
db.RLock()
|
|
type srcRef struct {
|
|
id uint
|
|
name string
|
|
}
|
|
var sources []srcRef
|
|
for i := range db.LogSources {
|
|
if db.LogSources[i].Managed {
|
|
sources = append(sources, srcRef{id: db.LogSources[i].ID, name: db.LogSources[i].Name})
|
|
}
|
|
}
|
|
// 已有规则(按 name)跳过
|
|
existing := map[string]bool{}
|
|
for i := range db.AlertRules {
|
|
existing[db.AlertRules[i].Name] = true
|
|
}
|
|
db.RUnlock()
|
|
|
|
if len(sources) == 0 {
|
|
return
|
|
}
|
|
// 绑定第一个 managed 数据源(通常是唯一部署)
|
|
target := sources[0]
|
|
|
|
newID := db.NextID("alertRules")
|
|
db.Lock()
|
|
defer db.Unlock()
|
|
for _, r := range builtinRules {
|
|
if existing[r.name] {
|
|
continue
|
|
}
|
|
newID++
|
|
db.AlertRules = append(db.AlertRules, models.AlertRule{
|
|
ID: newID, Name: r.name, SourceID: target.id,
|
|
Query: r.query, WindowMin: r.windowMin, Threshold: r.threshold,
|
|
Enabled: false, CooldownMin: r.cooldown,
|
|
CreatedAt: time.Now(), UpdatedAt: time.Now(),
|
|
})
|
|
}
|
|
db.RequestSave()
|
|
}
|