44 lines
1.6 KiB
Go
44 lines
1.6 KiB
Go
package service
|
|
|
|
import (
|
|
"crypto/md5"
|
|
"crypto/sha256"
|
|
"encoding/hex"
|
|
"regexp"
|
|
"strings"
|
|
)
|
|
|
|
var (
|
|
reUUID = regexp.MustCompile(`[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}`)
|
|
reISOTime = regexp.MustCompile(`\d{4}-\d{2}-\d{2}[T ]\d{2}:\d{2}:\d{2}(\.\d+)?(Z|[+-]\d{2}:?\d{2})?`)
|
|
reUnixMs = regexp.MustCompile(`\b1[4-9]\d{12}\b`) // unix 毫秒级时间戳
|
|
reRequestID = regexp.MustCompile(`\b(req[-_]?|request[-_]?|rid[-_:]?)[-_:]?[0-9a-zA-Z-]{4,}\b`)
|
|
reLongNum = regexp.MustCompile(`\b\d{4,}\b`) // 连续≥4位数字
|
|
)
|
|
|
|
// normalizeErrorMsg 归一化错误消息:剔除易变片段(UUID/时间戳/请求ID/连续数字),
|
|
// 使同因不同实例的错误命中同一记忆键。
|
|
func normalizeErrorMsg(msg string) string {
|
|
m := msg
|
|
m = reUUID.ReplaceAllString(m, "{uuid}")
|
|
m = reISOTime.ReplaceAllString(m, "{time}")
|
|
m = reUnixMs.ReplaceAllString(m, "{ts}")
|
|
m = reRequestID.ReplaceAllString(m, "{reqid}")
|
|
m = reLongNum.ReplaceAllString(m, "{num}")
|
|
return strings.TrimSpace(m)
|
|
}
|
|
|
|
// buildMemoryKey 构造记忆键 = SHA-256(upstream|error_code|归一化消息)。
|
|
// 含失败上游维度:不同上游的同类错误互不串扰。
|
|
func buildMemoryKey(upstream, code, msg string) string {
|
|
raw := strings.Join([]string{upstream, code, normalizeErrorMsg(msg)}, "|")
|
|
sum := sha256.Sum256([]byte(raw))
|
|
return hex.EncodeToString(sum[:])
|
|
}
|
|
|
|
// msgFingerprint 归一化消息的 md5(观测/展示用)。
|
|
func msgFingerprint(msg string) string {
|
|
sum := md5.Sum([]byte(normalizeErrorMsg(msg)))
|
|
return hex.EncodeToString(sum[:])
|
|
}
|