From 9aa5b0dcc6f3b45d7325df0c44c864df0ecd3ff0 Mon Sep 17 00:00:00 2001 From: douxu Date: Thu, 5 Jun 2025 15:56:40 +0800 Subject: [PATCH] refactor(logger): 1. optimize the logger log module design and add link tracking related designs 2. add logger facade functions to simplify the use of alarm functions --- config/model_config.go | 2 +- logger/facede.go | 54 ++++++++++++++++++ logger/logger.go | 109 +++++++++++++++++++++++++++++++++++++ logger/{init.go => zap.go} | 8 +-- main.go | 25 ++++----- middleware/trace.go | 23 ++++++++ util/trace.go | 45 +++++++++++++++ 7 files changed, 247 insertions(+), 19 deletions(-) create mode 100644 logger/facede.go create mode 100644 logger/logger.go rename logger/{init.go => zap.go} (89%) create mode 100644 middleware/trace.go create mode 100644 util/trace.go diff --git a/config/model_config.go b/config/model_config.go index dedc93c..e626070 100644 --- a/config/model_config.go +++ b/config/model_config.go @@ -9,6 +9,6 @@ import ( type ModelParseConfig struct { ComponentInfo orm.Component - Context context.Context + Ctx context.Context AnchorChan chan AnchorParamConfig } diff --git a/logger/facede.go b/logger/facede.go new file mode 100644 index 0000000..b9a5b54 --- /dev/null +++ b/logger/facede.go @@ -0,0 +1,54 @@ +// Package logger define log struct of modelRT project +package logger + +import ( + "context" + "sync" + + "go.uber.org/zap" + "go.uber.org/zap/zapcore" +) + +var ( + f *facade + fOnce sync.Once +) + +type facade struct { + _logger *zap.Logger +} + +// Debug define facade func of debug level log +func Debug(ctx context.Context, msg string, kv ...any) { + logFacade().log(ctx, zapcore.DebugLevel, msg, kv...) +} + +// Info define facade func of info level log +func Info(ctx context.Context, msg string, kv ...any) { + logFacade().log(ctx, zapcore.InfoLevel, msg, kv...) +} + +// Warn define facade func of warn level log +func Warn(ctx context.Context, msg string, kv ...any) { + logFacade().log(ctx, zapcore.WarnLevel, msg, kv...) +} + +// Error define facade func of error level log +func Error(ctx context.Context, msg string, kv ...any) { + logFacade().log(ctx, zapcore.ErrorLevel, msg, kv...) +} + +func (f *facade) log(ctx context.Context, lvl zapcore.Level, msg string, kv ...any) { + fields := makeLogFields(ctx, kv...) + ce := f._logger.Check(lvl, msg) + ce.Write(fields...) +} + +func logFacade() *facade { + fOnce.Do(func() { + f = &facade{ + _logger: GetLoggerInstance(), + } + }) + return f +} diff --git a/logger/logger.go b/logger/logger.go new file mode 100644 index 0000000..eb97838 --- /dev/null +++ b/logger/logger.go @@ -0,0 +1,109 @@ +// Package logger define log struct of modelRT project +package logger + +import ( + "context" + "path" + "runtime" + + "go.uber.org/zap" + "go.uber.org/zap/zapcore" +) + +type logger struct { + ctx context.Context + traceID string + spanID string + pSpanID string + _logger *zap.Logger +} + +func (l *logger) Debug(msg string, kv ...any) { + l.log(zapcore.DebugLevel, msg, kv...) +} + +func (l *logger) Info(msg string, kv ...any) { + l.log(zapcore.InfoLevel, msg, kv...) +} + +func (l *logger) Warn(msg string, kv ...any) { + l.log(zapcore.WarnLevel, msg, kv...) +} + +func (l *logger) Error(msg string, kv ...any) { + l.log(zapcore.ErrorLevel, msg, kv...) +} + +func (l *logger) log(lvl zapcore.Level, msg string, kv ...any) { + fields := makeLogFields(l.ctx, kv...) + ce := l._logger.Check(lvl, msg) + ce.Write(fields...) +} + +func makeLogFields(ctx context.Context, kv ...any) []zap.Field { + // Ensure that log information appears in pairs in the form of key-value pairs + if len(kv)%2 != 0 { + kv = append(kv, "unknown") + } + + kv = append(kv, "traceID", ctx.Value("traceID"), "spanID", ctx.Value("spanID"), "pspanID", ctx.Value("pspanID")) + + funcName, file, line := getLoggerCallerInfo() + kv = append(kv, "func", funcName, "file", file, "line", line) + fields := make([]zap.Field, 0, len(kv)/2) + for i := 0; i < len(kv); i += 2 { + key := kv[i].(string) + value := kv[i+1] + switch v := value.(type) { + case string: + fields = append(fields, zap.String(key, v)) + case int: + fields = append(fields, zap.Int(key, v)) + case int64: + fields = append(fields, zap.Int64(key, v)) + case float32: + fields = append(fields, zap.Float32(key, v)) + case float64: + fields = append(fields, zap.Float64(key, v)) + case bool: + fields = append(fields, zap.Bool(key, v)) + case error: + fields = append(fields, zap.Error(v)) + default: + fields = append(fields, zap.Any(key, v)) + } + } + return fields +} + +// getLoggerCallerInfo define func of return log caller information、method name、file name、line number +func getLoggerCallerInfo() (funcName, file string, line int) { + pc, file, line, ok := runtime.Caller(4) + if !ok { + return + } + file = path.Base(file) + funcName = runtime.FuncForPC(pc).Name() + return +} + +func New(ctx context.Context) *logger { + var traceID, spanID, pSpanID string + if ctx.Value("traceID") != nil { + traceID = ctx.Value("traceID").(string) + } + if ctx.Value("spanID") != nil { + spanID = ctx.Value("spanID").(string) + } + if ctx.Value("psapnID") != nil { + pSpanID = ctx.Value("pspanID").(string) + } + + return &logger{ + ctx: ctx, + traceID: traceID, + spanID: spanID, + pSpanID: pSpanID, + _logger: GetLoggerInstance(), + } +} diff --git a/logger/init.go b/logger/zap.go similarity index 89% rename from logger/init.go rename to logger/zap.go index a537bf8..18270a6 100644 --- a/logger/init.go +++ b/logger/zap.go @@ -68,15 +68,15 @@ func initLogger(lCfg config.LoggerConfig) *zap.Logger { return logger } -// InitLoggerInstance return instance of zap logger -func InitLoggerInstance(lCfg config.LoggerConfig) *zap.Logger { +// InitLoggerInstance define func of return instance of zap logger +func InitLoggerInstance(lCfg config.LoggerConfig) { once.Do(func() { _globalLogger = initLogger(lCfg) }) - return _globalLogger + defer _globalLogger.Sync() } -// GetLoggerInstance returns the global logger instance It's safe for concurrent use. +// GetLoggerInstance define func of returns the global logger instance It's safe for concurrent use. func GetLoggerInstance() *zap.Logger { _globalLoggerMu.RLock() logger := _globalLogger diff --git a/main.go b/main.go index b2604cd..ccc53a2 100644 --- a/main.go +++ b/main.go @@ -24,7 +24,6 @@ import ( "github.com/gin-gonic/gin" "github.com/panjf2000/ants/v2" - "go.uber.org/zap" "gorm.io/gorm" ) @@ -43,7 +42,6 @@ var ( var ( modelRTConfig config.ModelRTConfig postgresDBClient *gorm.DB - zapLogger *zap.Logger alertManager *alert.EventManager ) @@ -65,8 +63,7 @@ func main() { }() // init logger - zapLogger = logger.InitLoggerInstance(modelRTConfig.LoggerConfig) - defer zapLogger.Sync() + logger.InitLoggerInstance(modelRTConfig.LoggerConfig) // init alert manager _ = alert.InitAlertEventManager() @@ -74,7 +71,7 @@ func main() { // init model parse ants pool parsePool, err := ants.NewPoolWithFunc(modelRTConfig.ParseConcurrentQuantity, pool.ParseFunc) if err != nil { - zapLogger.Error("init concurrent parse task pool failed", zap.Error(err)) + logger.Error(ctx, "init concurrent parse task pool failed", "error", err) panic(err) } defer parsePool.Release() @@ -88,7 +85,7 @@ func main() { // init anchor param ants pool anchorRealTimePool, err := pool.AnchorPoolInit(modelRTConfig.RTDReceiveConcurrentQuantity) if err != nil { - zapLogger.Error("init concurrent anchor param task pool failed", zap.Error(err)) + logger.Error(ctx, "init concurrent anchor param task pool failed", "error", err) panic(err) } defer anchorRealTimePool.Release() @@ -101,16 +98,16 @@ func main() { postgresDBClient.Transaction(func(tx *gorm.DB) error { // load circuit diagram from postgres - componentTypeMap, err := database.QueryCircuitDiagramComponentFromDB(cancelCtx, tx, parsePool, zapLogger) + componentTypeMap, err := database.QueryCircuitDiagramComponentFromDB(cancelCtx, tx, parsePool) if err != nil { - zapLogger.Error("load circuit diagrams from postgres failed", zap.Error(err)) + logger.Error(ctx, "load circuit diagrams from postgres failed", "error", err) panic(err) } // TODO 暂时屏蔽完成 swagger 启动测试 - tree, err := database.QueryTopologicFromDB(ctx, tx, zapLogger, componentTypeMap) + tree, err := database.QueryTopologicFromDB(ctx, tx, componentTypeMap) if err != nil { - zapLogger.Error("load topologic info from postgres failed", zap.Error(err)) + logger.Error(ctx, "load topologic info from postgres failed", "error", err) panic(err) } diagram.GlobalTree = tree @@ -125,10 +122,10 @@ func main() { engine.Use(limiter.Middleware) // diagram api - engine.GET("/model/diagram_load", handler.CircuitDiagramLoadHandler) - engine.POST("/model/diagram_create", handler.CircuitDiagramCreateHandler) - engine.POST("/model/diagram_update", handler.CircuitDiagramUpdateHandler) - engine.POST("/model/diagram_delete", handler.CircuitDiagramDeleteHandler) + engine.GET("/diagram/load", handler.CircuitDiagramLoadHandler) + engine.POST("/diagram/create", handler.CircuitDiagramCreateHandler) + engine.POST("/diagram/update", handler.CircuitDiagramUpdateHandler) + engine.POST("/diagram/delete", handler.CircuitDiagramDeleteHandler) // real time data api engine.GET("/ws/rtdatas", handler.RealTimeDataReceivehandler) diff --git a/middleware/trace.go b/middleware/trace.go new file mode 100644 index 0000000..12b957e --- /dev/null +++ b/middleware/trace.go @@ -0,0 +1,23 @@ +package middleware + +import ( + "modelRT/util" + + "github.com/gin-gonic/gin" +) + +// StartTrace define func of set trace info from request header +func StartTrace() gin.HandlerFunc { + return func(c *gin.Context) { + traceID := c.Request.Header.Get("traceid") + pSpanID := c.Request.Header.Get("spanid") + spanID := util.GenerateSpanID(c.Request.RemoteAddr) + if traceID == "" { // 如果traceId 为空,证明是链路的发端,把它设置成此次的spanId,发端的spanId是root spanId + traceID = spanID // trace 标识整个请求的链路, span则标识链路中的不同服务 + } + c.Set("traceid", traceID) + c.Set("spanid", spanID) + c.Set("pspanid", pSpanID) + c.Next() + } +} diff --git a/util/trace.go b/util/trace.go new file mode 100644 index 0000000..eefe769 --- /dev/null +++ b/util/trace.go @@ -0,0 +1,45 @@ +package util + +import ( + "context" + "encoding/binary" + "math/rand" + "net" + "strconv" + "strings" + "time" +) + +// GenerateSpanID define func of generate spanID +func GenerateSpanID(addr string) string { + strAddr := strings.Split(addr, ":") + ip := strAddr[0] + ipLong, _ := IP2Long(ip) + times := uint64(time.Now().UnixNano()) + rand.NewSource(time.Now().UnixNano()) + spanID := ((times ^ uint64(ipLong)) << 32) | uint64(rand.Int31()) + return strconv.FormatUint(spanID, 16) +} + +// IP2Long define func of convert ip to unit32 type +func IP2Long(ip string) (uint32, error) { + ipAddr, err := net.ResolveIPAddr("ip", ip) + if err != nil { + return 0, err + } + return binary.BigEndian.Uint32(ipAddr.IP.To4()), nil +} + +// GetTraceInfoFromCtx define func of get trace info from context +func GetTraceInfoFromCtx(ctx context.Context) (traceID, spanID, pSpanID string) { + if ctx.Value("traceid") != nil { + traceID = ctx.Value("traceid").(string) + } + if ctx.Value("spanid") != nil { + spanID = ctx.Value("spanid").(string) + } + if ctx.Value("pspanid") != nil { + pSpanID = ctx.Value("pspanid").(string) + } + return +}