feat(log): Seq 会话追踪、控制台降噪与推送质量增强
请求日志自动带 sid/request_id,支持 LogBind;挂 Seq 后控制台仅 Warn+,Seq 仍全量;并修毫秒时间戳、@m、失败重试与停机 flush。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+61
-21
@@ -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 取客户端真实 IP:X-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 字节随机 hex(n=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)
|
||||
}
|
||||
}()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user