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
This commit is contained in:
douxu 2025-06-05 15:56:40 +08:00
parent d2196701ec
commit 9aa5b0dcc6
7 changed files with 247 additions and 19 deletions

View File

@ -9,6 +9,6 @@ import (
type ModelParseConfig struct { type ModelParseConfig struct {
ComponentInfo orm.Component ComponentInfo orm.Component
Context context.Context Ctx context.Context
AnchorChan chan AnchorParamConfig AnchorChan chan AnchorParamConfig
} }

54
logger/facede.go Normal file
View File

@ -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
}

109
logger/logger.go Normal file
View File

@ -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(),
}
}

View File

@ -68,15 +68,15 @@ func initLogger(lCfg config.LoggerConfig) *zap.Logger {
return logger return logger
} }
// InitLoggerInstance return instance of zap logger // InitLoggerInstance define func of return instance of zap logger
func InitLoggerInstance(lCfg config.LoggerConfig) *zap.Logger { func InitLoggerInstance(lCfg config.LoggerConfig) {
once.Do(func() { once.Do(func() {
_globalLogger = initLogger(lCfg) _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 { func GetLoggerInstance() *zap.Logger {
_globalLoggerMu.RLock() _globalLoggerMu.RLock()
logger := _globalLogger logger := _globalLogger

25
main.go
View File

@ -24,7 +24,6 @@ import (
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/panjf2000/ants/v2" "github.com/panjf2000/ants/v2"
"go.uber.org/zap"
"gorm.io/gorm" "gorm.io/gorm"
) )
@ -43,7 +42,6 @@ var (
var ( var (
modelRTConfig config.ModelRTConfig modelRTConfig config.ModelRTConfig
postgresDBClient *gorm.DB postgresDBClient *gorm.DB
zapLogger *zap.Logger
alertManager *alert.EventManager alertManager *alert.EventManager
) )
@ -65,8 +63,7 @@ func main() {
}() }()
// init logger // init logger
zapLogger = logger.InitLoggerInstance(modelRTConfig.LoggerConfig) logger.InitLoggerInstance(modelRTConfig.LoggerConfig)
defer zapLogger.Sync()
// init alert manager // init alert manager
_ = alert.InitAlertEventManager() _ = alert.InitAlertEventManager()
@ -74,7 +71,7 @@ func main() {
// init model parse ants pool // init model parse ants pool
parsePool, err := ants.NewPoolWithFunc(modelRTConfig.ParseConcurrentQuantity, pool.ParseFunc) parsePool, err := ants.NewPoolWithFunc(modelRTConfig.ParseConcurrentQuantity, pool.ParseFunc)
if err != nil { 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) panic(err)
} }
defer parsePool.Release() defer parsePool.Release()
@ -88,7 +85,7 @@ func main() {
// init anchor param ants pool // init anchor param ants pool
anchorRealTimePool, err := pool.AnchorPoolInit(modelRTConfig.RTDReceiveConcurrentQuantity) anchorRealTimePool, err := pool.AnchorPoolInit(modelRTConfig.RTDReceiveConcurrentQuantity)
if err != nil { 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) panic(err)
} }
defer anchorRealTimePool.Release() defer anchorRealTimePool.Release()
@ -101,16 +98,16 @@ func main() {
postgresDBClient.Transaction(func(tx *gorm.DB) error { postgresDBClient.Transaction(func(tx *gorm.DB) error {
// load circuit diagram from postgres // load circuit diagram from postgres
componentTypeMap, err := database.QueryCircuitDiagramComponentFromDB(cancelCtx, tx, parsePool, zapLogger) componentTypeMap, err := database.QueryCircuitDiagramComponentFromDB(cancelCtx, tx, parsePool)
if err != nil { 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) panic(err)
} }
// TODO 暂时屏蔽完成 swagger 启动测试 // TODO 暂时屏蔽完成 swagger 启动测试
tree, err := database.QueryTopologicFromDB(ctx, tx, zapLogger, componentTypeMap) tree, err := database.QueryTopologicFromDB(ctx, tx, componentTypeMap)
if err != nil { 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) panic(err)
} }
diagram.GlobalTree = tree diagram.GlobalTree = tree
@ -125,10 +122,10 @@ func main() {
engine.Use(limiter.Middleware) engine.Use(limiter.Middleware)
// diagram api // diagram api
engine.GET("/model/diagram_load", handler.CircuitDiagramLoadHandler) engine.GET("/diagram/load", handler.CircuitDiagramLoadHandler)
engine.POST("/model/diagram_create", handler.CircuitDiagramCreateHandler) engine.POST("/diagram/create", handler.CircuitDiagramCreateHandler)
engine.POST("/model/diagram_update", handler.CircuitDiagramUpdateHandler) engine.POST("/diagram/update", handler.CircuitDiagramUpdateHandler)
engine.POST("/model/diagram_delete", handler.CircuitDiagramDeleteHandler) engine.POST("/diagram/delete", handler.CircuitDiagramDeleteHandler)
// real time data api // real time data api
engine.GET("/ws/rtdatas", handler.RealTimeDataReceivehandler) engine.GET("/ws/rtdatas", handler.RealTimeDataReceivehandler)

23
middleware/trace.go Normal file
View File

@ -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()
}
}

45
util/trace.go Normal file
View File

@ -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
}