Angie rps

This commit is contained in:
Magnus Root 2026-05-15 17:56:04 +03:00
parent 10fd555fa9
commit 8464a41b6e
3 changed files with 50 additions and 7 deletions

View file

@ -435,9 +435,10 @@ func buildErrorRateAlertRules(client ClientData, domain DomainInfo, errCode, lim
if statusField == "upstream_status" {
fieldDesc = "ответ от origin"
}
circle := "🟡"
summary := fmt.Sprintf(
"🔴 %s / %s (SID:%s) — %s %s ошибок трафика держится > %s.",
client.ClientTitle, domain.DomainName, domain.SID, codeName, fieldShort, forDuration,
"%s %s / %s (SID:%s) — %s ошибок > %d%% держится > %s. [%s]",
circle, client.ClientTitle, domain.DomainName, domain.SID, codeName, limitPct, forDuration, fieldShort,
)
thresholdAnnotation := fmt.Sprintf(">%d%% для %s (%s)", limitPct, statusField, fieldDesc)
fieldInfoAnnotation := fmt.Sprintf("для %s (%s)", statusField, fieldDesc)
@ -468,6 +469,7 @@ func buildErrorRateAlertRules(client ClientData, domain DomainInfo, errCode, lim
"threshold": thresholdAnnotation,
"threshold_value": thresholdValueAnnotation,
"field_info": fieldInfoAnnotation,
"severity": "warning",
},
"notification_settings": map[string]string{"receiver": receiver},
"data": []interface{}{

View file

@ -221,9 +221,10 @@ func buildErrorRateAlertRulesVL(client ClientData, domain DomainInfo, errCode, l
if statusField == "upstream_status" {
fieldDesc = "ответ от origin"
}
circle := "🟡"
summary := fmt.Sprintf(
"🔴 %s / %s (SID:%s) — %s %s ошибок трафика держится > %s.",
client.ClientTitle, domain.DomainName, domain.SID, codeName, fieldShort, forDuration,
"%s %s / %s (SID:%s) — %s ошибок > %d%% держится > %s. [%s]",
circle, client.ClientTitle, domain.DomainName, domain.SID, codeName, limitPct, forDuration, fieldShort,
)
thresholdAnnotation := fmt.Sprintf(">%d%% для %s (%s)", limitPct, statusField, fieldDesc)
fieldInfoAnnotation := fmt.Sprintf("для %s (%s)", statusField, fieldDesc)
@ -286,6 +287,7 @@ func buildErrorRateAlertRulesVL(client ClientData, domain DomainInfo, errCode, l
"threshold": thresholdAnnotation,
"threshold_value": thresholdValueAnnotation,
"field_info": fieldInfoAnnotation,
"severity": "warning",
},
"notification_settings": map[string]string{"receiver": receiver},
"data": []interface{}{

View file

@ -451,16 +451,37 @@ func buildVLBucketLogsTargets(cfg map[string]interface{}, _ string, _ string, da
}
}
// Также поддерживаем прямое указание terms_field в query_config
if tf := getString(cfg, "terms_field"); tf != "" {
termsField = tf
}
if termsField == "" {
termsField = "_msg"
}
// vl_metric: "rps" — делим count на интервал для получения RPS
vlMetric := getString(cfg, "vl_metric")
vlInterval := getString(cfg, "vl_interval")
if vlInterval == "" {
vlInterval = "1m"
}
// LogsQL: группировка по полю + top N
var expr string
if termsSize > 0 {
expr = fmt.Sprintf("%s | stats by (%s) count() hits | sort by (hits desc) | limit %d", baseQuery, termsField, termsSize)
if vlMetric == "rps" {
// RPS: stats by (time:interval, field) count() / interval_seconds
if termsSize > 0 {
expr = fmt.Sprintf("%s | stats by (_time:%s, %s) count() hits | sort by (hits desc) | limit %d", baseQuery, vlInterval, termsField, termsSize)
} else {
expr = fmt.Sprintf("%s | stats by (_time:%s, %s) count() hits", baseQuery, vlInterval, termsField)
}
} else {
expr = fmt.Sprintf("%s | stats by (%s) count() hits", baseQuery, termsField)
if termsSize > 0 {
expr = fmt.Sprintf("%s | stats by (%s) count() hits | sort by (hits desc) | limit %d", baseQuery, termsField, termsSize)
} else {
expr = fmt.Sprintf("%s | stats by (%s) count() hits", baseQuery, termsField)
}
}
// legendFormat с именем поля — плагин подставит значение поля группировки
@ -469,6 +490,24 @@ func buildVLBucketLogsTargets(cfg map[string]interface{}, _ string, _ string, da
makeVLStatsTarget("A", expr, datasourceUID, legendFormat),
}
// Для RPS добавляем math expression $A / 60 (интервал 1m = 60 секунд)
if vlMetric == "rps" {
// Скрываем raw query A, показываем только RPS
// legendFormat задаём на raw target — math expression наследует метки
rawTarget := makeVLStatsTarget("A", expr, datasourceUID, legendFormat)
rawTarget["hide"] = true
mathExpr := map[string]interface{}{
"refId": "RPS",
"datasource": map[string]interface{}{"type": "__expr__", "uid": "__expr__"},
"type": "math",
"expression": "$A / 60",
}
targets = []interface{}{
rawTarget,
mathExpr,
}
}
// Если задан unknown_label — добавляем отдельный target для записей с пустым значением поля
unknownLabel := getString(cfg, "unknown_label")
if unknownLabel != "" {