Compare commits

...

10 Commits

Author SHA1 Message Date
ning
037112a9e6 refactor: update alert mute api 2025-10-15 15:19:01 +08:00
zjxpsetp
c6e75d31a1 update jmx dashboard to support multi gc mode 2025-10-15 12:41:01 +08:00
zjxpsetp
bd24f5b056 Merge remote-tracking branch 'origin/main'
# Conflicts:
#	integrations/Java/dashboards/jmx_by_kubernetes.json
2025-10-15 12:36:14 +08:00
zjxpsetp
89551c8edb update jmx dashboard to support multi gc mode 2025-10-15 12:29:11 +08:00
ning
042b44940d docs: update i18n 2025-10-14 17:38:25 +08:00
ning
8cd8674848 refactor: optimize alert mute match 2025-10-14 16:59:57 +08:00
ning
7bb6ac8a03 refactor: update alert mute sync 2025-10-14 16:45:15 +08:00
ning
76b35276af refactor: update event api 2025-10-13 20:24:52 +08:00
SaladDay
439a21b784 feat: add expired filter for alert mute rules (#2907) 2025-10-13 14:04:32 +08:00
Yening Qin
47e70a2dba fix: sql order (#2908) 2025-10-13 12:18:16 +08:00
10 changed files with 1520 additions and 1434 deletions

View File

@@ -1,6 +1,7 @@
package mute
import (
"slices"
"strconv"
"strings"
"time"
@@ -153,13 +154,7 @@ func MatchMute(event *models.AlertCurEvent, mute *models.AlertMute, clock ...int
// 如果不是全局的,判断 匹配的 datasource id
if len(mute.DatasourceIdsJson) != 0 && mute.DatasourceIdsJson[0] != 0 && event.DatasourceId != 0 {
idm := make(map[int64]struct{}, len(mute.DatasourceIdsJson))
for i := 0; i < len(mute.DatasourceIdsJson); i++ {
idm[mute.DatasourceIdsJson[i]] = struct{}{}
}
// 判断 event.datasourceId 是否包含在 idm 中
if _, has := idm[event.DatasourceId]; !has {
if !slices.Contains(mute.DatasourceIdsJson, event.DatasourceId) {
return false, errors.New("datasource id not match")
}
}
@@ -198,7 +193,7 @@ func MatchMute(event *models.AlertCurEvent, mute *models.AlertMute, clock ...int
return false, errors.New("event severity not match mute severity")
}
if mute.ITags == nil || len(mute.ITags) == 0 {
if len(mute.ITags) == 0 {
return true, nil
}
if !common.MatchTags(event.TagsMap, mute.ITags) {

View File

@@ -25,6 +25,7 @@ func (rt *Router) pushEventToQueue(c *gin.Context) {
if event.RuleId == 0 {
ginx.Bomb(200, "event is illegal")
}
event.FE2DB()
event.TagsMap = make(map[string]string)
for i := 0; i < len(event.TagsJSON); i++ {
@@ -40,7 +41,7 @@ func (rt *Router) pushEventToQueue(c *gin.Context) {
event.TagsMap[arr[0]] = arr[1]
}
hit, _ := mute.EventMuteStrategy(event, rt.AlertMuteCache)
hit, _ := mute.EventMuteStrategy(event, rt.AlertMuteCache)
if hit {
logger.Infof("event_muted: rule_id=%d %s", event.RuleId, event.Hash)
ginx.NewRender(c).Message(nil)

View File

@@ -628,6 +628,7 @@ func (rt *Router) Config(r *gin.Engine) {
service.GET("/recording-rules", rt.recordingRuleGetsByService)
service.GET("/alert-mutes", rt.alertMuteGets)
service.GET("/active-alert-mutes", rt.activeAlertMuteGets)
service.POST("/alert-mutes", rt.alertMuteAddByService)
service.DELETE("/alert-mutes", rt.alertMuteDel)

View File

@@ -20,7 +20,8 @@ func (rt *Router) alertMuteGetsByBG(c *gin.Context) {
bgid := ginx.UrlParamInt64(c, "id")
prods := strings.Fields(ginx.QueryStr(c, "prods", ""))
query := ginx.QueryStr(c, "query", "")
lst, err := models.AlertMuteGets(rt.Ctx, prods, bgid, -1, query)
expired := ginx.QueryInt(c, "expired", -1)
lst, err := models.AlertMuteGets(rt.Ctx, prods, bgid, -1, expired, query)
ginx.NewRender(c).Data(lst, err)
}
@@ -55,11 +56,17 @@ func (rt *Router) alertMuteGets(c *gin.Context) {
bgid := ginx.QueryInt64(c, "bgid", -1)
query := ginx.QueryStr(c, "query", "")
disabled := ginx.QueryInt(c, "disabled", -1)
lst, err := models.AlertMuteGets(rt.Ctx, prods, bgid, disabled, query)
expired := ginx.QueryInt(c, "expired", -1)
lst, err := models.AlertMuteGets(rt.Ctx, prods, bgid, disabled, expired, query)
ginx.NewRender(c).Data(lst, err)
}
func (rt *Router) activeAlertMuteGets(c *gin.Context) {
lst, err := models.AlertMuteGetsAll(rt.Ctx)
ginx.NewRender(c).Data(lst, err)
}
func (rt *Router) alertMuteAdd(c *gin.Context) {
var f models.AlertMute
@@ -69,7 +76,9 @@ func (rt *Router) alertMuteAdd(c *gin.Context) {
f.CreateBy = username
f.UpdateBy = username
f.GroupId = ginx.UrlParamInt64(c, "id")
ginx.NewRender(c).Message(f.Add(rt.Ctx))
ginx.Dangerous(f.Add(rt.Ctx))
ginx.NewRender(c).Data(f.Id, nil)
}
type MuteTestForm struct {

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,5 @@
{
[
{
"name": "JMX - Kubernetes",
"tags": "Prometheus JMX Kubernetes",
"configs": {
@@ -1870,4 +1871,5 @@
"version": "3.0.0"
},
"uuid": 1755595969673000
}
}
]

View File

@@ -230,7 +230,7 @@ func AlertMuteGet(ctx *ctx.Context, where string, args ...interface{}) (*AlertMu
return lst[0], err
}
func AlertMuteGets(ctx *ctx.Context, prods []string, bgid int64, disabled int, query string) (lst []AlertMute, err error) {
func AlertMuteGets(ctx *ctx.Context, prods []string, bgid int64, disabled int, expired int, query string) (lst []AlertMute, err error) {
session := DB(ctx)
if bgid != -1 {
@@ -249,6 +249,15 @@ func AlertMuteGets(ctx *ctx.Context, prods []string, bgid int64, disabled int, q
}
}
if expired != -1 {
now := time.Now().Unix()
if expired == 1 {
session = session.Where("mute_time_type = ? AND etime < ?", TimeRange, now)
} else {
session = session.Where("(mute_time_type = ? AND etime >= ?) OR mute_time_type = ?", TimeRange, now, Periodic)
}
}
if query != "" {
arr := strings.Fields(query)
for i := 0; i < len(arr); i++ {
@@ -478,7 +487,7 @@ func AlertMuteGetsAll(ctx *ctx.Context) ([]*AlertMute, error) {
// get my cluster's mutes
var lst []*AlertMute
if !ctx.IsCenter {
lst, err := poster.GetByUrls[[]*AlertMute](ctx, "/v1/n9e/alert-mutes?disabled=0")
lst, err := poster.GetByUrls[[]*AlertMute](ctx, "/v1/n9e/active-alert-mutes")
if err != nil {
return nil, err
}
@@ -490,6 +499,10 @@ func AlertMuteGetsAll(ctx *ctx.Context) ([]*AlertMute, error) {
session := DB(ctx).Model(&AlertMute{}).Where("disabled = 0")
// 只筛选在生效时间内的屏蔽规则, 这里 btime < now+10 是为了避免同步期间有规则满足了生效时间条件
now := time.Now().Unix()
session = session.Where("(mute_time_type = ? AND btime <= ? AND etime >= ?) OR mute_time_type = ?", TimeRange, now+10, now, Periodic)
err := session.Find(&lst).Error
if err != nil {
return nil, err

View File

@@ -228,6 +228,9 @@ func TargetTotal(ctx *ctx.Context, options ...BuildTargetWhereOption) (int64, er
func TargetGets(ctx *ctx.Context, limit, offset int, order string, desc bool, options ...BuildTargetWhereOption) ([]*Target, error) {
var lst []*Target
order = validateOrderField(order, "ident")
if desc {
order += " desc"
} else {

View File

@@ -3,6 +3,7 @@ package models
import (
"fmt"
"os"
"regexp"
"strconv"
"strings"
"time"
@@ -555,6 +556,47 @@ func UserTotal(ctx *ctx.Context, query string, stime, etime int64) (num int64, e
return num, nil
}
var (
// 预编译正则表达式,避免重复编译
whitespaceRegex = regexp.MustCompile(`\s+`)
validOrderRegex = regexp.MustCompile(`^[a-zA-Z_][a-zA-Z0-9_]*(\.[a-zA-Z_][a-zA-Z0-9_]*)?$`)
)
func validateOrderField(order string, defaultField string) string {
// 空值检查
if order == "" {
return defaultField
}
// 长度检查
if len(order) > 64 {
logger.Warningf("SQL injection attempt detected: order field too long (%d chars)", len(order))
return defaultField
}
// 移除所有空白字符
order = whitespaceRegex.ReplaceAllString(order, "")
if order == "" {
return defaultField
}
// 检查危险字符
orderLower := strings.ToLower(order)
if strings.ContainsAny(order, "();,'\"` --/*\\=+-*/><|&^~") ||
strings.Contains(orderLower, "0x") || strings.Contains(orderLower, "0b") {
logger.Warningf("SQL injection attempt detected: contains dangerous characters")
return defaultField
}
// 使用正则表达式验证格式:只允许字母开头的字段名,可选择性包含表名
if !validOrderRegex.MatchString(order) {
logger.Warningf("SQL injection attempt detected: invalid order field format")
return defaultField
}
return order
}
func UserGets(ctx *ctx.Context, query string, limit, offset int, stime, etime int64,
order string, desc bool, usernames, phones, emails []string) ([]User, error) {
@@ -564,6 +606,8 @@ func UserGets(ctx *ctx.Context, query string, limit, offset int, stime, etime in
session = session.Where("last_active_time between ? and ?", stime, etime)
}
order = validateOrderField(order, "username")
if desc {
order = order + " desc"
} else {

View File

@@ -192,6 +192,15 @@ var I18N = `{
"View Alerting Engines": "查看告警引擎列表",
"View Product Version": "查看产品版本",
"Some alert rules still in the BusiGroup": "业务组中仍有告警规则",
"Some alert mutes still in the BusiGroup": "业务组中仍有屏蔽规则",
"Some alert subscribes still in the BusiGroup": "业务组中仍有订阅规则",
"Some Board still in the BusiGroup": "业务组中仍有仪表盘",
"Some targets still in the BusiGroup": "业务组中仍有监控对象",
"Some recording rules still in the BusiGroup": "业务组中仍有记录规则",
"Some recovery scripts still in the BusiGroup": "业务组中仍有自愈脚本",
"Some target busigroups still in the BusiGroup": "业务组中仍有监控对象",
"---------zh_CN--------": "---------zh_CN--------"
},
"zh_HK": {
@@ -387,6 +396,15 @@ var I18N = `{
"View Alerting Engines": "查看告警引擎列表",
"View Product Version": "查看產品版本",
"Some alert rules still in the BusiGroup": "業務組中仍有告警規則",
"Some alert mutes still in the BusiGroup": "業務組中仍有屏蔽規則",
"Some alert subscribes still in the BusiGroup": "業務組中仍有訂閱規則",
"Some Board still in the BusiGroup": "業務組中仍有儀表板",
"Some targets still in the BusiGroup": "業務組中仍有監控對象",
"Some recording rules still in the BusiGroup": "業務組中仍有記錄規則",
"Some recovery scripts still in the BusiGroup": "業務組中仍有自愈腳本",
"Some target busigroups still in the BusiGroup": "業務組中仍有監控對象",
"---------zh_HK--------": "---------zh_HK--------"
},
"ja_JP": {
@@ -579,6 +597,15 @@ var I18N = `{
"View Alerting Engines": "アラートエンジンの表示",
"View Product Version": "製品のバージョンを見る",
"Some alert rules still in the BusiGroup": "ビジネスグループにまだアラートルールがあります",
"Some alert mutes still in the BusiGroup": "ビジネスグループにまだミュートルールがあります",
"Some alert subscribes still in the BusiGroup": "ビジネスグループにまだサブスクライブルールがあります",
"Some Board still in the BusiGroup": "ビジネスグループにまだダッシュボードがあります",
"Some targets still in the BusiGroup": "ビジネスグループにまだ監視対象があります",
"Some recording rules still in the BusiGroup": "ビジネスグループにまだ記録ルールがあります",
"Some recovery scripts still in the BusiGroup": "ビジネスグループにまだ自己回復スクリプトがあります",
"Some target busigroups still in the BusiGroup": "ビジネスグループにまだ監視対象があります",
"---------ja_JP--------": "---------ja_JP--------"
},
"ru_RU": {
@@ -771,6 +798,15 @@ var I18N = `{
"View Alerting Engines": "Просмотр списка алертинг-инженеров",
"View Product Version": "Просмотр версии продукта",
"Some alert rules still in the BusiGroup": "В бизнес-группе еще есть правила оповещений",
"Some alert mutes still in the BusiGroup": "В бизнес-группе еще есть правила отключения оповещений",
"Some alert subscribes still in the BusiGroup": "В бизнес-группе еще есть правила подписки",
"Some Board still in the BusiGroup": "В бизнес-группе еще есть панели мониторинга",
"Some targets still in the BusiGroup": "В бизнес-группе еще есть объекты мониторинга",
"Some recording rules still in the BusiGroup": "В бизнес-группе еще есть правила записи",
"Some recovery scripts still in the BusiGroup": "В бизнес-группе еще есть скрипты самоисцеления",
"Some target busigroups still in the BusiGroup": "В бизнес-группе еще есть объекты мониторинга",
"---------ru_RU--------": "---------ru_RU--------"
}
}`