diff --git a/go.mod b/go.mod index 064306e4..67c507e1 100644 --- a/go.mod +++ b/go.mod @@ -15,6 +15,7 @@ require ( github.com/go-playground/validator/v10 v10.22.1 github.com/go-resty/resty/v2 v2.15.3 github.com/go-sql-driver/mysql v1.8.1 + github.com/google/uuid v1.6.0 github.com/ilyakaznacheev/cleanenv v1.5.0 github.com/jarcoal/httpmock v1.3.1 github.com/jmoiron/sqlx v1.4.0 @@ -68,7 +69,6 @@ require ( github.com/gabriel-vasile/mimetype v1.4.5 // indirect github.com/golang-jwt/jwt v3.2.2+incompatible // indirect github.com/golang-jwt/jwt/v4 v4.5.0 // indirect - github.com/google/uuid v1.6.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/jinzhu/inflection v1.0.0 // indirect github.com/jinzhu/now v1.1.5 // indirect diff --git a/internal/auth/mysql_repository.go b/internal/auth/mysql_repository.go index 14e17b09..806743f7 100644 --- a/internal/auth/mysql_repository.go +++ b/internal/auth/mysql_repository.go @@ -118,8 +118,6 @@ func (m mysqlRepo) GetPermission(ctx context.Context, groupID uint8) (Permission m.log.Error("can't find permission for group", zap.Uint8("user_group_id", groupID)) return Permission{}, nil } - - m.log.Error("unexpected error", zap.Error(err)) return Permission{}, errgo.Wrap(err, "dal") } diff --git a/main.go b/main.go index 7c046eaa..b89fe533 100644 --- a/main.go +++ b/main.go @@ -17,11 +17,14 @@ package main import ( "fmt" + "github.com/google/uuid" + "github.com/bangumi/server/cmd" "github.com/bangumi/server/internal/pkg/logger" ) func main() { + uuid.EnableRandPool() if err := cmd.Root.Execute(); err != nil { logger.Fatal("failed to start app:\n" + fmt.Sprintf("\n%+v", err)) } diff --git a/web/error.go b/web/error.go index caaffcee..bb7666b3 100644 --- a/web/error.go +++ b/web/error.go @@ -37,6 +37,7 @@ func globalNotFoundHandler(c echo.Context) error { }) } +//nolint:funlen func getDefaultErrorHandler() echo.HTTPErrorHandler { var log = logger.Named("http.err"). WithOptions(zap.AddStacktrace(zapcore.PanicLevel), zap.WithCaller(false)) @@ -49,6 +50,7 @@ func getDefaultErrorHandler() echo.HTTPErrorHandler { _ = c.JSON(e.Code, res.Error{ Title: http.StatusText(e.Code), Description: e.Msg, + RequestID: c.Request().Header.Get(cf.HeaderRequestID), Details: util.Detail(c), }) return @@ -69,6 +71,7 @@ func getDefaultErrorHandler() echo.HTTPErrorHandler { _ = c.JSON(http.StatusInternalServerError, res.Error{ Title: http.StatusText(e.Code), Description: e.Error(), + RequestID: c.Request().Header.Get(cf.HeaderRequestID), Details: util.DetailWithErr(c, err), }) return @@ -76,6 +79,19 @@ func getDefaultErrorHandler() echo.HTTPErrorHandler { } if errors.Is(err, context.Canceled) { + log.Error("unexpected echo error", + zap.Int("code", http.StatusInternalServerError), + zap.Any("message", "request timeout"), + zap.String("path", c.Request().URL.Path), + zap.String("query", c.Request().URL.RawQuery), + zap.String("cf-ray", c.Request().Header.Get(cf.HeaderRequestID)), + ) + + _ = c.JSON(http.StatusInternalServerError, res.Error{ + Title: "request timeout", + Description: "request timeout", + RequestID: c.Request().Header.Get(cf.HeaderRequestID), + }) return } diff --git a/web/new.go b/web/new.go index b618d073..97b90252 100644 --- a/web/new.go +++ b/web/new.go @@ -24,6 +24,7 @@ import ( "strings" "time" + "github.com/google/uuid" "github.com/labstack/echo/v4" "github.com/labstack/echo/v4/middleware" "github.com/prometheus/client_golang/prometheus/promhttp" @@ -99,12 +100,20 @@ func New() *echo.Echo { app.Use(func(next echo.HandlerFunc) echo.HandlerFunc { return func(c echo.Context) error { - c.SetRequest(c.Request(). - WithContext(context.WithValue(c.Request().Context(), logger.RequestKey, &logger.RequestTrace{ - IP: c.RealIP(), - ReqID: c.Request().Header.Get(cf.HeaderRequestID), - Path: c.Request().RequestURI, - }))) + ctx, cancel := context.WithTimeout(context.WithoutCancel(c.Request().Context()), time.Minute) + defer cancel() + + reqID := c.Request().Header.Get(cf.HeaderRequestID) + + if reqID == "" { + reqID = uuid.Must(uuid.NewV7()).String() + } + + c.SetRequest(c.Request().WithContext(context.WithValue(ctx, logger.RequestKey, &logger.RequestTrace{ + IP: c.RealIP(), + ReqID: reqID, + Path: c.Request().RequestURI, + }))) return next(c) } diff --git a/web/res/error.go b/web/res/error.go index 1b34a81a..94c1e526 100644 --- a/web/res/error.go +++ b/web/res/error.go @@ -22,6 +22,7 @@ import ( "github.com/labstack/echo/v4" + "github.com/bangumi/server/web/req/cf" "github.com/bangumi/server/web/util" ) @@ -31,6 +32,7 @@ var ErrNotFound = NewError(http.StatusNotFound, "resource can't be found in the type Error struct { Title string `json:"title"` Details any `json:"details,omitempty"` + RequestID string `json:"request_id,omitempty"` Description string `json:"description"` } @@ -77,6 +79,7 @@ func InternalError(c echo.Context, err error, message string) error { return c.JSON(http.StatusInternalServerError, Error{ Title: "Internal Server Error", Description: message, + RequestID: c.Request().Header.Get(cf.HeaderRequestID), Details: util.DetailWithErr(c, err), }) }