feat(log): Seq 会话追踪、控制台降噪与推送质量增强

请求日志自动带 sid/request_id,支持 LogBind;挂 Seq 后控制台仅 Warn+,Seq 仍全量;并修毫秒时间戳、@m、失败重试与停机 flush。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-07-23 05:26:44 +08:00
parent b254606003
commit 831a7f017a
6 changed files with 554 additions and 197 deletions
+61 -21
View File
@@ -2,7 +2,9 @@ package hotime
import (
"context"
"crypto/rand"
"database/sql"
"encoding/hex"
"fmt"
"io/ioutil"
stdlog "log"
@@ -25,8 +27,8 @@ import (
. "code.hoteas.com/golang/hotime/db"
"code.hoteas.com/golang/hotime/log"
mysql "github.com/go-sql-driver/mysql"
logrus "github.com/sirupsen/logrus"
"github.com/rs/zerolog"
logrus "github.com/sirupsen/logrus"
)
type Application struct {
@@ -97,10 +99,45 @@ func (that *Application) initiateGracefulShutdown(drain, shutdown time.Duration)
} else {
that.Log.Infof("服务已安全关闭")
}
// 冲刷 Seq 队列,避免停机前后关键日志丢失
that.Log.CloseSeq()
if that.WebConnectLog != nil {
that.WebConnectLog.CloseSeq()
}
os.Exit(0)
})
}
// clientIP 取客户端真实 IPX-Real-IP → X-Forwarded-For 第一段 → RemoteAddr
func clientIP(req *http.Request) string {
if req == nil {
return ""
}
if ip := strings.TrimSpace(req.Header.Get("X-Real-IP")); ip != "" {
return ip
}
if xff := req.Header.Get("X-Forwarded-For"); xff != "" {
if i := strings.Index(xff, ","); i >= 0 {
return strings.TrimSpace(xff[:i])
}
return strings.TrimSpace(xff)
}
host, _, err := net.SplitHostPort(req.RemoteAddr)
if err == nil && host != "" {
return host
}
return req.RemoteAddr
}
// shortID 生成 n 字节随机 hexn=6 → 12 位)
func shortID(n int) string {
b := make([]byte, n)
if _, err := rand.Read(b); err != nil {
return Md5(strconv.Itoa(Rand(10)))[:n*2]
}
return hex.EncodeToString(b)
}
// Run 启动实例
func (that *Application) Run(router Router) {
//如果没有设置配置自动生成配置
@@ -438,10 +475,22 @@ func (that *Application) handler(w http.ResponseWriter, req *http.Request) {
if err != nil {
unescapeUrl = req.RequestURI
}
// 请求级追踪:sid=sessionId 前 12 位(脱敏),request_id 回写响应头
requestId := shortID(6)
sid := sessionId
if len(sid) > 12 {
sid = sid[:12]
}
w.Header().Set("X-Request-Id", requestId)
reqLog := that.Log.WithFields("sid", sid, "request_id", requestId)
dbCopy := that.Db
dbCopy.Log = reqLog
//访问实例
context := Context{SessionIns: SessionIns{SessionId: sessionId, HoTimeCache: that.HoTimeCache},
Resp: w, Req: req, Application: that, RouterString: s, Config: that.Config, Db: &that.Db,
HandlerStr: unescapeUrl}
Resp: w, Req: req, Application: that, RouterString: s, Config: that.Config, Db: &dbCopy,
HandlerStr: unescapeUrl, Logger: reqLog}
//header默认设置
header := w.Header()
header.Set("Content-Type", "text/html; charset=utf-8")
@@ -455,26 +504,17 @@ func (that *Application) handler(w http.ResponseWriter, req *http.Request) {
defer func() {
//是否展示日志
if that.WebConnectLog != nil {
//负载均衡优化
ipStr := ""
if req.Header.Get("X-Forwarded-For") != "" {
ipStr = req.Header.Get("X-Forwarded-For")
} else if req.Header.Get("X-Real-IP") != "" {
ipStr = req.Header.Get("X-Real-IP")
}
//负载均衡优化
if ipStr == "" {
//RemoteAddr := that.Req.RemoteAddr
ipStr = Substr(context.Req.RemoteAddr, 0, strings.Index(context.Req.RemoteAddr, ":"))
}
that.WebConnectLog.Info().
Str("ip", ipStr).
evt := that.WebConnectLog.Info().
Str("ip", clientIP(req)).
Str("method", context.Req.Method).
Str("sid", sid).
Str("request_id", requestId).
Float64("cost_ms", ObjToFloat64(time.Now().UnixNano()-nowUnixTime.UnixNano())/1000000.00).
Float64("size_kb", ObjToFloat64(context.DataSize)/1000.00).
Msg(context.HandlerStr)
Float64("size_kb", ObjToFloat64(context.DataSize)/1000.00)
if country := req.Header.Get("EO-Client-IPCountry"); country != "" {
evt = evt.Str("ip_country", country)
}
evt.Msg(context.HandlerStr)
}
}()