341 lines
8.6 KiB
Go
341 lines
8.6 KiB
Go
|
package http
|
|||
|
|
|||
|
import (
|
|||
|
"fmt"
|
|||
|
"regexp"
|
|||
|
"strings"
|
|||
|
"time"
|
|||
|
|
|||
|
"github.com/gin-gonic/gin"
|
|||
|
"github.com/toolkits/pkg/logger"
|
|||
|
|
|||
|
"github.com/didi/nightingale/v5/cache"
|
|||
|
"github.com/didi/nightingale/v5/models"
|
|||
|
)
|
|||
|
|
|||
|
type collectRuleForm struct {
|
|||
|
ClasspathId int64 `json:"classpath_id"`
|
|||
|
PrefixMatch int `json:"prefix_match"`
|
|||
|
Name string `json:"name"`
|
|||
|
Note string `json:"note"`
|
|||
|
Step int `json:"step"`
|
|||
|
Type string `json:"type"`
|
|||
|
Data string `json:"data"`
|
|||
|
AppendTags string `json:"append_tags"`
|
|||
|
}
|
|||
|
|
|||
|
func collectRuleAdd(c *gin.Context) {
|
|||
|
var f collectRuleForm
|
|||
|
bind(c, &f)
|
|||
|
|
|||
|
me := loginUser(c).MustPerm("collect_rule_create")
|
|||
|
|
|||
|
cr := models.CollectRule{
|
|||
|
ClasspathId: f.ClasspathId,
|
|||
|
PrefixMatch: f.PrefixMatch,
|
|||
|
Name: f.Name,
|
|||
|
Note: f.Note,
|
|||
|
Step: f.Step,
|
|||
|
Type: f.Type,
|
|||
|
Data: f.Data,
|
|||
|
AppendTags: f.AppendTags,
|
|||
|
CreateBy: me.Username,
|
|||
|
UpdateBy: me.Username,
|
|||
|
}
|
|||
|
|
|||
|
renderMessage(c, cr.Add())
|
|||
|
}
|
|||
|
|
|||
|
func collectRulePut(c *gin.Context) {
|
|||
|
var f collectRuleForm
|
|||
|
bind(c, &f)
|
|||
|
|
|||
|
me := loginUser(c).MustPerm("collect_rule_modify")
|
|||
|
cr := CollectRule(urlParamInt64(c, "id"))
|
|||
|
|
|||
|
cr.PrefixMatch = f.PrefixMatch
|
|||
|
cr.Name = f.Name
|
|||
|
cr.Note = f.Note
|
|||
|
cr.Step = f.Step
|
|||
|
cr.Type = f.Type
|
|||
|
cr.Data = f.Data
|
|||
|
cr.AppendTags = f.AppendTags
|
|||
|
cr.UpdateAt = time.Now().Unix()
|
|||
|
cr.UpdateBy = me.Username
|
|||
|
|
|||
|
renderMessage(c, cr.Update(
|
|||
|
"prefix_match",
|
|||
|
"name",
|
|||
|
"note",
|
|||
|
"step",
|
|||
|
"type",
|
|||
|
"data",
|
|||
|
"update_at",
|
|||
|
"update_by",
|
|||
|
"append_tags",
|
|||
|
))
|
|||
|
}
|
|||
|
|
|||
|
func collectRuleDel(c *gin.Context) {
|
|||
|
var f idsForm
|
|||
|
bind(c, &f)
|
|||
|
f.Validate()
|
|||
|
loginUser(c).MustPerm("collect_rule_delete")
|
|||
|
renderMessage(c, models.CollectRulesDel(f.Ids))
|
|||
|
}
|
|||
|
|
|||
|
func collectRuleGets(c *gin.Context) {
|
|||
|
classpathId := urlParamInt64(c, "id")
|
|||
|
|
|||
|
where := "classpath_id = ?"
|
|||
|
param := []interface{}{classpathId}
|
|||
|
|
|||
|
typ := queryStr(c, "type", "")
|
|||
|
if typ != "" {
|
|||
|
where += " and type = ?"
|
|||
|
param = append(param, typ)
|
|||
|
}
|
|||
|
|
|||
|
objs, err := models.CollectRuleGets(where, param...)
|
|||
|
renderData(c, objs, err)
|
|||
|
}
|
|||
|
|
|||
|
func collectRuleGetsByIdent(c *gin.Context) {
|
|||
|
ident := queryStr(c, "ident")
|
|||
|
|
|||
|
objs := cache.CollectRulesOfIdent.GetBy(ident)
|
|||
|
renderData(c, objs, nil)
|
|||
|
}
|
|||
|
|
|||
|
type Summary struct {
|
|||
|
LatestUpdatedAt int64 `json:"latestUpdatedAt"`
|
|||
|
Total int `json:"total"`
|
|||
|
}
|
|||
|
|
|||
|
func collectRuleSummaryGetByIdent(c *gin.Context) {
|
|||
|
ident := queryStr(c, "ident")
|
|||
|
var summary Summary
|
|||
|
objs := cache.CollectRulesOfIdent.GetBy(ident)
|
|||
|
total := len(objs)
|
|||
|
if total > 0 {
|
|||
|
summary.Total = total
|
|||
|
var latestUpdatedAt int64
|
|||
|
for _, obj := range objs {
|
|||
|
if latestUpdatedAt < obj.UpdateAt {
|
|||
|
latestUpdatedAt = obj.UpdateAt
|
|||
|
}
|
|||
|
}
|
|||
|
summary.LatestUpdatedAt = latestUpdatedAt
|
|||
|
}
|
|||
|
|
|||
|
renderData(c, summary, nil)
|
|||
|
}
|
|||
|
|
|||
|
type RegExpCheck struct {
|
|||
|
Success bool `json:"success"`
|
|||
|
Data []map[string]string `json:"tags"`
|
|||
|
}
|
|||
|
|
|||
|
var RegExpExcludePatition string = "```EXCLUDE```"
|
|||
|
|
|||
|
func regExpCheck(c *gin.Context) {
|
|||
|
param := make(map[string]string)
|
|||
|
dangerous(c.ShouldBind(¶m))
|
|||
|
|
|||
|
ret := &RegExpCheck{
|
|||
|
Success: true,
|
|||
|
Data: make([]map[string]string, 0),
|
|||
|
}
|
|||
|
|
|||
|
// 处理时间格式
|
|||
|
if t, ok := param["time"]; !ok || t == "" {
|
|||
|
tmp := map[string]string{"time": "time参数不存在或为空"}
|
|||
|
ret.Data = append(ret.Data, tmp)
|
|||
|
} else {
|
|||
|
timePat, _ := GetPatAndTimeFormat(param["time"])
|
|||
|
if timePat == "" {
|
|||
|
tmp := map[string]string{"time": genErrMsg("时间格式")}
|
|||
|
ret.Data = append(ret.Data, tmp)
|
|||
|
} else {
|
|||
|
suc, tRes, _ := checkRegPat(timePat, param["log"], true)
|
|||
|
if !suc {
|
|||
|
ret.Success = false
|
|||
|
tRes = genErrMsg("时间格式")
|
|||
|
}
|
|||
|
tmp := map[string]string{"time": tRes}
|
|||
|
ret.Data = append(ret.Data, tmp)
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// 计算方式
|
|||
|
calc_method := param["calc_method"]
|
|||
|
|
|||
|
// 处理主正则(with exclude)
|
|||
|
if re, ok := param["re"]; !ok || re == "" {
|
|||
|
tmp := map[string]string{"re": "re参数不存在或为空"}
|
|||
|
ret.Data = append(ret.Data, tmp)
|
|||
|
} else {
|
|||
|
// 处理exclude的情况
|
|||
|
exclude := ""
|
|||
|
if strings.Contains(re, RegExpExcludePatition) {
|
|||
|
l := strings.Split(re, RegExpExcludePatition)
|
|||
|
if len(l) >= 2 {
|
|||
|
param["re"] = l[0]
|
|||
|
exclude = l[1]
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// 匹配主正则
|
|||
|
suc, reRes, isSub := checkRegPat(param["re"], param["log"], false)
|
|||
|
if !suc {
|
|||
|
ret.Success = false
|
|||
|
reRes = genErrMsg("主正则")
|
|||
|
}
|
|||
|
if calc_method != "" && calc_method != "cnt" && !isSub {
|
|||
|
ret.Success = false
|
|||
|
reRes = genSubErrMsg("主正则")
|
|||
|
}
|
|||
|
tmp := map[string]string{"主正则": reRes}
|
|||
|
ret.Data = append(ret.Data, tmp)
|
|||
|
|
|||
|
// 匹配exclude, 这个不影响失败
|
|||
|
if exclude != "" {
|
|||
|
suc, exRes, _ := checkRegPat(exclude, param["log"], false)
|
|||
|
if !suc {
|
|||
|
//ret.Success = false
|
|||
|
exRes = "未匹配到排除串,请检查是否符合预期"
|
|||
|
}
|
|||
|
tmp := map[string]string{"排除串": exRes}
|
|||
|
ret.Data = append(ret.Data, tmp)
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// 处理tags
|
|||
|
var nonTagKey = map[string]bool{
|
|||
|
"re": true,
|
|||
|
"log": true,
|
|||
|
"time": true,
|
|||
|
"calc_method": true,
|
|||
|
}
|
|||
|
|
|||
|
for tagk, pat := range param {
|
|||
|
// 如果不是tag,就继续循环
|
|||
|
if _, ok := nonTagKey[tagk]; ok {
|
|||
|
continue
|
|||
|
}
|
|||
|
suc, tagRes, isSub := checkRegPat(pat, param["log"], false)
|
|||
|
if !suc {
|
|||
|
// 正则错误
|
|||
|
ret.Success = false
|
|||
|
tagRes = genErrMsg(tagk)
|
|||
|
} else if !isSub {
|
|||
|
// 未匹配出子串
|
|||
|
ret.Success = false
|
|||
|
tagRes = genSubErrMsg(tagk)
|
|||
|
} else if includeIllegalChar(tagRes) || includeIllegalChar(tagk) {
|
|||
|
// 保留字报错
|
|||
|
ret.Success = false
|
|||
|
tagRes = genIllegalCharErrMsg()
|
|||
|
}
|
|||
|
|
|||
|
tmp := map[string]string{tagk: tagRes}
|
|||
|
ret.Data = append(ret.Data, tmp)
|
|||
|
}
|
|||
|
|
|||
|
renderData(c, ret, nil)
|
|||
|
}
|
|||
|
|
|||
|
//根据配置的时间格式,获取对应的正则匹配pattern和time包用的时间格式
|
|||
|
func GetPatAndTimeFormat(tf string) (string, string) {
|
|||
|
var pat, timeFormat string
|
|||
|
switch tf {
|
|||
|
case "dd/mmm/yyyy:HH:MM:SS":
|
|||
|
pat = `([012][0-9]|3[01])/[JFMASOND][a-z]{2}/(2[0-9]{3}):([01][0-9]|2[0-4])(:[012345][0-9]){2}`
|
|||
|
timeFormat = "02/Jan/2006:15:04:05"
|
|||
|
case "dd/mmm/yyyy HH:MM:SS":
|
|||
|
pat = `([012][0-9]|3[01])/[JFMASOND][a-z]{2}/(2[0-9]{3})\s([01][0-9]|2[0-4])(:[012345][0-9]){2}`
|
|||
|
timeFormat = "02/Jan/2006 15:04:05"
|
|||
|
case "yyyy-mm-ddTHH:MM:SS":
|
|||
|
pat = `(2[0-9]{3})-(0[1-9]|1[012])-([012][0-9]|3[01])T([01][0-9]|2[0-4])(:[012345][0-9]){2}`
|
|||
|
timeFormat = "2006-01-02T15:04:05"
|
|||
|
case "dd-mmm-yyyy HH:MM:SS":
|
|||
|
pat = `([012][0-9]|3[01])-[JFMASOND][a-z]{2}-(2[0-9]{3})\s([01][0-9]|2[0-4])(:[012345][0-9]){2}`
|
|||
|
timeFormat = "02-Jan-2006 15:04:05"
|
|||
|
case "yyyy-mm-dd HH:MM:SS":
|
|||
|
pat = `(2[0-9]{3})-(0[1-9]|1[012])-([012][0-9]|3[01])\s([01][0-9]|2[0-4])(:[012345][0-9]){2}`
|
|||
|
timeFormat = "2006-01-02 15:04:05"
|
|||
|
case "yyyy/mm/dd HH:MM:SS":
|
|||
|
pat = `(2[0-9]{3})/(0[1-9]|1[012])/([012][0-9]|3[01])\s([01][0-9]|2[0-4])(:[012345][0-9]){2}`
|
|||
|
timeFormat = "2006/01/02 15:04:05"
|
|||
|
case "yyyymmdd HH:MM:SS":
|
|||
|
pat = `(2[0-9]{3})(0[1-9]|1[012])([012][0-9]|3[01])\s([01][0-9]|2[0-4])(:[012345][0-9]){2}`
|
|||
|
timeFormat = "20060102 15:04:05"
|
|||
|
case "mmm dd HH:MM:SS":
|
|||
|
pat = `[JFMASOND][a-z]{2}\s+([1-9]|[1-2][0-9]|3[01])\s([01][0-9]|2[0-4])(:[012345][0-9]){2}`
|
|||
|
timeFormat = "Jan 2 15:04:05"
|
|||
|
case "mmdd HH:MM:SS":
|
|||
|
pat = `(0[1-9]|1[012])([012][0-9]|3[01])\s([01][0-9]|2[0-4])(:[012345][0-9]){2}`
|
|||
|
timeFormat = "0102 15:04:05"
|
|||
|
case "dd mmm yyyy HH:MM:SS":
|
|||
|
pat = `([012][0-9]|3[01])\s+[JFMASOND][a-z]{2}\s+(2[0-9]{3})\s([01][0-9]|2[0-4])(:[012345][0-9]){2}`
|
|||
|
timeFormat = "02 Jan 2006 15:04:05"
|
|||
|
default:
|
|||
|
logger.Errorf("match time pac failed : [timeFormat:%s]", tf)
|
|||
|
return "", ""
|
|||
|
}
|
|||
|
return pat, timeFormat
|
|||
|
}
|
|||
|
|
|||
|
// 出错信息直接放在body里
|
|||
|
func checkRegPat(pat string, log string, origin bool) (succ bool, result string, isSub bool) {
|
|||
|
if pat == "" {
|
|||
|
return false, "", false
|
|||
|
}
|
|||
|
|
|||
|
reg, err := regexp.Compile(pat)
|
|||
|
if err != nil {
|
|||
|
return false, "", false
|
|||
|
}
|
|||
|
|
|||
|
res := reg.FindStringSubmatch(log)
|
|||
|
switch len(res) {
|
|||
|
// 没查到
|
|||
|
case 0:
|
|||
|
return false, "", false
|
|||
|
// 没查到括号内的串,返回整个匹配串
|
|||
|
case 1:
|
|||
|
return true, res[0], false
|
|||
|
// 查到了,默认取第一个串
|
|||
|
default:
|
|||
|
var msg string
|
|||
|
if origin {
|
|||
|
msg = res[0]
|
|||
|
isSub = false
|
|||
|
} else {
|
|||
|
msg = res[1]
|
|||
|
isSub = true
|
|||
|
}
|
|||
|
return true, msg, isSub
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
func includeIllegalChar(s string) bool {
|
|||
|
illegalChars := ":,=\r\n\t"
|
|||
|
return strings.ContainsAny(s, illegalChars)
|
|||
|
}
|
|||
|
|
|||
|
// 生成返回错误信息
|
|||
|
func genErrMsg(sign string) string {
|
|||
|
return fmt.Sprintf("正则匹配失败,请认真检查您[%s]的配置", sign)
|
|||
|
}
|
|||
|
|
|||
|
// 生成子串匹配错误信息
|
|||
|
func genSubErrMsg(sign string) string {
|
|||
|
return fmt.Sprintf("正则匹配成功。但根据配置,并没有获取到()内的子串,请认真检查您[%s]的配置", sign)
|
|||
|
}
|
|||
|
|
|||
|
// 生成子串匹配错误信息
|
|||
|
func genIllegalCharErrMsg() string {
|
|||
|
return `正则匹配成功。但是tag的key或者value包含非法字符:[:,/=\r\n\t], 请重新调整`
|
|||
|
}
|