Найти в Дзене
Одиночная палата

Begin. Журналирование и прототипирование

Оглавление

Как бывает

Каждый программист рано или поздно сталкивается с необходимостью снабдить тот или иной кусок собственного или чужого кода журналированием. Это может быть отдельная функция в библиотеке, модуль программного комплекса, интерфейс программный или графический, даже одностраничный сайт или утилита. Программисты довольно давно и относительно успешно договорились о том как следует оформлять сам код, существуют стандартные подходы и методики документирования, форматы обмена данными да и много всего прочего с чем если и возникают проблемы, то только из-за личных качеств программиста. Кто-то может считать, что его код настолько идеален, что его не надо оформлять и комментировать. Кому-то просто лень приводить формат обмена данными между своими какими-то приложениями к общепринятым стандартам. В общем, все есть уже готовое, и уже личное дело каждого следовать тому или иному руководству или нет.

А вот если зачем-то стал нужен журнал обычно сразу получается какая-то анархия и разброд. Какие-то общие методики и рекомендации, разумеется, есть - нельзя сказать, что бы этим не занимались, но они касаются каких-то весьма общих форм. С другой стороны есть конкретные системы конкретных популярных программ. Ну, например, есть Syslog для систем на базе ОС Linux. Он представляет из себя технический журнал операционной системы. Не сведущему в деталях работы системы лазить туда в общем бессмысленно. Но это один из вариантов стандарта которого можно придерживаться. Есть еще система журналирования веб-сервера Apache, сделанный с оглядкой все на тот же Syslog, но немного более навороченный и тоже больше технический - не особо предназначенный для чтения напрямую. Есть встроенный журнал событий для программ под Windows.

Казалось бы, бери и пользуйся, но что бы настроить свою программу писать журналы в конкретных форматах под конкретные системы нужно как минимум знать как и что туда писать. Почему-то использование системных журналов для программиста кажется чем-то то ли слишком серьезным, сложным. А до того что бы сделать что-то свое понятное и удобное кому-то еще обычно не доходит. Журналы почти у каждой программы получаются свои, со своей логикой, понятной только изобретателю. Пишутся они то ли куда-то в текстовый файл, то ли во всплывающее на экране окошко, или вообще куда-то в свою базу данных, откуда достать журнал может только сам разработчик. Но и это еще ерунда по сравнению с тем, что в этих журналах содержится. С содержанием сложнее в двойне. Если методики и общие принципы ведения журналов в общем понятны, то на счет содержания, к сожалению, все сводится к принципу "что вижу - о том и пою". И это хорошо если человек петь умеет. А если слуха нет?

В итоге получается то что получается: начиная от 'Ok' в один бесконечный столбец, заканчивая мешаниной дампов данных, стэктрейсов и локализованных сложносочиненных предложений или даже коротких эссе объемом больше, чем все данные обрабатываемые самой программой. При этом если в документации к программе сказано хотя бы пару слов о значении этой абракадабры то это скорее приятное исключение, чем правило. Обычно отношение к журналам у программистов эгоистическое и слегка человеконенавистническое - понимай как хочешь.

Почему так происходит

Поскольку моя основная профессиональная деятельность по большей части как раз сводится к разбору этих самых журналов и внутренностей программ, которые эти журналы производят, то вариантов я насмотрелся довольно большое количество разных. И поэтому примерно могу представить, со своей колокольни разумеется, о чем мог рассуждать мой предшественник и как вообще он мог прийти к тому к чему пришел в итоге. Мне, повторюсь, с моей точки зрения, это представляется следующим образом.

Во-первых, все-таки в основном не из-за лени и не из вредности. Часто фактором принятия тех или иных решений в программировании является не собственная эстетическая зрелость, не квалификация и компетенции, и даже не техническое задание, а банальные сроки. Я искренне верю в человечество, а особенно в программистскую её часть - ну не сделал бы так нормальный человек будь у него чуть больше времени на посидеть подумать.

