Обычно библиотеки логгирования предлагают из коробки сразу несколько "уровней" важности, с которыми Вы можете записывать сообщения. В документации к ним можно найти рекомендации - как лучше этими уровнями пользоваться, примерно такие:
Info
: все ожидаемые события, учет которых запланирован.Warning
: неожиданные/подозрительные события - иначе говоря аномалии, после которых еще возможно продолжение работы приложения.Error
: событие, после которого невозможно дальнейшее выполнение программы.Fatal
: событие, требующее по-настоящему немедленного вмешательства.
Проблема в том, что это все не совсем работает без некоторых дополнительных соглашений и уточнений. Именно о них я и хотел бы поговорить ниже.
"Продолжить работу"
Трактовать “возможно продолжить работу” можно очень по разному. Скажем, на экране пользователя можно настроить любимый цвет рамки вокруг экрана: пусть будет розовый. Если по каким-то причинам хранилище, где мы держим эти настройки цвета было недоступно и мы не можем отобразить этот цвет - можно ли это считать как “возможно продолжить” или это катастрофа? К сожалению, я еще ни разу не встречал хорошего универсального формального критерия, чтобы четко можно было разделять "катастрофа-терпимо". А раз не можешь остановить - значит нужно направить. Потому я предлагаю инвертировать споры об “это неправильный уровень”: если в коде написано, что отсутствие цвета - это Error
- значит программист Вам говорит, что этот цвет чертовски важен в этом домене - возможно, именно этот цвет сигнализирует, что сейчас надо продавать акции на миллионы долларов, и наоборот. Соответственно, чтение кода немного меняется: когда видите место, где из-за какой-то на Ваш взгляд ерунды прерывается выполнение, вопрос, который должен возникать автору, “Почему ты считаешь, что это так важно?” вместо “Истинно тебе говорю - ты программируешь неправильно!”.
Схожая инверсия может помочь в вечных спорах на тему “это исключительная ситуация или нет”. Опять же, все довольно просто. Важна не техническая составляющая: “нет соединения к базе - это исключение”, а “серверу прислали неправильный id
- так это ожидаемо”. Важно то, чья это будет головная боль и как ее можно избежать или хотя бы минимизировать урон. Чьи планы на вечер пятницы пойдут к черту из-за того, что это сломалось? Если Вашим сервисом пользуются приложения, которые вне Вашего контроля, то Вам действительно плевать на то, что они присылают некорректные id
и у них там что-то идет не так. Если Ваше приложение - это инструмент для управления базой данный - наподобие Sql Server Magement Studio - очевидно, что отсутствие доступа к базе - не Ваша печаль. А если Вашим сервисом пользуются приложения, за которые Вы же и в ответе - то это Ваши неприятности в конечном счете. Вопрос лишь в том, как и когда Вы об этом узнаете - быстро из сработавшей сигнализации или от звонка злого как черт владельца бизнеса, которому Вы пишете софт. А также вопрос в том, как “дешево, надежно и сердито” эту сигнализацию наладить.
"Error"
Давайте представим себе экран, где есть кнопка “Открыть” и текстовое поле. Эта кнопка по замыслу должна открыть Вам какой-то полезный функционал, а в текстовом поле предполагается ввести имя и фамилию. После нажатия на кнопку принципиально возможны два сценария: приложение либо крэшится либо нет. Если же мы были удачливы и оно не упало, у нас опять два варианта: мы получили желанный экран или мы получили сообщение об ошибке вместо него. Сообщение об ошибке в свою очередь можно принципиально разделить еще на два класса: может ли пользователь сам исправить ситуацию в рамках программы или нет - имеется ввиду корректирование своего ввода, а не “обратиться к Вашему системному администратору”.
На практике сообщение вида “Обратитесь к администратору” это просто подслащенные крэш. Да, несомненно это лучше, чем убить весь процесс со всеми пользовательскими данными, но от этого оно не перестает фактически блокировать работу. В тоже время ошибка может быть сообщением о неправильных с точки зрения программы действиях, а именно надо было ввести имя и фамилию “Джон Иванов”, а человек ввел просто “Джон” и валидатору это не нравится. К чему это все? Что вообще у нас тут речь идет о 3-х достаточно разных сущностях, но при том на каждую из них можно сослаться как Error, что вызывает изрядную путаницу. Крэш приложения - это несомненно уровень Error
в нашей системе определений, но это очень важный Error
.
Ошибка валидации имя-фамилии - это несомненно уровень Info
- мы ждем, что пользователь будет норовить записать все что угодно, а мы - пресекать это. Ну и записывать все те разы, когда пользователь был неправ. Но от этого ошибки не перестают бесить людей, которые их видят. То, что напрямую связано с людьми и их UX - важно; и неплохо бы присматривать за этим, не допускать, чтобы сообщения об этом тонули в километрах унылых Info
записей “Пользователь такой-то залогинился”. Иными словами, чтобы устранить путаницу, хочется иметь уровни Error+
и Info+
.
Я предвижу восклицания “Так погодите, ведь крэш приложения - это недопустимо! Надо сразу действовать! Это Fatal
уровень!" На это я неспеша прикурю воображаемую сигарету, затянусь и задумчиво отвечу: “Ну… всех ведь все равно не спасти...". Ладно, я не курю, но, думаю, образ понятен. Появление сообщения Fatal
должно быть эквивалентом запуска тревоги воздушной угрозы, когда в офисе разработки врубается сирена и это жуткое красное аварийное освещение. Вот честно, Вы именно так реагируете на то, что у кого-то из бухгалтерии на экране, который раз в сто лет запускают, упало приложение? Вполне может быть нормально, что у Вас сейчас даже и нет подобной ситуации, где уровень Fatal
- согласно такой системы определений - нужен. Так вот трюк в том, чтобы не блокировать возможность добавить обработку такой потенциальной ситуации в будущем, забивая сейчас Fatal
уровень сообщениями, которым важностьError+
в самый раз.
Таким образом, мы приходим к тому, что в действительности неплохо бы иметь уровни вида
Info
Info+
Warning
Error
Error+
Fatal
“Плюсовые” уровни можно легко организовать в Вашей любимой библиотеке логгирования расширив ее существующие методы Error/Info
, которые бы просто унифицировано добавляли какой-то хэштэг в обычные сообщения, скажем #IMPORTANT.
Что ж - это все, что я хотел сказать об ошибках и их логгировании. Буду рад, если этот текст добавит разработчикам больше взаимопонимания и уменьшит споры о том, как "правильно".
fkthat
Вообще-то в многопользователских приложениях (а лично я разрабатываю именно их) всё точно так и есть — падение приложения это как раз всеобщая тревога с красными сиренами.
jackyodd Автор
это отлично — это говорит о высоком качестве Вашего приложения, но также существуют продукты/команды, где крэш это «нужно будет глянуть, что там», особенно когда разовый и трудновоспроизводимый
fkthat
Да вовсе нет, просто в случае многопользовательского приложения (тот же веб) падение всего приложения в целом влияет сразу на много людей, так что тут надо сразу меры принимать. Например, в дотнетовской документации MS по LogLevel про уровень Critical так и сказано:
jackyodd Автор
А если крэша не будет, но всем этим людям будет показываться красивое сообщение об ошибке «Приложение не работает — обратитесь к администратору» и работу они делать свою не могут — это окей? Можно не спешить?
fkthat
Ну я ведь нигде и не говорил, что Fatal (Critical) это только полный крэш и трэш.
jackyodd Автор
Так. И из этого тогда вроде как получается, что в Вашем проекте даже сообщение об ошибке (не крэш) являются чрезвычайным событием, на которое следует мгновенная реакция команды разработки, которая все бросает и чинит эту проблему. И если Вы можете позволить себе это — т.е. успеваете полностью закрыть причину той ошибки раньше, чем появится новая аналогичная — то это круто, серьезно. Если это так — то в вашем бэклоге сейчас не должно сейчас быть ни единой жалобы от пользователя вроде «Была вот такая ошибка — хотел воспользоваться этой фичей так, как ни одному человеку в голову не придет — почините пжл». А если Вы начинаете выделять «ожидаемые ошибки» как уровень Error — то не кажется ли это странным, что запись об ожидаемом событии имеет более высокий приоритет, чем запись Warning — для неожиданных?
fkthat
Да совсем нет. К примеру, допустим, ошибка возникает при запросе к БД. Если это ошибка из-за того, что сервер БД упал — то это явный critical (нарушена работа всего приложения и надо срочно сервер БД перезапускать). Если это ошибка из-за того, что, например, разработчики в коде опечатались в имени таблицы, то это error (принципиальная ошибка, но, которая затрагивает только отделный сценарий или сценарии, а не все приложение в целом). А если ошибка вызвана тем, что пользователь ввел некоректные данные, а мы не предусмотрели их проверить и отправить человеческий ответ, то это, скорее, уже просто warning. В общем-то, между error и warning граница достаточно размытая, но с critical, по-моему, все достаточно очевидно.
jackyodd Автор
Вы точно читали сам текст? Потому что он как бы и есть о том, как уйти от проблемы размытости этих уровней засчет переноса точки зрения с сугубо технических деталей проблемы на реакцию людей на нее. Даже пример с БД есть. Если сервер БД, который упал, находится в ведении вообще другой компании-хостера — и Вы сами не можете с ним сделать ни че го — какова же будет Ваша реакция на такой critical — прям вот не покинете офис, пока бестолочи там не соизволят его наконец поднять?
fkthat
Как какая реакция — звонить тому, чей сервер БД, и делать ему мозг, пока не исправит. И, да, если приложение, которое мне деньги делает, полностью лежит для всех клиентов, то в бар бухать я точно не пойду, пока его обратно не поднимут.
jackyodd Автор
Но Вы же понимаете, что это не единственная возможная схема взаимоотношений? Приложение может делать деньги, но не Вам. Контракт на хостинг БД не с вами, а с компанией, которой Вы продаете услуги разработки. В этом и есть суть описанной идеи — в Вашем сценарии это действительно разумно делать критикал — вы теряете деньги. Но как-то глуповато сидеть и ждать, пока кто-то поднимет упавший сервер, от чего Вам ни холодно ни жарко в рамках существующих договоренностей.
p.s. И честно говоря, я не вижу разницы, из-за чего терять деньги — из-за упавшей БД или опечатке в коде, чтобы иметь для них разные уровни.