mirror of
https://github.com/wonli/aqi.git
synced 2025-09-26 20:51:23 +08:00
日志过滤
This commit is contained in:
2
app.go
2
app.go
@@ -181,7 +181,7 @@ func Init(options ...Option) *AppConfig {
|
||||
var c config.Logger
|
||||
err = viper.UnmarshalKey(acf.LogPathKey, &c)
|
||||
if err != nil {
|
||||
color.Red("failed to init app log")
|
||||
color.Red("failed to init app log: " + err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
|
@@ -10,12 +10,13 @@ type Logger struct {
|
||||
LogFile string
|
||||
RuntimeLogFile string
|
||||
|
||||
LogPath string `yaml:"logPath"` // Path of the log file
|
||||
MaxSize int `yaml:"maxSize"` // Maximum log file size in MB
|
||||
MaxBackups int `yaml:"maxBackups"` // Maximum number of log file backups
|
||||
MaxAge int `yaml:"maxAge"` // Maximum number of days to retain log files
|
||||
Compress bool `yaml:"compress"` // Whether to enable gzip compression
|
||||
UseCaller bool `yaml:"useCaller"` // Whether to enable Zap Caller
|
||||
LogPath string `yaml:"logPath"` // Path of the log file
|
||||
LogFilter map[string]string `yaml:"logFilter"` // Filter
|
||||
MaxSize int `yaml:"maxSize"` // Maximum log file size in MB
|
||||
MaxBackups int `yaml:"maxBackups"` // Maximum number of log file backups
|
||||
MaxAge int `yaml:"maxAge"` // Maximum number of days to retain log files
|
||||
Compress bool `yaml:"compress"` // Whether to enable gzip compression
|
||||
UseCaller bool `yaml:"useCaller"` // Whether to enable Zap Caller
|
||||
}
|
||||
|
||||
// GetEncoder 根据模式获取编码器
|
||||
|
@@ -2,16 +2,19 @@ package logger
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/fatih/color"
|
||||
"github.com/wonli/aqi/internal/config"
|
||||
"go.uber.org/zap"
|
||||
"go.uber.org/zap/buffer"
|
||||
"go.uber.org/zap/zapcore"
|
||||
"gopkg.in/natefinch/lumberjack.v2"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var ZapLog *zap.Logger
|
||||
@@ -68,7 +71,7 @@ func Init(c config.Logger) {
|
||||
stdEncoder := newLimitLengthEncoder(c.GetEncoder(""), 300)
|
||||
stdLog := zapcore.NewCore(stdEncoder, zapcore.AddSync(os.Stdout), zap.InfoLevel)
|
||||
|
||||
fileEncoder := getFileStyleEncoder()
|
||||
fileEncoder := getFileStyleEncoder(c)
|
||||
fileLog := zapcore.NewCore(fileEncoder, zapcore.AddSync(&hook), zap.InfoLevel)
|
||||
rFileLog := zapcore.NewCore(fileEncoder, zapcore.AddSync(&runtimeHook), zap.InfoLevel)
|
||||
|
||||
@@ -106,7 +109,7 @@ func newLimitLengthEncoder(encoder zapcore.Encoder, limit int) zapcore.Encoder {
|
||||
}
|
||||
|
||||
// getFileStyleEncoder 获取文件风格的日志编码器
|
||||
func getFileStyleEncoder() zapcore.Encoder {
|
||||
func getFileStyleEncoder(c config.Logger) zapcore.Encoder {
|
||||
encoderConfig := zapcore.EncoderConfig{
|
||||
TimeKey: "", // 这些字段会在自定义格式中处理
|
||||
LevelKey: "",
|
||||
@@ -120,22 +123,69 @@ func getFileStyleEncoder() zapcore.Encoder {
|
||||
EncodeName: zapcore.FullNameEncoder,
|
||||
}
|
||||
|
||||
return &fileStyleEncoder{
|
||||
Encoder: zapcore.NewConsoleEncoder(encoderConfig),
|
||||
filters := make([]FilterRule, 0, len(c.LogFilter))
|
||||
if c.LogFilter != nil {
|
||||
for actionPattern, fieldRule := range c.LogFilter {
|
||||
// 解析字段路径和长度(格式:field.path:maxLen)
|
||||
ruleParts := strings.SplitN(fieldRule, ":", 2)
|
||||
if len(ruleParts) != 2 {
|
||||
continue
|
||||
}
|
||||
|
||||
fieldPath := strings.TrimSpace(ruleParts[0])
|
||||
maxLen, _ := strconv.Atoi(strings.TrimSpace(ruleParts[1]))
|
||||
if maxLen <= 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
patternStr := fmt.Sprintf(`"action"\s*:\s*"%s"`, regexp.QuoteMeta(actionPattern))
|
||||
pattern := regexp.MustCompile(patternStr)
|
||||
|
||||
fieldPattern := fmt.Sprintf(`("%s":\s*)(.*)`, regexp.QuoteMeta(fieldPath))
|
||||
fieldRegex := regexp.MustCompile(fieldPattern)
|
||||
|
||||
filters = append(filters, FilterRule{
|
||||
Action: actionPattern,
|
||||
Field: fieldPath,
|
||||
MaxLen: maxLen,
|
||||
Pattern: pattern,
|
||||
FieldRegex: fieldRegex,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return &fileStyleEncoder{
|
||||
Encoder: zapcore.NewConsoleEncoder(encoderConfig),
|
||||
base64Filter: regexp.MustCompile(`("data:[^"]*;base64,)([^"]*)`),
|
||||
logFilters: filters,
|
||||
}
|
||||
}
|
||||
|
||||
type FilterRule struct {
|
||||
Action string
|
||||
Field string
|
||||
MaxLen int
|
||||
Pattern *regexp.Regexp // 匹配action的正则
|
||||
FieldRegex *regexp.Regexp // 匹配字段的正则
|
||||
}
|
||||
|
||||
// fileStyleEncoder 自定义编码风格输出
|
||||
type fileStyleEncoder struct {
|
||||
zapcore.Encoder
|
||||
|
||||
base64Filter *regexp.Regexp
|
||||
logFilters []FilterRule
|
||||
}
|
||||
|
||||
func (e *fileStyleEncoder) EncodeEntry(entry zapcore.Entry, fields []zapcore.Field) (*buffer.Buffer, error) {
|
||||
// 处理自定义过滤规则
|
||||
if e.logFilters != nil {
|
||||
entry.Message = e.processMessage(entry.Message, e.logFilters)
|
||||
}
|
||||
|
||||
// 正则表达式过滤base64的主体
|
||||
filterRegex := regexp.MustCompile(`("data:[^"]*;base64,)([^"]*)`)
|
||||
if filterRegex.MatchString(entry.Message) {
|
||||
// 替换中间部分,保留前后部分
|
||||
entry.Message = filterRegex.ReplaceAllString(entry.Message, `$1..(replace)..`)
|
||||
if e.base64Filter.MatchString(entry.Message) {
|
||||
entry.Message = e.base64Filter.ReplaceAllString(entry.Message, `$1..(replace)..`)
|
||||
}
|
||||
|
||||
// 创建输出缓冲区
|
||||
@@ -203,3 +253,28 @@ func (e *fileStyleEncoder) EncodeEntry(entry zapcore.Entry, fields []zapcore.Fie
|
||||
|
||||
return buf, nil
|
||||
}
|
||||
|
||||
func (e *fileStyleEncoder) processMessage(msg string, filters []FilterRule) string {
|
||||
for _, rule := range filters {
|
||||
if strings.Contains(msg, rule.Action) && rule.Pattern.MatchString(msg) {
|
||||
msg = rule.FieldRegex.ReplaceAllStringFunc(msg, func(match string) string {
|
||||
parts := rule.FieldRegex.FindStringSubmatch(match)
|
||||
if len(parts) >= 3 {
|
||||
value := parts[2]
|
||||
if len(value) > rule.MaxLen {
|
||||
prefix := parts[1]
|
||||
// 确保不会从中文字符中间截断
|
||||
safeIndex := rule.MaxLen
|
||||
for safeIndex > 0 && !utf8.RuneStart(value[safeIndex]) {
|
||||
safeIndex--
|
||||
}
|
||||
return prefix + value[:safeIndex] + "..."
|
||||
}
|
||||
return match
|
||||
}
|
||||
return match
|
||||
})
|
||||
}
|
||||
}
|
||||
return msg
|
||||
}
|
||||
|
60
logger/zap_test.go
Normal file
60
logger/zap_test.go
Normal file
@@ -0,0 +1,60 @@
|
||||
package logger
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestZap(t *testing.T) {
|
||||
|
||||
logFilters := map[string]string{
|
||||
"article.list": "data:16",
|
||||
"res.uploadImg": "params:64",
|
||||
}
|
||||
|
||||
filters := make([]FilterRule, 0, len(logFilters))
|
||||
for actionPattern, fieldRule := range logFilters {
|
||||
// 解析字段路径和长度(格式:field.path:maxLen)
|
||||
ruleParts := strings.SplitN(fieldRule, ":", 2)
|
||||
if len(ruleParts) != 2 {
|
||||
continue
|
||||
}
|
||||
|
||||
fieldPath := strings.TrimSpace(ruleParts[0])
|
||||
maxLen, _ := strconv.Atoi(strings.TrimSpace(ruleParts[1]))
|
||||
if maxLen <= 0 {
|
||||
maxLen = 0
|
||||
}
|
||||
|
||||
patternStr := fmt.Sprintf(`"action":"%s"`, regexp.QuoteMeta(actionPattern))
|
||||
pattern := regexp.MustCompile(patternStr)
|
||||
|
||||
fieldPattern := fmt.Sprintf(`("%s":\s*)(.*)`, regexp.QuoteMeta(fieldPath))
|
||||
fieldRegex := regexp.MustCompile(fieldPattern)
|
||||
|
||||
filters = append(filters, FilterRule{
|
||||
Action: actionPattern,
|
||||
Field: fieldPath,
|
||||
MaxLen: maxLen,
|
||||
Pattern: pattern,
|
||||
FieldRegex: fieldRegex,
|
||||
})
|
||||
}
|
||||
|
||||
// 测试消息
|
||||
msg1 := `{"action":"res.uploadImg","params":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAYoAAAGICAYAAABftLn"}`
|
||||
msg2 := `{"code":0,"action":"article.list","data":{"page":{"current":1,"pageSize":10,"total":5}}}`
|
||||
|
||||
fs := fileStyleEncoder{}
|
||||
|
||||
// 处理消息
|
||||
processedMsg1 := fs.processMessage(msg1, filters)
|
||||
log.Println("处理后的消息1:", processedMsg1)
|
||||
|
||||
processedMsg2 := fs.processMessage(msg2, filters)
|
||||
log.Println("处理后的消息2:", processedMsg2)
|
||||
}
|
Reference in New Issue
Block a user