Что происходит с panic в Rust после установки panic hook?
Panic hook получает уведомление о панике до начала раскрутки стека или немедленного завершения процесса. Он может записать диагностику, отправить событие в систему мониторинга или изменить формат сообщения, но сам по себе не предотвращает panic и не превращает её в Result.
Если паника затем перехватывается через catch_unwind, hook всё равно вызывается первым. При режиме panic = "abort" hook также может выполниться, хотя раскрутки стека и восстановления выполнения не произойдёт.
В Rust разделены два сценария: ожидаемые ошибки обычно возвращаются через Result, а нарушение предположений программы обозначается через panic. При этом приложению часто требуется единая диагностика паник без изменения самого механизма аварийного завершения.
Panic hook решает именно задачу наблюдения и форматирования: стандартный hook печатает сообщение и место возникновения, а пользовательский может заменить это поведение на журналирование или передачу данных в систему мониторинга.
Без собственного hook разные части приложения могут по-разному сообщать о паниках, а важные сведения могут оказаться только в стандартном выводе процесса. Неправильное понимание hook опасно: разработчик может принять запись события за восстановление выполнения или попытаться использовать hook для обычной обработки ошибок.
Есть и глобальное ограничение: hook устанавливается для всего процесса, а не для отдельной функции или конкретного вызова. Поэтому временная замена hook в библиотеке способна повлиять на приложение и другие потоки.
При возникновении паники Rust сначала вызывает установленный hook, передавая ему информацию о панике: полезную нагрузку и, когда доступно, место возникновения. После завершения hook среда продолжает обычную обработку паники: выполняет раскрутку стека, если она включена, либо завершает процесс в режиме abort.
В примере hook сначала печатает информацию, затем catch_unwind получает ошибку и позволяет продолжить выполнение. Название hook не означает перехват: перехватывает панику именно catch_unwind.
set_hook заменяет текущий глобальный hook. Если нужно сохранить стандартную диагностику, его можно получить через take_hook, обернуть собственным обработчиком и вызвать внутри него; после этого при необходимости hook следует восстановить. Нельзя вызывать set_hook или take_hook из потока, который уже находится в состоянии паники: такая попытка сама приводит к панике.
Hook не является заменой Result. Он подходит для аварийной диагностики, но не должен скрывать ожидаемые ошибки ввода, сети или бизнес-правил. Кроме того, обработчик должен быть коротким и надёжным: паника внутри hook во время обработки другой паники может привести к аварийному завершению процесса.
Сервис должен отправлять сведения о неожиданных паниках в систему мониторинга. Рассматривались три варианта: оставить стандартный hook, полностью заменить его собственным журналированием или установить обёртку, которая сохраняет стандартный вывод и дополнительно отправляет структурированные данные.
Стандартный вариант прост, но не даёт централизованного события для мониторинга. Полная замена даёт контроль над форматом, однако может убрать полезную диагностику разработчика. Обёртка лучше сохраняет исходную информацию, но требует аккуратно работать с глобальным состоянием и не выполнять ненадёжные операции внутри hook.
Выбирается обёртка над прежним hook: она фиксирует минимальные безопасные данные, вызывает исходный обработчик и не пытается продолжать выполнение после критической паники. Обычные ожидаемые сбои при этом остаются Result, поэтому мониторинг не смешивает штатные ошибки с нарушением инвариантов программы.
1. Вызывает ли catch_unwind panic hook?
Да. Hook вызывается при возникновении паники до того, как catch_unwind получит управление над результатом. Поэтому перехват паники не отменяет логирование hook; если диагностика должна зависеть от того, будет ли паника перехвачена, одного hook недостаточно.
2. Работает ли panic hook при panic = "abort"?
Да, hook вызывается до завершения процесса. Но после него нет раскрутки стека и нет возможности восстановить выполнение через catch_unwind. Поэтому hook в таком режиме предназначен только для последней диагностики, а не для восстановления.
3. Можно ли безопасно устанавливать отдельный hook внутри библиотеки на время одной операции?
Обычно это плохой дизайн: hook глобален для процесса, поэтому библиотека временно изменит поведение всего приложения. Дополнительные риски появляются при параллельной работе потоков и при панике до восстановления прежнего hook. Предпочтительнее предоставить приложению callback или явно передавать контекст, а глобальный hook настраивать один раз на уровне исполняемого приложения.