Почему современный фрамеворки отказываются от recoverable error?
Собственно вопрос в заголовке.
В нативном php вызов функции, например, trigger_error('Моя некритичная ошибка') вызовет сохранение этой информации в лог ну и обработку всякими хендлерами включая внешние типа newrelic. При этом если я руками не вызову эту функцию с E_USER_ERROR, выполнение программы продолжится дальше (опять же согласно документации php). Но если взять фрамеворк (Yii1/2, Laravel) то мы увидим, что создание ошибок вида E_USER_NOTICE/E_USER_WARNING уже вызовет завершение работы приложения. Т.е. мало того, что они выкинули как класс revoverable error, так еще и изменили поведение стандартной функции php.
Простой пример, зачем нужны recoverable error:
Есть сервис в вакууме рассылки смс к которому подключено два шлюза (основной и запасной). Ошибка подключения к основному шлюзу и отправка через запасной - это восстановимая ошибка, так как не возник отказ в обслуживании. Отказ обоих шлюзов - это уже критическая ошибка.
Так вот, в современных фрамеворках я уже не могу так просто бросить себе recoverable error и приходиться городить огород что бы просто мониторить факт отказа основного шлюза.
Вопрос один: зачем они так сделали?
Извините, немного подгорело.
Вы вопрос читали? Я не хочу ничего выводить, я хочу кинуть в лог (в сторонние системы мониторинга через стандартные средства). Я же сам этот ворнинг генерю, зачем мне его как ексеплен потом ловить, что бы потом еще раз с ним что-то сделать?
Олег Матрозов: Вы - динозавр, батюшка. В краце:
где-то далеко в просторах кода - throw *Аларм, пишите меня в лог*
где-то ближе к вам - catch(ex) -> log(ex) // ex == *Аларм, пишите меня в лог*
P.S. В вашем примере вообще нет ошибок, которые надо выкидывать, только пишем в лог если что-то не то и всё.
Ну, может я и динозавр, но многие системы внешнего мониторинга (тот же newrelic) навешиваются как стандартных hendler и это очень удобно, пока ты не встречаешься с модными фрамеворками. И не понимаю, кто решил, что trigger_error с E_USER_NOTICE теперь не по феншую, оно ведь до сих пор не deprecated в php включая 7 версию. Т.е. на лицо изменение базового функционала без видимых на то причин, только потому, что модно.
> error reporting level вы все еще контролируете.
Да, но это вызовет скрытие ошибки и от других хендлеров, а не только от хендлера фрамеворка.
> Ваша ситуация прекрасно разруливается исключениями.
Приведите пример пожалуйста.
> Научитесь пользоваться исключениями. В php7 в принципе даже ошибки парсинга теперь вызывают исключения.
Я не спорю, что в php7 всё ванильно и я бы ничего не писал, если бы yii1 работал бы только на php7 и вышел вот только что.
class FailoverSmsSender {
private $mainSmsGateway;
private $fallbackSmsGateway;
public function send(string $phoneNumber, string $message) {
try {
$this->mainSmsGateway->send($phoneNumber, $message);
} catch (SmsGatewayTimeoutException $exception) {
// перепосылаем только если проблема в самом гейтвее
// а не потому что мы посылаем неправильные данные.
$this->fallbackSmsGateway->send($phoneNumber, $message);
}
}
}
вот как-то так. Далее уже нюансы. Если мы хотим юзать неблокируемое API - юзаем ayres и корутины, или вместо try/catch юзаем промисы... куча вариантов.
Я не спорю, что в php7 всё ванильно и я бы ничего не писал, если бы yii1 работал бы только на php7 и вышел вот только что.
мы же про проблемы современных фреймворков, не? Разница лишь в том что не все ошибки сами конвертятся в исключения, но так можно делать прямо в тех же хэндлерах ошибок. Symfony например так делает на php5.3+
> вот как-то так. Далее уже нюансы. Если мы хотим юзать неблокируемое API
Эм... я не просил пример, как мне через try/catch разрулить отправку, я спрашивал, как мне в стандартную систему обработки ошибок кинуть E_NOTICE и так, что бы программа не прекратила работу.
Т.е. проблема в том, что вот имею я NewRelic или аналог который навешивается как error_hendler и всё радостно мне сливает в веб-интерфейс и мне удобно в коде, после пойманной ошибки о недоставки сообщения через основной гетвей просто кинуть trigger_error, что бы запись появилась в логе и в релике. Проблема вашего приведенного кода в том, что он не логирует. Да, вы можете сами руками вызвать тогда error_log, но это запишет только в лог пхп, но никак не в тот же релик. И получается, что вам придется городить огород для вызова еще и методов релика, что бы залогировать ту же ошибку еще и там.
> Symfony например так делает на php5.3+
Я с Symfony не знаком увы. Хорошо, если он не конвертит всё по дефолту. В Yii увы всё заворачивается в Exception без вариантов (и в Laravel похоже тоже).
Хм... вот это "правильно" я и не понимаю. Не спорю, что последние движения в php7 в сторону выбрасывания исключений вместо части ошибок - это гут, но это совсем другой плоскости вопрос. Я не понимаю, почему люди с легкостью отказываются от целого пласта удобного инструментария в угоду.... моде? И ведь не сами разработчики php E_NOTICCE и т.д. выпиливают. Просто такое ощущение, что кто-то не смог писать на старой парадигме и сказал: "Толкиен не прав, я знаю как было на самом деле!".
Так я прямо в вопросе и привел живой пример с необходимостью отлова некритичного события. И для его отлова уже давно была создана вполне удобная инфраструктура, включая кучу сторонних сервисов, которую в итоге и поломали. На голом php или фрамеворке, где E_NOTICE не роняет код добавление того же newrelic в модули php и одна строчка кода позволяют вам мониторить состояние бека в красивых графиках. А отсутствие необходимости хардкодить под систему мониторинга - позволяют легко использовать одну или другую не завязывая код на них. Сейчас же, что бы из yii бросить ворнинг (не убивая при этом исполнение) мне приходится кроме стандартного error_log вызывать еще и специфичные для мониторинга функции, что бы видеть эту ситуацию в графиках. На лицо ухудшение удобства при непонятных фишках в замен.
Нет, вы не поймите меня не правильно. Я так же вылизываю код, что бы там ни нотисов, не депрекатедов не вылазило. Но меня возмущает именно то, с какой легкостью убили удобное средство доставки информации. При этом я не вижу логики, почему бы те же E_NOTICE/E_WARNING не пропускать в стандартную очередь обработки, а уж E_ERROR и т.д. превращать в ексепшены.
Сергей Протько:
> мой код падал потому что это возможный баг
Я бы был с вами полностью согласен, если бы хендлер того же yii реагировал еще на что-то, кроме стандартного error_reporting. Что бы можно было нотисы пропускать. Тем более нотис в продакшене не так страшен, как отказ обслуживания из-за него. Я лучше увижу этот нотис в системе мониторинга и спокойно его пофикшу, чем будут в панике разбираться, почему сервис из-за нотиса перестал работать совсем.
И я дал вам ответ - такие вещи разруливаются исключениями. та "не критичная" для вас ситуация "коннектором" к шлюзу должна восприниматься как исключительная. То есть "исключительность" ситуации должна оцениваться в контексте компонента бросающего исключение, а не в контексте всей системы.
Опять же если у вас падает соединение со шлюзом в <50% случаев иэто НОРМАЛЬНО для коннектора - тогда он может тупо вернуть false. А вот E_USER_ERROR это те же исключения только не явно и неудобно.
Сергей Протько: Погодите, такое ощущение, что вы упорно смотрите немного не стой стороны. Я ничего и не говорю про E_ERROR/E_USER_ERROR и им подобные. Я говорю именно про более легкие случаи. Я вам привел пример, в котором мне необходимо выкидывать наружу оповещение. Да, я вполне вероятно именно при помощи try/cache буду ловить ошибку из конкретного коннекта к гатевею. И вот, его поймав, я хочу положить в лог и системы мониторинга информацию о том, что коннекшен не удался и, после этого, подключаться к запасному варианту. Внешние системы мониторинга уже имеют кучу удобностей, например, как нотификейшены по превышению рейта ошибок (т.е. одна ошибка коннекта к основному гатевею - это норм, а вот 20 за последний час - уже проблема, может быть нужно искать другой гатевей). Я вас прошу привести пример именно замены этой нотификации и доставки такого рода информации в сравнении с уже реализованными решениями.
Олег Матрозов: думаю что бы вы поняли мой пример вы должны понять и мысли которые мною движат при проектировании системы: я за минимизацию изменений в коде. То есть если мне нужно добавить логирование - я просто леплю декоратор.
interface SmsGateway {
public function send(string $phoneNumber, string $message);
}
class FirstSmsGateway implements SmsGateway { /** implementation */}
class SecondSmsGateway implements SmsGateway {/** implementation */}
class SmsGatewayLogger implements SmsGateway {
private $logger; // PSR-3 logger
private $next; // SmsGateway
// ...constructor...
public function send(string $phoneNumber, string $message) {
try {
$result = $this->next->send($phoneNumber, $message);
} catch (Exception $e) {
$this->addExceptionInfoToLog($e);
throw $e;
}
// мало ли.... короч тут можно логировать что угодно
// вся функциональность логирования размещается тут
$this->addSuccessToLog($result);
return $result;
}
// ...
}
class FailoverSmsSender implements SmsGateway
{
// цепочка гейтвеев, зачем двумя ограничиваться...
// они должны быть отсортированы по каким-то критериям
// например первым пойдет какой-нибудь дешевый и не слишком надежный
// а последним - самый дорогой но 100% надежный.
private $gatewayChain; // SmsGateway[]
// ...constructor...
public function send(string $phoneNumber, string $message) {
foreach ($this->gatewayChain as $gateway) {
if ($result = $this->tryGateway($gateway)) {
return $result;
}
}
return;
}
private function tryGateway(SmsGateway $gateway) {
try {
$result = $gateway->send($phoneNumber, $message);
} catch (SmsGatewayTimeoutException $exception) {
// нас опять же интересуют только ошибки сервиса, а не наши
// разруливается через наследование исключений
return;
}
return $result;
}
}
Вот, громоздко, но зато с большего универсально, легко тестировать и легко масштабировать систему, особенно если мы используем какой-нибудь dependency injection контейнер с красивой и удобной поддержкой декорации сервисов, типа php-di или symfony/di.
В случае Yii1 мы скорее всего будем плакать потому что такие подходы там не практикует практически никто...