Refactor service logging to callback pattern, add configurable StopTimeout and enhance panic context (by AI)

This commit is contained in:
AI Engineer 2026-05-10 22:40:31 +08:00
parent 8ed2986eb9
commit c207bdd400
6 changed files with 51 additions and 61 deletions

4
.gitignore vendored
View File

@ -3,3 +3,7 @@
.ai/
.geminiignore
.gemini
env.json
env.yml
env.yaml

View File

@ -1,5 +1,14 @@
# CHANGELOG - go/service
## v1.3.1 (2026-05-10)
- **Logging Refactor (Callback Pattern)**: 引入 `LogRequest` 闭环式回调封装,自动处理日志级别检查、对象池获取及元数据填充,消除 20+ 参数带来的维护压力。
- **Graceful Shutdown**: `ServiceConfig` 新增 `StopTimeout` 字段,支持通过配置灵活管控服务优雅退出的超时时间(默认 5s
- **Panic Recovery**: 增强 `handler.go` 中的 `recover` 逻辑,在发生 Panic 时自动记录 `requestId``path`,大幅提升故障定位效率。
- **Infrastructure Alignment**: 全量回退并对齐 Go 版本至 `1.25.0`;同步更新 `handler.go` 以适配新的日志调用模式。
## v1.0.5 (2026-05-10)
- **Wait**: This version was a temporary placeholder. See v1.3.1 for the actual release.
## v1.0.4 (2026-05-10)
- **Log Optimization**: Implemented `NoLogGets`, `NoLogHeaders`, `LogInputArrayNum`, `LogInputFieldSize`, and other fine-grained logging filters.
- **Static Log**: Added automatic logging for static file access.

View File

@ -59,6 +59,7 @@ type ServiceConfig struct {
MaxReadFrameSize uint32 // 单个帧的最大读取大小
MaxUploadBufferPerConnection int32 // 每个连接的最大上传缓冲区大小
MaxUploadBufferPerStream int32 // 每个流的最大上传缓冲区大小
StopTimeout int // 停止服务的超时时间 (ms)
}
var Config = ServiceConfig{}

View File

@ -44,7 +44,7 @@ func (rh *RouteHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
defer func() {
// 捕捉 Panic
if err := recover(); err != nil {
requestLogger.Error("panic recovered", "error", err, "stack", string(debug.Stack()))
requestLogger.Error("panic recovered", "requestId", requestId, "path", r.URL.Path, "error", err, "stack", string(debug.Stack()))
if !response.changed {
response.WriteHeader(http.StatusInternalServerError)
outputResult(response, "internal server error")
@ -102,18 +102,31 @@ func (rh *RouteHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
}
}
logRequest(
requestLogger,
r.Method, r.URL.Path, hostOnly(r.Host), scheme, r.Proto,
request.ClientIp(), serverId, Config.App, "", // node 暂无
r.Header.Get(discover.HeaderFromApp), r.Header.Get(discover.HeaderFromNode),
"", request.DeviceId(), request.SessionId(), requestId,
request.Header.Get(discover.HeaderClientAppName), request.Header.Get(discover.HeaderClientAppVersion),
authLevel, priority,
reqHeaders, args,
response.Code, usedTime,
respHeaders, cast.String(respData), uint(len(response.body)),
)
LogRequest(requestLogger, func(entry *RequestLog) {
entry.Method = r.Method
entry.Path = r.URL.Path
entry.Host = hostOnly(r.Host)
entry.Scheme = scheme
entry.Proto = r.Proto
entry.ClientIp = request.ClientIp()
entry.ServerId = serverId
entry.App = Config.App
entry.FromApp = r.Header.Get(discover.HeaderFromApp)
entry.FromNode = r.Header.Get(discover.HeaderFromNode)
entry.DeviceId = request.DeviceId()
entry.SessionId = request.SessionId()
entry.ClientAppName = r.Header.Get(discover.HeaderClientAppName)
entry.ClientAppVersion = r.Header.Get(discover.HeaderClientAppVersion)
entry.AuthLevel = authLevel
entry.Priority = priority
entry.RequestHeaders = reqHeaders
entry.RequestData = args
entry.ResponseCode = response.Code
entry.UsedTime = usedTime
entry.ResponseHeaders = respHeaders
entry.ResponseData = respData
entry.ResponseDataLength = uint(len(response.body))
})
}
}()

52
log.go
View File

@ -1,7 +1,6 @@
package service
import (
"apigo.cc/go/cast"
"apigo.cc/go/log"
)
@ -71,24 +70,8 @@ func (l *RequestLog) Reset() {
l.ResponseData = nil
}
// RequestLog 调用封装
func logRequest(
logger *log.Logger,
method, path, host, scheme, proto string,
clientIp, serverId, app, node string,
fromApp, fromNode string,
userId, deviceId, sessionId, requestId string,
clientAppName, clientAppVersion string,
authLevel, priority int,
reqHeaders map[string]string,
reqData map[string]any,
responseCode int,
usedTime float32,
respHeaders map[string]string,
responseData string,
responseDataLength uint,
extra ...any,
) {
// LogRequest 闭环式日志调用封装
func LogRequest(logger *log.Logger, fn func(entry *RequestLog)) {
if !logger.CheckLevel(log.INFO) {
return
}
@ -96,35 +79,10 @@ func logRequest(
entry := log.GetEntry[RequestLog]()
logger.FillBase(entry.GetBaseLog(), log.LogTypeRequest)
entry.Method = method
entry.Path = path
entry.Host = host
entry.Scheme = scheme
entry.Proto = proto
entry.ClientIp = clientIp
entry.ServerId = serverId
entry.App = app
entry.Node = node
entry.FromApp = fromApp
entry.FromNode = fromNode
entry.UserId = userId
entry.DeviceId = deviceId
entry.SessionId = sessionId
entry.ClientAppName = clientAppName
entry.ClientAppVersion = clientAppVersion
entry.AuthLevel = authLevel
entry.Priority = priority
entry.RequestHeaders = reqHeaders
entry.RequestData = reqData
entry.ResponseCode = responseCode
entry.UsedTime = usedTime
entry.ResponseHeaders = respHeaders
entry.ResponseData = responseData
entry.ResponseDataLength = responseDataLength
if len(extra) > 0 {
cast.FillMap(&entry.Extra, extra)
}
// 执行业务赋值逻辑
fn(entry)
// 统一发送
logger.Log(entry)
}

View File

@ -188,7 +188,12 @@ func (as *AsyncServer) Stop() {
as.discoverer.Stop()
}
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
stopTimeout := time.Duration(Config.StopTimeout) * time.Millisecond
if stopTimeout <= 0 {
stopTimeout = 5 * time.Second
}
ctx, cancel := context.WithTimeout(context.Background(), stopTimeout)
defer cancel()
if err := as.server.Shutdown(ctx); err != nil {