feat(api): implement global error handler middleware and trace integration

- Introduce APIError type and AbortWithError helper in response package
- Implement errorHandlerMiddleware to record Go errors to Otel Spans and format JSON response
- Register errorHandlerMiddleware globally in router
- Refactor logs analytics handler to use the new unified error pattern
This commit is contained in:
ryan
2026-06-16 10:24:14 +08:00
parent 9fa38dcfdc
commit f129a9cfca
5 changed files with 64 additions and 10 deletions
+29
View File
@@ -7,11 +7,13 @@ package router
import (
"context"
"errors"
"net/http"
"strconv"
"strings"
"time"
"github.com/Rain-kl/Wavelet/internal/common/response"
"github.com/Rain-kl/Wavelet/internal/config"
"github.com/Rain-kl/Wavelet/internal/model"
"github.com/Rain-kl/Wavelet/pkg/logger"
@@ -103,3 +105,30 @@ func corsMiddleware() gin.HandlerFunc {
c.Next()
}
}
// errorHandlerMiddleware 捕获 c.Errors 并统一格式化为 JSON 返回给客户端,同时将其记录到 Span 异常中
func errorHandlerMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
c.Next()
if len(c.Errors) > 0 {
err := c.Errors.Last().Err
span := trace.SpanFromContext(c.Request.Context())
// 1. 如果有活跃的 Span,将错误信息记录到 Trace 中,并把 Span 状态置为 Error
if span.IsRecording() {
span.RecordError(err)
span.SetStatus(codes.Error, err.Error())
}
// 2. 将错误转化为统一的 JSON 格式响应给客户端
var apiErr *response.APIError
if errors.As(err, &apiErr) {
c.JSON(apiErr.Code, response.Err(apiErr.Msg))
} else {
// 兜底策略:未知的系统级错误
c.JSON(http.StatusInternalServerError, response.Err("内部系统错误"))
}
}
}
}
+1 -1
View File
@@ -83,7 +83,7 @@ func Serve() {
r.Use(sessions.Sessions(config.Config.App.SessionCookieName, sessionStore))
// 补充中间件
r.Use(otelgin.Middleware(config.Config.App.AppName), loggerMiddleware(), risk_control.RiskControlMiddleware())
r.Use(otelgin.Middleware(config.Config.App.AppName), errorHandlerMiddleware(), loggerMiddleware(), risk_control.RiskControlMiddleware())
registerRoutes(r)