GinJuniorTechnical
Как Gin обрабатывает паники и что такое Recovery middleware?
Recovery — встроенный middleware Gin, который ловит паники через defer/recover, возвращает клиенту HTTP 500 и логирует стек вызовов, не останавливая сервер. Подключается через gin.Default() или явно r.Use(gin.Recovery()).
Recovery middleware в Gin
Recovery — встроенный middleware Gin, который перехватывает паники (panic) в обработчиках и middleware, предотвращая аварийное завершение всего HTTP-сервера. После перехвата паники Recovery возвращает клиенту ответ 500 Internal Server Error и записывает стек вызовов в лог.
Как подключить Recovery
package main
import (
"net/http"
"github.com/gin-gonic/gin"
)
func main() {
// gin.Default() включает Logger + Recovery автоматически
r := gin.Default()
// Или вручную через gin.New():
// r := gin.New()
// r.Use(gin.Logger())
// r.Use(gin.Recovery())
r.GET("/panic", func(c *gin.Context) {
panic("something went wrong!")
})
r.GET("/ok", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{"status": "ok"})
})
r.Run(":8080")
}
После запроса GET /panic сервер вернёт 500, но продолжит обрабатывать GET /ok — паника не убивает процесс.
Что делает Recovery внутри
Recovery оборачивает вызов c.Next() в defer с recover(). При панике он:
- Вызывает
recover()и получает значение паники. - Проверяет, не разорвано ли соединение клиентом (
brokenPipe). - Логирует стек вызовов через
debug.Stack(). - Возвращает
500, если ответ ещё не был записан.
Кастомный Recovery с логированием
import (
"log/slog"
"net/http"
"runtime/debug"
"github.com/gin-gonic/gin"
)
func CustomRecovery() gin.HandlerFunc {
return func(c *gin.Context) {
defer func() {
if err := recover(); err != nil {
stack := debug.Stack()
slog.Error("panic recovered",
"error", err,
"path", c.Request.URL.Path,
"method", c.Request.Method,
"stack", string(stack),
)
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{
"error": "internal server error",
})
}
}()
c.Next()
}
}
func main() {
r := gin.New()
r.Use(CustomRecovery())
r.Run(":8080")
}
RecoveryWithWriter — запись стека в произвольный io.Writer
import (
"os"
"github.com/gin-gonic/gin"
)
func main() {
r := gin.New()
// Пишем стек паники в stderr
r.Use(gin.RecoveryWithWriter(os.Stderr))
r.Run(":8080")
}
Подводные камни
- Recovery не перехватывает панику в goroutine. Если хендлер запускает
go func() { panic(...) }(), Recovery не поможет — goroutine упадёт и убьёт процесс. Добавляйте собственныйdefer/recoverвнутри каждой goroutine. - Паника после записи заголовков. Если
c.JSON()уже был вызван до паники, Recovery не сможет изменить статус ответа — клиент получит частичный ответ с кодом 200. - gin.Default() подключает Recovery автоматически. Добавление ещё одного
r.Use(gin.Recovery())удвоит Recovery в цепочке. Проверяйте, не используете ли вы оба варианта одновременно. - Стек трейс в продакшене. Стандартный Recovery выводит полный стек в stdout. В продакшене перехватывайте панику кастомным Recovery и отправляйте в Sentry/Datadog, а не в открытый лог.
- brokenPipe паники логируются на уровне warn, не error. Gin специально обрабатывает разрыв соединения как ненастоящую ошибку сервера — убедитесь, что ваш кастомный Recovery делает то же самое.
- Порядок middleware важен. Recovery должен быть первым в цепочке — только тогда он перехватит паники из всех последующих middleware и хендлеров.
Common mistakes
- Давать ответ про Recovery middleware в Gin только на уровне определения, не показывая поведение в реальном приложении.
- Игнорировать границы ответственности вокруг темы «Recovery middleware в Gin»: кто отменяет работу, кто владеет ресурсом и где формируется ответ клиенту.
- Не связывать Recovery middleware в Gin с observability, тестированием или безопасностью, когда это влияет на продакшен-поведение.
What the interviewer is testing
- Точно объясняет, что именно делает Recovery middleware в Gin и где это используется в Go-коде.
- Связывает Recovery middleware в Gin с корректным lifecycle запроса, отменой, конкурентностью или конфигурацией сервера там, где это уместно.
- Не изобретает API и опирается на реальные контракты официальной документации.