Во-вторых, в программировании распространено так называемое прототипирование. Слово мудреное, но за ним стоит лишь черновик или набросок. Термин пришел в программирование, видимо из чего-то дизайнерского или, как это часто бывает, из строительного мира. Что бы показать себе или заказчику, что в принципе можно сделать - идею наглядно, программисты прибегают именно к этому приему - созданию эдакого наброска без основного функционала, но принимающее что-то на входе и выдающее нечто второе на выходе. Внутри обычно стоит заглушка с комментарием о том, что на самом деле там будет в окончательном варианте. Так вот, в отличии от строительства или дизайна черновик не выбрасывается в корзину, а постепенно становится, той самой программой. Вопрос о журналировании встает если не самом конце процесса, то где-то очень в середине, когда сам разработчик уже нуждается в какой-то помощи от своей программы, и когда сроки уже горят. А там уже, как водится, и так сойдет.

В-третьих, как и было сказано, нет готовых рецептов. Не сказано где-то, что вот такая-то программа должна оставлять в журнале работы такие-то определенные сообщения в таком-то объеме. Программист предоставлен сам себе и даже подглядывание чего-то чужого тут не подойдет, поскольку внутренний смысл каждый раз если не уникальный, то не такой что бы валялся на каждом форуме.

Как должно быть

Собственно, поэтому предлагаю свой более или менее выработанный вариант. Конкретного и универсального решения у меня, конечно, тоже нет, но это тот минимум, который бы, как мне кажется, не помешал бы начинающему программисту что бы понимать в чем дело. Да и не очень начинающему, может быть, тоже.

Уровни

Про уровни сказано довольно много и подробно. Стандартно это 5-10 уровней от критической ошибки то полного дампа памяти. В прототипе программы, на самом старте, никогда не кажется, что когда-то настанет тот момент когда понадобится более двух-трех уровней. Навскидку это что-то вроде: 1 - 'Ошибка' (Error) с выходом из программы, 2 - 'Предупреждение' (Warning/Alert) без закрытия, 3 - 'Информация' (Info) - что-то на что следует обратить внимание. Если дело зашло чуть дальше добавляется более подробная информация о переменных или настройках процесса: 4 - Отладка (Debug). И так далее. Распространенные библиотеки и фреймворки предлагают еще некоторые промежуточные состояния и градации: некритическая ошибка, критическая ошибка, совсем критическая ну и в обратную сторону. Что делать со всем этим хозяйством изначально не очень понятно. Где в программе из дюжины строчек может понадобиться стэктрейс или дамп не очевидно. Более того сперва это просто либо выскакивающее окошко или печать в консоль (вывод на веб-страницу), а вовсе не журнал.

По своему опыту могу сказать, что вам обязательно понадобится как минимум четыре уровня в любом случае. Пятый - трейс, если в планах что-то сложнее счетчика калорий то тоже мастхев опция. Поэтому лучше сразу их предусмотреть прямо в прототипе. Ну, то есть одним из параметров запуска программы должен быть уровень журналирования принимающий как минимум пять значений. Вы можете потом написать идеальную программу, которая вообще никогда не ломается и не воспользоваться этим параметром, но лучше что бы было но не использовалось, чем потом окажется, что четырех было мало. Да и не встречал я пока идеальных программ.

Зачем нужны уровни и почему нужно запускать программу с их разными значениями думаю объяснять особо никому не надо? Отмечу лишь то, что для прикладной программы по умолчанию должен стоять уровень для пользователя, а не для администратора или тем более разработчика. Это почему-то не всем и не всегда очевидно. Если вы делаете свой веб-сервер - пожалуйста, пишите в журнал протокольные заголовки, а в более простых случаях журнал будет читать именно конечный пользователь.

Детализация

Если с уровнями все интуитивно более или менее понятно, тем более если вы запускали чужие программы с нестандартными параметрами, то вот со смыслом этих самых уровней иногда просто беда. То в информации мы видим пути обрабатываемых документов или значения переменных, то в трейсе коментарии на русском или на китайском. Неким образом вводят в заблуждение названия самих общепринятых терминов: 'error' или 'exception', 'debug_trace' или 'stack_trace'.

