diff --git a/CHANGELOG.md b/CHANGELOG.md index 20df616..18700ae 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # 更新日志 +25w12b +--- +- PRE-RELEASE: 此版本是v2.0.8/v2.1.0的预发布版本,请勿在生产环境中使用; +- ADD: 加入`timing`中间件记录响应时间 +- ADD: 加入`loggin`中间件包装日志输出 +- CHANGE: 更新安全政策, v1和24w版本序列生命周期正式结束 + 25w12a --- - PRE-RELEASE: 此版本是v2.0.8/v2.1.0的预发布版本,请勿在生产环境中使用; diff --git a/DEV-VERSION b/DEV-VERSION index e56b4bf..ec595d1 100644 --- a/DEV-VERSION +++ b/DEV-VERSION @@ -1 +1 @@ -25w12a \ No newline at end of file +25w12b \ No newline at end of file diff --git a/SECURITY.MD b/SECURITY.MD index 8fc021c..5ac050f 100644 --- a/SECURITY.MD +++ b/SECURITY.MD @@ -6,8 +6,10 @@ | 版本 | 是否支持 | | --- | --- | -| v1.x.x | :white_check_mark: | -| 24w*a/b/c... | :warning: 此为PRE-RELEASE版本,用于开发与测试,可能存在未知的问题 | +| v2.x.x | :white_check_mark: 当前最新版本序列, 受支持 | +| v1.x.x | :x: 这些版本已结束生命周期,不再受支持 | +| 25w*a/b/c... | :warning: 此为PRE-RELEASE版本,用于开发与测试,可能存在未知的问题 | +| 24w*a/b/c... | :warning: 此为PRE-RELEASE版本,用于开发与测试,可能存在未知的问题 生命周期已完全结束 | | v0.x.x | :x: 这些版本不再受支持 | ### 用户须知 diff --git a/loggin/loggin.go b/loggin/loggin.go new file mode 100644 index 0000000..ccc66be --- /dev/null +++ b/loggin/loggin.go @@ -0,0 +1,34 @@ +package loggin + +import ( + "ghproxy/timing" + "time" + + "github.com/WJQSERVER-STUDIO/go-utils/logger" + "github.com/gin-gonic/gin" +) + +var ( + logw = logger.Logw + LogDump = logger.LogDump + logDebug = logger.LogDebug + logInfo = logger.LogInfo + logWarning = logger.LogWarning + logError = logger.LogError +) + +// 日志中间件 +func Middleware() gin.HandlerFunc { + return func(c *gin.Context) { + // 处理请求 + c.Next() + + var timingResults time.Duration + + // 获取计时结果 + timingResults, _ = timing.Get(c) + + // 记录日志 IP METHOD URL USERAGENT PROTOCOL STATUS TIMING + logInfo("%s %s %s %s %d %s ", c.ClientIP(), c.Request.Method, c.Request.URL.Path, c.Request.UserAgent(), c.Writer.Status(), timingResults) + } +} diff --git a/main.go b/main.go index e5fd3c1..41094a3 100644 --- a/main.go +++ b/main.go @@ -12,8 +12,10 @@ import ( "ghproxy/api" "ghproxy/auth" "ghproxy/config" + "ghproxy/loggin" "ghproxy/proxy" "ghproxy/rate" + "ghproxy/timing" "github.com/WJQSERVER-STUDIO/go-utils/logger" @@ -124,7 +126,16 @@ func init() { gin.LoggerWithWriter(io.Discard) router = gin.New() + + // 添加recovery中间件 router.Use(gin.Recovery()) + + // 添加log中间件 + router.Use(loggin.Middleware()) + + // 添加计时中间件 + router.Use(timing.Middleware()) + //H2C默认值为true,而后遵循cfg.Server.EnableH2C的设置 if cfg.Server.EnableH2C == "on" { router.UseH2C = true diff --git a/proxy/chunkreq.go b/proxy/chunkreq.go index 181151a..893b524 100644 --- a/proxy/chunkreq.go +++ b/proxy/chunkreq.go @@ -54,7 +54,6 @@ func initChunkedHTTPClient() { func ChunkedProxyRequest(c *gin.Context, u string, cfg *config.Config, mode string, runMode string) { method := c.Request.Method - logInfo("%s %s %s %s %s Chunked-Proxy-Request", c.ClientIP(), method, u, c.Request.Header.Get("User-Agent"), c.Request.Proto) // 发送HEAD请求, 预获取Content-Length headReq, err := http.NewRequest("HEAD", u, nil) diff --git a/proxy/handler.go b/proxy/handler.go index b0ad7d1..a052125 100644 --- a/proxy/handler.go +++ b/proxy/handler.go @@ -14,6 +14,7 @@ import ( func NoRouteHandler(cfg *config.Config, limiter *rate.RateLimiter, iplimiter *rate.IPRateLimiter, runMode string) gin.HandlerFunc { return func(c *gin.Context) { + // 限制访问频率 if cfg.RateLimit.Enabled { diff --git a/timing/timing.go b/timing/timing.go new file mode 100644 index 0000000..430dddd --- /dev/null +++ b/timing/timing.go @@ -0,0 +1,78 @@ +package timing + +import ( + "sync" + "time" + + "github.com/gin-gonic/gin" +) + +// 阶段计时结构(固定数组优化) +type timingData struct { + phases [8]struct { // 预分配8个阶段存储 + name string + dur time.Duration + } + count int + start time.Time +} + +// 对象池(内存重用优化) +var pool = sync.Pool{ + New: func() interface{} { + return new(timingData) + }, +} + +// 中间件入口 +func Middleware() gin.HandlerFunc { + return func(c *gin.Context) { + // 从池中获取计时器 + td := pool.Get().(*timingData) + td.start = time.Now() + td.count = 0 + + // 存储到上下文 + c.Set("timing", td) + + // 请求完成后回收对象 + defer func() { + pool.Put(td) + }() + + c.Next() + } +} + +// 记录阶段耗时 +func Record(c *gin.Context, name string) { + if val, exists := c.Get("timing"); exists { + td := val.(*timingData) + if td.count < len(td.phases) { + td.phases[td.count].name = name + td.phases[td.count].dur = time.Since(td.start) // 直接记录当前时间 + td.count++ + } + } +} + +// 获取计时结果(日志输出用) +func Get(c *gin.Context) (total time.Duration, phases []struct { + Name string + Dur time.Duration +}) { + if val, exists := c.Get("timing"); exists { + td := val.(*timingData) + for i := 0; i < td.count; i++ { + phases = append(phases, struct { + Name string + Dur time.Duration + }{ + Name: td.phases[i].name, + Dur: td.phases[i].dur, + }) + } + total = time.Since(td.start) + } + return +}