Ответ
Ошибка 500 (Internal Server Error) — это общий код ответа, означающий, что на сервере произошла непредвиденная ошибка. Мой план действий для её диагностики будет следующим:
Шаг 1: Сбор информации (не трогая прод)
- Проверить логи приложения: Это первое и самое важное место. Искать записи об ошибках (
ERROR,FATAL), паниках (panic) и их стектрейсы, которые произошли в момент получения 500-й ошибки. - Проверить логи веб-сервера (Nginx, Apache): Если приложение работает за прокси-сервером, ошибка может быть на его уровне (например,
upstream sent invalid header). - Системы мониторинга и алертинга (Prometheus, Grafana, Sentry): Проанализировать дашборды. Возможно, есть всплеск нагрузки на CPU/RAM, нехватка дискового пространства, или ошибка уже была автоматически зафиксирована и сгруппирована в Sentry.
Шаг 2: Воспроизведение и локализация
- Попытаться воспроизвести ошибку: Если возможно, воспроизвести ошибку в тестовом или staging-окружении с теми же входными данными. Это самый безопасный способ для отладки.
- Проанализировать запрос: Какой именно эндпоинт, с какими параметрами, заголовками и телом запроса вызывает ошибку?
- Проверить внешние зависимости: Убедиться, что базы данных, кэши (Redis), брокеры сообщений (RabbitMQ, Kafka) и другие внешние API, к которым обращается сервис, доступны и работают корректно.
Шаг 3: Анализ кода и исправление
- Анализ стектрейса: Если есть
panic, стектрейс точно укажет на место в коде, где произошла проблема. В Go для перехвата паник в HTTP-обработчиках используетсяrecover.func recoveryMiddleware(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { defer func() { if err := recover(); err != nil { log.Printf("recovered from panic: %vn%s", err, debug.Stack()) http.Error(w, "Internal Server Error", http.StatusInternalServerError) } }() next.ServeHTTP(w, r) }) } - Анализ недавних изменений: Проверить последние коммиты и деплои. Очень часто причина кроется в недавно добавленном коде.
- Логирование запросов/ответов: Если причина неясна, можно временно добавить middleware для детального логирования входящих запросов и ответов, чтобы поймать проблемный случай.
func loggingMiddleware(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { log.Printf("Request: %s %s", r.Method, r.URL.Path) next.ServeHTTP(w, r) }) }
Шаг 4: Внедрение исправления
- Написать исправление и тесты: Устранить причину ошибки и написать регрессионный тест, который бы падал без этого исправления, чтобы предотвратить повторение ошибки в будущем.
- Выкатить исправление: Развернуть исправленную версию приложения.
Ответ 18+ 🔞
А, ну это классика, ёпта! Ошибка 500 — это когда сервер такой: «Ой, блядь, всё, пиздец, я не могу». И он тебе вместо ответа как бы говорит: «Сам разбирайся, я в ахуе». Но мы-то не такие, мы сейчас эту манду с ушами разберём по косточкам.
Шаг 1: Сначала посмотрим, что наговорил этот обалдуй-сервер
- Логи приложения — наш главный свидетель. Там надо искать крики «ERROR», «FATAL» и особенно «panic». Это как найти на месте преступления записку «Я, Герасим, утопил Муму». Стектрейс — это вообще золото, он покажет, в каком именно месте кода всё накрылось медным тазом.
- Логи веб-сервера (Nginx/Apache). А вдруг это не наше приложение обосралось, а какой-то прокси-сервер рядом? Там бывает такое: «upstream sent invalid header while reading response header from upstream». Ну, типа, наш сервис прислал какую-то дичь в заголовках, и Nginx просто офигел.
- Системы мониторинга (Prometheus, Grafana, Sentry). Это как камеры наблюдения. Глянем — может, у нас CPU зашкаливает, память кончилась или диск переполнился? А Sentry — она вообще умная, она уже могла сгруппировать ошибки и сказать: «Смотри, чувак, вот тут у тебя уже 500 раз на этом месте падало, ты чё, слепой?».
Шаг 2: Пытаемся понять, как это воспроизвести, не сломав прод
- Воспроизведение — это святое. Надо попробовать на тестовом стенде натравить на сервис те же самые данные, что и в проде. Если повторится — овердохуища, можно спокойно ковыряться.
- Какой запрос был-то? Какой эндпоинт, какие параметры, что в теле? Может, клиент прислал JSON, в котором вместо числа — строка «пизда», а наш код этого не ожидал.
- А внешние сервисы живы? База данных не легла? Redis не ответил? Внешний API не начал возвращать хуйню вместо ответа? Сервер иногда падает, потому что кто-то другой его подвёл — классическая история предательства.
Шаг 3: Лезем в код с фонарём и отвёрткой
- Стектрейс — наш лучший друг. Если была паника, то она, как труп, укажет прямо на место преступления. В Go, чтобы такие паники не убивали весь сервер, умные люди придумали
recover. Вот смотри, как это выглядит, когда мы ловим панику, как какого-то мартышлюшку:func recoveryMiddleware(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { defer func() { if err := recover(); err != nil { log.Printf("recovered from panic: %vn%s", err, debug.Stack()) http.Error(w, "Internal Server Error", http.StatusInternalServerError) } }() next.ServeHTTP(w, r) }) }Видишь? Мы ловим панику, пишем в лог «ой, блядь, тут такое было», и отдаём пользователю цивилизованную 500-ку, а не просто обрываем соединение.
- Что меняли недавно? Абсолютно всегда, в 99% случаев, ошибка появляется после последнего деплоя. Кто-то что-то «улучшил». Надо смотреть диффы последних коммитов — там обычно и сидит виновник, этот пидарас шерстяной.
- Добавим логирования, если совсем темно. Можно на время воткнуть промежуточный слой, который будет записывать все входящие запросы. Иногда только так и поймаешь тот один хитрый запрос, который всё ломает.
func loggingMiddleware(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { log.Printf("Request: %s %s", r.Method, r.URL.Path) next.ServeHTTP(w, r) }) }
Шаг 4: Чиним и выкатываем, чтобы больше не позориться
- Чиним баг и пишем тест. Нашли причину — исправили. А главное — пишем тест, который падает без нашего фикса. Чтобы в будунии какой-нибудь полупидор не «отрефакторил» код и снова не вернул эту ошибку. Это называется — поставить хуй в пробирку.
- Выкатываем исправление. Аккуратно, по всем правилам, на прод. И смотрим, чтобы наши графики ошибок в мониторинге резко пошли вниз, а не нахуй вверх.
Вот и весь план, ёпта. Главное — не паниковать, как тот сервер. Системно собирать улики, воспроизводить и бить точно в причину. А то бывает, начинают тыкать палкой в рандомные места — только хуже делают.