Еще хуже то, что перемешаны между собой значимость и подробность. Например, ошибка может быть критической, после которой работа программы невозможна в принципе, или можно поругаться и продолжить со следующим файлом - это значимость. Но в самом сообщении об ошибке может быть как просто код, так и полный стэк вызовов - это подробность. По-английски иногда так и делят 'log level' - сообщения какой значимости следует писать в журнал и вторым, совершенно другим параметром, является 'verbose level' - многословность, детализация. То есть сообщения разных уровней качества еще и могут быть с разным уровнем детализации. Где-то можно встретить отдельно еще и 'debug level' - еще один надуровень - глубина отладки что ли. Но это уже экстрим и если вам вдруг понадобился и третий параметр, то вы и без меня прекрасно понимаете зачем он вам и что с ним делать.

Со старта я бы не занимался разведением дополнительных сущностей. Когда помимо просто уровня требуется еще и детализация, то журналирование и его наполнение наверняка указаны в ТЗ изначально. А если нет, а вы при этом подозреваете, что без второго или третьего вам не обойтись, то следует прежде всего озаботиться включением этого пункта в задание. Можно принять за правило, что чем выше уровень журналирования, тем подробнее информация. А серьезные ошибки следует сопровождать предварительной записью всех вариантов уровней.

Ну, например. Сообщение о самой ошибке может содержать конкретный код исключения, не знаю, 'ERR: ORA-00001'. Но перед ней будут вывалены 'DEBUG: unique constraint (MYBASE.PK_SN_FIELDS_DESC) violated', и 'TRACE: Callstack(5) MYPKG.ADD_REC(2) -> MYOTHERPKG(1, 2) ->.... Env(11) OS=Windows;...'. Таким образом, включая более высокий уровень журналирования вы можете получить то же самое, что и при использовании отдельного уровня детализации.

Содержание

Сложнее всего придумать что-то общее к содержимому самих текстов ошибок. Если это какой-то вычислитель дифференциальных уравнений, то наверное можно обойтись буквенными обозначениями и числами, которые нормальный математик способен понять и без дополнительных пояснений. Во всех остальных случаях обычно хорошо бы иметь человеческий комментарий к происходящему. Причем человеческий не образно, а буквально - т. е. не для дальнейшего разбора каким-то другим адским анализатором логов.

Тут часто можно увидеть некую дихотомию между тем, что понимают под человеческим языком программисты и пользователи. Программист часто оказывается слишком высокого мнения о пользователе. "Ну понятно же 'RuntimeException, mod_php7_apache2.so' и поэтому возвращаем '500 - Internal Server Error'" - может не со зла думать разработчик веб-сервера. Для пользователя, хотя он как правило все-таки админ, этого может показаться мягко говоря не достаточно. Да что там веб-сервер, даже современные браузеры до сих пор плюются в пользователя чем-то типа 'ERR_SSL_VERSION_OR_CIPHER_MISMATCH'. 'ВАСЯ_ДУРАК_НИЧЕГО_НЕ_ПОНЯЛ_УАХАХ' - так это выглядит обычно для пользователя, а не "попробуйте убрать 's' из 'https' в адресе".

Я для себя подразделяю содержание примерно таким образом. Info - это вот как-раз то самое для пользователя, будь он хоть сто раз админ, когда он открыл текстовый файл, там должны быть дата, время и что-то типа '[INFO] Division by zero. Weight must be positive number'. На английском - это важно. Почему поясню ниже. Warning и Error это тоже для пользователя, но может быть чуть более формально, для того что бы журнал давал исчерпывающую информацию об ошибке или предупреждении. Может быть что-то даже с кодом. '[ERR-01] Cannot open config.json - access denied' - в самый раз. Но не в коем случае не '[E01] IOException 0x034 fopen(config.txt, r)' - с натяжкой такое можно выбросить админу. Это уже Debug. Trace соответственно может уже содержать конкретные данные и стек вызовов где именно происходит неудачное чтение файла. Trace это то, что может уже понадобиться разработчику и ничего не скажет администратору - то что можно приложить к письму помимо скриншота пустой страницы с индикатором загрузки.

Формат

Если хочется сделать журнал непременно совместимым с каким-то существующим форматом то проблем нет, они обычно хорошо описаны. Проблема таких журналов, как и было отмечено, в том, что они предназначены для дальнейшего разбора, а не для человеческого глаза. А если журнал кто-то должен много просматривать и понимать что там и зачем, то лучше сделать что-то более удобочитаемое. Помимо обязательных полей, таких как дата, время, уровень, какой-то код сообщения нужно обратить внимание на такие вещи как обособление наименований и значений каким-то однообразным способом. Если в первых двадцати строках идет регистрация добавленных в плейлист файлов, а потом идет многострочный html с описанием плейлиста, а потом служебная информация об аудиокодеках и методе сжатия, то читать такое будет если не бесполезно, то сложно. Однообразность - лучшее качество журнала.

Не надо кодировать какие-то поля в не читаемый вид. Например, объекты и массивы не следует сериализовать и приводить к однострочному тексту - он там такой не нужен. Пускай будут в высоту. Не нужно избегать псевдографического форматирования. Пусть SQL запросы и куски XML будут оформлены табуляцией. Штампы времени нет смысла выводить в секундах от Unix Epoch или в каком-то локализованном формате, 'YYYY-MM-DD HH:MM:SS' - вполне себе читаемый, хорошо фильтруемый и даже сортируемый формат. Локализация же текста вещь дискуссионная, но я придерживаюсь исключительно английского. Не потому что так выглядит важнее или из-за проблем с кодировками, а ввиду того, что потенциально ведь никогда не исключено, что вашу программу когда-то запустит немец, китаец или тринидад-и-тобагец. Зачем ограничивать пользователей по национальному признаку изначально? Лучше уж если вы потом решили локализовать программу на все языки, то для этого существуют другие методы и приемы.

2021-03-11 22:37:11 Sauron [INFO] Starting world enslavement
2021-03-11 22:37:11 Sauron [DEBUG] makeRing(1)
2021-03-11 22:37:11 Sauron [TRACE] Object mainRing =
{
"text" : "Ash nazg durbatulûk,\nAsh nazg gimbatul,\nAsh nazg thrakatulûk,\nAgh burzum-ishi krimpatul."
"subRings" :
[
{
"type" : "man",
...
},
...
]
}
2021-03-11 22:37:11 Sauron [DEBUG] divide() & rule()
2021-03-11 22:37:11 Sauron [INFO] World enslaved
2021-03-11 22:37:11 Sauron [WARN] Isildur was born
...

Какой профит

Журнал работы такая же неотъемлемая часть любого сколь-нибудь серьезного программного продукта или комплекса, будь-то страшное бэкэнд е-коммерс приложение, игрушка, веб-сервис, андроид приложение, или хотя бы калькулятор калорий. Если журнал программе не нужен, то это и не совсем программа. Конечно, журнал не должен быть самоцелью и делается он из утилитарной необходимости: для простоты сопровождения, для удобства обратной связи от пользователя, для анализа работы и отладки в боевых условиях - не дебаггером единым. В дальнейшем можно подключить к нему и разнообразные действия, как то оповещение, автоматическую отправку отчетов, перекачивать их в ту же базу данных, собирать в архивы аудита и много всего прочего.

Полезно стратегически выбрать для себя раз и навсегда какой-то общий подход и действовать в его рамках. Разучить единожды несложные манипуляции с распространенными библиотеками и фреймворками. Если на стадии прототипирования нет времени увлекаться получением хорошего журнала полезно просто оставить комментарии и метки, куда потом можно будет вдумчиво добавить сообщения. Отсюда можно и заранее рассчитать объем будущих временных затрат на добавление нормального журналирования. Почему-то все привыкли к тому, что документированию и тестированию отдается хорошая доля времени проекта, а на добавление журнала если его нет в ТЗ то как-то всем и наплевать. Журнал по моему убеждению не какая-то отдельная фича которой может не быть, это такая же часть софта как и интерфейс, локализация или конфигурация, как, собственно, код.

End;

Что-то пошло не так, и нам не удалось загрузить комментарии. Попробуйте ещё раз
Документы, вакансии и контакты