Что такое логгер в программировании
Логирование. NLog Platform. Зачем нужны логи в приложении
Здесь мы затронем тему логирования в нашем приложении, что такое логи, и как правильно вести логи, насколько это необходимо и полезно. Рассмотрим систему логирования NLog Platform.
Ведение логов помогает разработчику в процессе создания и последующего сопровождения приложения, при поиске ошибок в коде и в разрешении непонятных ситуаций, когда наше приложение в момент работы ведет себя странным образом, и нам нужно найти причину такого поведения.
Любой разработчик сталкивается с подобными ситуациями, когда какой-то компонент приложения отрабатывает странным образом, выдает не тот результат или вообще перестает работать. Использование логов поможет нам в подобных ситуациях. Время поиска проблемных мест в нашем коде сократится в разы, и мы быстрее сможем решить ту или иную проблему.
Вообще, на сегодняшний момент ни одно более или менее серьезное приложение не обходится без написания логов.
Под таким журналом можно понимать и записи в обычный текстовый файл, и записи в базу данных, и записи на удаленный веб-сервис, и даже email-письма на определенный адрес о тех или иных состояниях нашего приложения.
Какие записи делать в этот журнал, то есть, какую конкретно информацию записывать, определяет сам разработчик. Это могут быть сведения о том, что все работает в штатном режиме, то есть просто ежедневный мониторинг нашего приложения, или что произошла какая-то ошибка, на которую нужно максимально срочно отреагировать и устранить, и так далее.
Всего существует шесть уровней логирования, каждый из которых предназначен для сообщений того или иного типа, той или иной важности:
Trace – максимально детальная информация о том, что происходит с целевым участком кода, по шагам. Например: Попытка открыть подключение к БД, успешно\неуспешно. Сколько времени заняла эта операция. Сколько времени выполнялась выборка из БД, успешно\неуспешно. Сколько записей извлечено. Какая была нагрузка на систему, сколько использовано памяти. Сколько записей прошло нужную фильтрацию. Сколько записей оказалось в результирующей выборке, куда эти записи отправились дальше. Проверка нужных значений в каждой записи.
Debug – это информация для отладки. Логирование крупных операций, менее детально, чем в Trace. Здесь мы не так подробно описываем весь процесс операции, но, тем не менее, заносим в журнал основные операции. Например: Совершено обращение к БД. Из базы выбрано N записей. Записи успешно обработаны и отправлены клиенту.
Info – это более общие информационные сообщения о текущей работе приложения, что происходит с системой в процессе ее использования. Например: Была выгрузка студентов в Excel-файл. На сайте зарегистрирован новый студент. Студент добавил новый отчет. Студент перемещен в другую группу.
Warn – сообщения о странной или подозрительной работе приложения. Это еще не серьезная ошибка, но следует обратить внимание на такое поведение системы. Например: Добавлен студент с возрастом 2 года. Студент получил отрицательный балл. Преподаватель завершил курс, в котором училось 0 студентов. В группе находится больше студентов, чем максимально возможно.
Error – сообщения об ошибках в приложении. Подобные сообщения – это уже большая проблема, которую нужно решить для дальнейшей правильной работы системы. Например: Ошибка сохранения нового студента в БД. Невозможно загрузить студентов в данной группе. Ошибка при входе в личный кабинет студента.
Fatal – сообщения об очень серьезных ошибках в системе. Чаще всего это связано с работоспособностью всего приложения или его окружения на сервере. На такие сообщения следует реагировать МАКСИМАЛЬНО оперативно. Например: Приложение постоянно перезагружается из-за нехватки памяти или места на жестком диске. Приложение завершило работу по неизвестной причине. Нет доступа к базе данных. Нет доступа к сети. Заблокирован какой-то порт.
То есть, прежде чем отправить какое-то сообщение в лог, нам нужно отнести его к той или иной группе.
Например, мы написали новый функционал и хотим его протестировать, как правильно и быстро он работает. Для этого мы будем использовать тип сообщений Trace, то есть все наши сообщения в логе будут помечены как Trace.
Подобным образом мы можем описать, как работает наше приложение в целом, сообщения будут с пометкой Info.
Если же в опасных участках кода мы генерируем исключение, то теперь мы также добавим запись в лог с пометкой Error.
К какой группе отнести то или иное сообщение решает сам разработчик. К данному вопросу следует подойти с максимальной серьезностью. Очевидно, что ошибки не следует помечать как Info, не следует игнорировать ошибки и просто не записывать их в лог. От правильно настроенной системы логирования будет зависеть простота сопровождения всей системы, оперативность реагирования на ошибки и время, затраченное на устранение проблемы.
Иногда разработчики ленятся писать логи, не хотят тратить на это время. В дальнейшем оказывается, что время, затраченное на поиск и исправление ошибок, в разы больше времени, которое потребовалось бы на создание системы логов.
Естественно, многое зависит от сложности проекта. Если вы создаете простейший трехстраничный сайт-визитку или консольное приложение для собственных нужд у себя на локальном компьютере, то написание сложной системы логирования может быть дольше, чем создание самого проекта. В таком случае в логи можно записывать только сообщения об ошибках или почему упал сайт. Но если вы работаете над сложным проектом в команде с другими разработчиками, то грамотное ведение логов просто обязательно.
Для того, чтобы начать логирование, мы подключим в наш проект платформу NLog. Это можно легко сделать посредством менеджера NuGet (прямо из Visual Studio).
Обратите внимание на конфигурационный файл NLog.config. В этом файле находятся настройки логгера (куда будут выводиться логи, формат записи логов и т.д.). Давайте настроим файл следующим образом:
Далее уже в коде объявим новый логгер (здесь код проекта приводится в сокращенном виде, исходный код всего проекта можно скачать в конце статьи):
Чаще всего следует объявлять один статичный логгер в пределах всего класса. Здесь мы посредством класса-менеджера LogManager объявили новый логгер, с которым будем работать.
Начнем логирование с уровня Trace. В методе, где мы выбираем студента по его идентификатору, давайте максимально подробно опишем как это происходит:
Теперь давайте добавим несколько сообщений уровня Debug. Как мы помним, это тоже отладочная информация, но менее детальная. Данный подход мы используем в другом методе, для наглядности:
Идем далее. На уровне Info мы описываем регулярные операции в нашем приложении, то есть поднимаемся еще на уровень выше. Предположим, что мы работаем над ASP.NET MVC приложением, и у нас есть действие в контроллере, которое обращается к ранее описанному методу GetStudentById():
Теперь добавим в логи сообщения уровня Warn. Как мы помним, на этом уровне логирования мы описываем все потенциально опасные ситуации, странное и нелогичное поведение компонентов. Будем заносить в лог запись, если студенту меньше 15 лет:
Далее обработаем ошибку в нашем коде и запишем в лог сообщение уровня Error:
Теперь определим, что же нам записать на уровне Fatal. В нашем простейшем примере просто смоделируем подобную ситуацию:
Мы рассмотрели все шесть уровней логирования и описали процесс работы нашего приложения максимально подробно. Теперь мы можем сразу проанализировать работу сайта, просто изучив логи, и не заглядывать в исходный код.
Подобным образом происходит логирование. В нашем простейшем примере, где мы моделируем работу со студентами, все предельно ясно и прозрачно даже без логов. Но в сложных проектах ведение логов является неотъемлемой частью разработки.
Конечно, это далеко не полные возможности настройки платформы NLog. В конфигурационном файле можно настроить запись логов в другие места, например, в базу данных, на консоль, в оперативную память, отправлять как емаил-сообщение, отправлять сообщения по сети и так далее. Также можно настроить фильтрацию сообщений, более сложный шаблон сообщений. Если вас не устраивает стандартный функционал логгера, то можно написать свое собственное расширение и подключить.
На этом здесь все, давайте подведем небольшой итог. Мы изучили тему логирования в приложении. Посмотрели как правильно логировать те или иные участки кода, а также познакомились с одной из самых популярных платформ логирования – это NLog Platform, также рассмотрели ее возможности и как можно настроить генерацию логов на этой платформе.
Архитектура логирования
Мой опыт разработки в основном строится вокруг разнообразных сетевых cервисов под Windows и Linux. Я обычно стремлюсь добиться максимальной кроссплатформенности вплоть до бинарной совместимости. И конечно, накопилось некоторое количество стабильных решений связанных с логированием.
Топик написан как продолжение к этой статье и будет полезен в первую очередь начинающим программистам.
Итак, начну со своих дополнений к предыдущей статье.
Я как и автор пользуюсь NLog’ом и разумеется широко использую его особенности. Конечно, после
их реализации в любом другом логгере, нижеописанную практику можно применять и у них.
Кстати, log4net продолжает развиваться.
Под капотом NLog
Сразу обсудим полезность второй фичи.
Часто, при разработке кода, возникает необходимость посмотреть значение какой либо переменной в процессе выполнения. Обычно, используют дебаггер и останавливают программу в интересующем месте. Для меня же, это явный признак, что этом месте будет полезен Trace вывод. В комплекте с юнит-тестами мы сразу получаем развертку этой переменной во времени и протокол для сравнения с тестами в других условиях. Таким образом, дебаггером я практически не пользуюсь.
Очевидно, что в боевом применении, даже выключенное подробное логирование, может мешать как скорости выполнения так и параллельности.
Исходный код класса можно посмотреть тут.
Отлично! Для NLog можно быть уверенным, что ваши сколь угодно детальные сообщения могут быть отключены и это минимально скажется на производительности. Но, это не повод посвящать логированию половину кода.
Что и как логировать
Следует придерживаться правил:
В целом, при выводе в лог, всегда отмечайте, то количество потенциально лишних вычислений, которые потребуются для случая когда лог отключен.
Простой пример (фрагмент некоторого класса):
private static Logger Log = LogManager. GetCurrentClassLogger ( ) ;
public string Request ( string cmd, string getParams )
Uri uri = new Uri ( _baseUri, cmd + «?» + getParams ) ;
HttpWebRequest webReq = ( HttpWebRequest ) WebRequest. Create ( uri ) ;
webReq. Method = «GET» ;
webReq. Timeout = _to ;
using ( WebResponse resp = webReq. GetResponse ( ) )
using ( Stream respS = resp. GetResponseStream ( ) )
using ( StreamReader sr = new StreamReader ( respS ) )
page = sr. ReadToEnd ( ) ;
catch ( Exception err )
Все аргументы логирования требовались для логики. Сообщение Debug отмечает аргументы с которыми мы пришли в функцию. В обработчике ошибки мы дублируем входные параметры на случай отключения Debug уровня. А это уже даст информацию при необходимости написать юнит-тест. Стек исключения не выводим, так как остается возможность сделать это вышестоящим обработчиком.
Вообще, в текущем обработчике ошибок полезно детализировать контекст который к привел к исключению и специфичные особенности исключения. В примере было бы полезно вывести поле Status для случая WebException.
Гарантии сохранности лога
Несмотря на некоторые возможности NLog по авто записи логов, нет гарантии сохранности лога при завершении процесса.
Первое, что следует сделать, это обработать событие AppDomain.UnhandledException. В нем следует записать в лог полную информацию об ошибке и вызвать LogManager.Flush(). Обработчик этого события использует тот же поток, который и вызвал исключение, а по окончании, немедленно выгружает приложение.
private static readonly Logger Log = LogManager. GetCurrentClassLogger ( ) ;
public static void Main ( string [ ] args )
static void OnUnhandledException ( object sender, UnhandledExceptionEventArgs e )
Кроме того, следует вызывать LogManager.Flush() везде, где потенциально возможно завершение процесса. В конце всех не фоновых потоков.
Если ваше приложение представляет собой win-service или Asp.Net, то следует обработать соответствующие события начала и завершения кода.
Сколько логировать
Серьезная проблема для разработчика. Всегда хочется получать больше информации, но код начинает выглядеть очень плохо. Я руководствуюсь следующими соображениями.
Вывод в лог это по сути комментарий. Логирование уровня Trace по большей части их и заменяет.
Уровни Trace и Debug читают разработчики, а все что выше — техподдержка и админы. Поэтому до уровня Info сообщения должны точно отвечать на вопросы: «Что произошло?», «Почему?» и по возможности «Как исправить?». Особенно это касается ошибок в файлах конфигурации.
Боевое развертывание
Предположим, разработка дошла до внедрения.
Отложим вопросы ротации логов, размера файлов и глубины истории. Это все очень специфично для каждого проекта и настраивается в зависимости от реальной работы сервиса.
Остановлюсь только на смысловой организации файлов. Их следует разделить на 3 группы. Может потребуется развести логи модулей в разные файлы, но дальше я все равно буду говорить об одном файле для каждой группы.
Если приложение успешно внедрено, то в работе остаются только первые две группы.
Расследование сбоев
Когда работающий сервис подает признаки ошибки, то не следует его пытаться сразу перезагружать. Возможно нам «повезло» поймать ошибки связанные с неверной синхронизацией потоков. И не известно сколько в следующий раз ждать ее повторения.
В первую очередь следует подключить заготовленные заранее конфиги для группы наблюдения. Как раз это и должен позволять делать приличный логгер. Когда мы получили подтверждение о том, что новая конфигурация успешно применена, то пытаемся опять спровоцировать сбой. Желательно несколько раз. Это обеспечит возможность для его воспроизведения в «лабораторных» условиях. Дальше уже работа программистов. А пока можно и перезагрузиться.
name = «fileInfo» type = «AsyncWrapper» queueLimit = «5000» overflowAction = «Block» >
type = «File» fileName = «$
name = «fileWarn» type = «AsyncWrapper» queueLimit = «5000» overflowAction = «Block» >
type = «File» fileName = «$
name = «*» minlevel = «Info» writeTo = «fileInfo» />
name = «*» minlevel = «Warn» writeTo = «fileWarn» />
При настройке фильтров следует учитывать относительность уровней логирования для каждой из подсистем. Например, некоторый модуль, имея Info сообщение об инициализации, может быть создан для каждого подключенного пользователя. Разумеется, его вывод в Info группу следует ограничить уровнем Warn.
Чего с логгером делать не следует
Логгер должен быть простым и надежным как молоток. И у него должна быть четко очерчена область применения в конкретном проекте. К сожалению, разработчиков часто трудно удержать. Паттерны проектирования, это в основном полезно, но не этом случае. Достаточно часто стал замечать предложения выделить для логгера обобщенный интерфейс (пример) или реализовать обертку в проекте, чтобы отложить муки выбора NLog vs log4net на потом.
Что бы ни было тому причиной, надо точно помнить, что в первую очередь, такие удобства напрочь убивают компилятору возможность оптимизации.
Не стоит напрямую выводить информацию логгера пользователю, даже с фильтрами. Проблема в том, что эта информация зависит от внутренней структуры программы. Вряд ли вы в процессе рефакторинга пытаетесь сохранить вывод логгера. Наверное, здесь стоит просто задуматься и разделить функционал. Возможно, в проекте просто требуется еще один уровень логирования. В этом случае как раз и уместна будет обертка над логгером.
Чего же мне еще не хватает в NLog?
NLog, Log4Net, Enterprise Library, SmartInspect.
Разнообразные сравнения логгеров между собой, упускают одну важную деталь.
Важно сравнивать не только ограничения/возможности, но и возможность быстро добавить свои «хотелки».
Поэтому, буду пока дружить с NLog.
Чего и Вам желаю.
Python: Логируем как профессионалы
Часто вижу, что помимо обработки исключений, люди мучаются кое с чем еще, а именно с логированием.
Большинство людей не знают, что писать в логи, поэтому решают логировать все, что угодно, думая, что все подряд – это в любом случае лучше, чем ничего, и, в конечном итоге, просто создают шум. А шум – это информация, которая никак не помогает вашей команде понять, в чем дело и как решить проблему.
Более того, я не думаю, что эти люди могут уверенно пользоваться уровнями логирования, поэтому используют по умолчанию logger.info везде (если не пишут print ).
Наконец, люди, похоже, не знают, как сконфигурировать логирование в Python, понятия не имеют, что такое обработчики, фильтры, методы форматирования (форматтеры) и т.д.
Цель этой статьи – разъяснить, что такое логирование и как вы должны его реализовывать. Я постараюсь привести содержательные примеры и обеспечить вас гибкими эмпирическими приемами, которые следует использовать при логировании в любом приложении, которое вы когда-либо будете создавать.
Введение
Примеры облегчают визуальное восприятие, поэтому мы будем рассматривать следующую систему:
Пользователи могут подключать несколько интеграций к ресурсам (например, GitHub, Slack, AWS и т.д.)
Ресурсы уникальны в зависимости от интеграции (например, репозитории списков с GitHub, диалоги из Slack, экземпляры списков EC2 из AWS и т.д.)
Каждая интеграция уникальна, имеет свой набор сложностей, конечных точек, шагов и т.д.
Каждая интеграция может привести к различным ошибкам, которые могут возникнуть в любое время (например, неверная аутентификация, ресурс не существует и т.д.)
Я не буду сосредотачиваться на проблемах поддержки таких интеграций, просто пронаблюдаем за тем, как это работает.
Природа логирования: хорошее логирование имеет значение
Для начала давайте проанализируем характеристики логов.
«Наглядными» мы их называем потому, что они предоставляют вам какую-то информацию, «контекстными», потому что они дают вам общее представление о том, как обстоят дела на данный момент времени. И наконец, «реактивными» они являются потому, что они позволяют вам предпринимать действия только после того, как что-то произошло (даже если ваши логи отправляются/получаются в режиме реального времени, на самом деле вы не можете изменить то, что произошло только что).
Если вы не будете учитывать природу логов, то будете производить только шум, что снизит вашу производительность.
Дальше я приведу несколько примеров, основанных на системе, которую мы определили выше:
Если вы зададите описание, к примеру «operation connect failed», но не добавите контекст, трудно будет понять, какая из интеграций не отработала, кто пострадал, на каком этапе подключения произошел сбой, поэтому и среагировать вы не можете. В конечном итоге вы будете копаться в тонне логов без малейшего представления о том, где может быть проблема.
О, а еще не стоит недооценивать способность разработчика испортить описание. Сделать это легко, просто отправив поверхностные сообщения без какого-либо контекста, например «An error happened» или «An unexpected exception was raised». Если я прочту такое, то даже не пойму, на что повлияла ошибка, потому что не буду знать, ЧТО конкретно произошло. Так что да, можно сломать даже основной смысл логов.
Логи – это конфиденциальная информация из вашего программного обеспечения, нужная чтобы вы оставались в курсе происходящего и могли реагировать на ситуации. Любые логи, которые не дают вам такой информации – это шум.
Когда нужно логировать?
Чтобы логи оставались реактивными, вам нужно логировать «события». Сделайте их такими же понятными и удобными для чтения, как эта статья. Возможно, вы не прочитали каждую строку, которую я написал выше, но вы все равно можете продолжить дальше, пропустить ненужные разделы и сосредоточиться на том, что привлекло ваше внимание. Логи должны быть такими же.
Есть эмпирическое правило построение логов:
В начале соответствующих операций или потоков (например, при подключении к сторонним сервисам и т.д.);
При любом достигнутом прогрессе (например, успешная аутентификация, получен валидный код ответа и т.д.);
При завершении операции (успешном или неудачном).
Логи должны рассказывать вам историю, у каждой истории есть начало, середина и конец.
Будьте осторожны с релевантностью, добавлять логи проще, чем удалять их, ведь все, что нерелевантно – шум.
Что логировать?
Чтобы логи были наглядными и контекстными, нужно предоставлять правильный набор информации, и я не могу сказать, какая информация будет являться таковой, не зная вашего случая. Давайте вместо этого воспользуемся нашим примером.
Рассмотрим интеграцию с AWS в качестве примера. Было бы круто иметь следующие сообщения:
Хороший пример логов
Сообщение
Понимание картины
Контекст
Началась операция с AWS
Атрибуты лога должны позволить мне выяснить, кто его вызвал
Retrieved instances from all regions
Был достигнут существенный прогресс
Connection to AWS has been successful
Операция с AWS завершилась
Атрибуты лога должны позволить мне найти сущности, на которые операция произвела положительный эффект
Пример логов об ошибках
Допустим, что извлечь экземпляры из региона af-south-1 не удалось из-за какой-то внезапной ошибки в этом регионе.
Сообщение
Понимание картины
Контекст
Началась операция с AWS
Атрибуты лога должны позволить мне выяснить, кто его вызвал
Failed to retrieve instances from regions af-south-1 when connecting to AWS for user X
Операция AWS не завершена, произошел сбой в регионе af-south-1, пострадал пользователь X
Я должен иметь возможность увидеть трассировку стека ошибки, чтобы понять, почему извлечение не удалось
В обоих случаях, я могу отследить, когда произошло какое-то событие (в логах есть отметки о времени), что именно произошло и кто от этого пострадал.
Я решил не указывать пользователя при начале и успешном завершении операции, потому что это не имеет значения (ведь это шум), поскольку:
Если я знаю, что что-то запустилось, но не знаю результата выполнения, то что я могу сделать?
Если все хорошо, то зачем беспокоиться?
Добавление таких данных делает логи шумными, потому что на них невозможно реагировать, делать-то с этим ничего не надо! Но я все еще должен быть в состоянии собрать детальную информацию из атрибутов (кто, когда, почему и т.д.). Если вы хотите что-то измерить, вам следует воспользоваться метриками, а не логами.
С другой стороны, логи об ошибках кажутся более подробными, и так и должно быть! Чтение таких логов дает достаточно уверенности, чтобы немедленно перейти к действиям:
Свяжитесь с пользователем Х и сообщите ему, что вам известно о проблеме в этом регионе.
Ключевой момент следующий: вы можете отреагировать сразу и для этого вам не требуется более глубокого изучения ситуации. Вы знаете все, что вам нужно, и можете немедленно принять меры для уменьшения ущерба. Разработчикам, возможно, потребуется углубиться в трассировку стека, чтобы собрать больше контекста (в случае с ошибкой), но общая картина уже становится ясна.
Любое сообщение об ошибке, в котором отсутствует эта минимальная информация, становится шумом, поскольку у вас появляется беспокойство, но вы все еще не можете действовать. Сначала нужно углубиться в ситуацию, чтобы понять, насколько проблема серьезна.
Если вы все еще не поняли, как писать полезные сообщения, я поделюсь с вами очень простым лайфхаком:
Всегда спрашивайте себя: Что я хочу уяснить для себя, после получения такого лога?
Предоставление контекста с помощью Python
В Python атрибуты логов можно добавить с помощью дополнительного поля, например:
Контекст не отменяет необходимости в содержательных сообщениях! Поэтому я бы так не делал:
Сообщения должны быть четкими и не оставлять места вопросам о том, что же вообще происходит. Контекст должен обогащать ваш опыт, предоставив информацию о более глубоких деталях, и давать вам понимание, по какой причине что-то произошло.
Нечто большее, чем logger.info и logger.error
Не так-то просто понять, что происходит, когда тысячи клиентов выдают логи «Connecting to Slack». Поскольку вы выдаете логи, а вашим приложением пользуются несколько клиентов, нужно иметь возможность фильтровать информацию по релевантности.
Чтобы упростить ситуацию, вот вам новое эмпирическое правило (будьте гибкими):
Уровень
Когда используется
Для какой-то действительно повторяющейся информации. Возможно, было бы полезно выдавать весь контекст информации, но порой этого не нужно.
Когда происходит что-то важное, достойное того, чтобы о нем было известно большую часть времени.
Случилось что-то странное (но не прервало поток/операцию). Если проблема возникнет на более поздних этапах, такой лог может дать вам подсказку.
Произошла ошибка, ее следует устранить как можно быстрее.
Произошла очень серьезная ошибка, она требует немедленного вмешательства. Если не уверены в критичности ошибки, применяйте ERROR .
Для описанной системы/потоков, я бы использовал уровни логов, как определено:
Что делать с logger.critical и logger.warning ?
WARNING – недостаточно веская причина для остановки потока, однако это предупреждение на будущее, если возникнет какая-то проблема.
CRITICAL – самый тревожный предупреждающий лог, который вы когда-либо получите. По сути, он должен быть той самой причиной встать в три часа ночи и пойти что-то чинить.
Для этих случаев мы рассмотрим:
Для AWS: если какой-то регион AWS недоступен, мы можем предположить, что у пользователя там нет никаких ресурсов, поэтому можно двигаться дальше.
Для Slack: если OAuth завершится неудачно из-за невалидного id клиента, значит остальные пользователи столкнутся с той же проблемой, интеграция не отработает, пока мы вручную не сгенерируем новый id. Это дело кажется достаточно критичным.
Непопулярное мнение: использование DEBUG -уровня на продакшене
Да, я считаю, что логи DEBUG нужно использовать на продакшене.
Другой вариант – включить дебаг после того, как странная ошибка потребует более детального разбирательства.
Простите, но для меня такой вариант недопустим.
В реальном мире клиентам нужна быстрая реакция, командам нужно выполнять свою работу и постоянно поддерживать системы в рабочем состоянии. У меня нет времени и пропускной способности для нового деплоя или включения флага и ожидания повторения проблемы. Я должен реагировать на неожиданные проблемы в считанные секунды, а не минуты.
Правильно настройте логгер
Еще я замечаю, что люди испытывают трудности при настройке логгера (или вообще его не настраивают). Конечно, документация в Python не очень дружелюбная, но это не оправдание, чтобы вообще ее не трогать.
Использование ручных команд непросто поддерживать и понимать;
fileConfig – негибкая история, у вас не бывает динамических значений (без дополнительных фокусов);
dictConfig – простая история в запуске и настройке.
Я поделюсь несколькими советами и определениями, которые вам надо знать, а затем мы создадим окончательную конфигурацию на реальных примерах из проектов, над которыми я работаю.
Вот кусочек того, о чем мы будем говорить дальше:
Что такое логгеры?
В любом случае, придерживайтесь:
Форматируйте логи
Форматтеры вызываются для вывода конечного сообщения и отвечают за него преобразование в конечную строку.
Когда я работал в Zak (бывшем Mimic), и даже сегодня в Lumos мы форматировали логи как JSON. Он является хорошим стандартом для систем, работающих на продакшене, поскольку содержит множество атрибутов. Проще визуализировать JSON, чем обычную длинную строку, и для этого вам не нужно создавать свой собственный форматтер (ознакомьтесь с python-json-logger).
Для локальной разработки я рекомендую использовать форматирование по умолчанию для простоты.
Ваше решение будет зависеть от вида проекта. Для Tryceratops я решил использовать обычный форматтер, поскольку он проще и работает локально, там нет нужды в JSON.
Фильтруйте логи
Фильтры можно использовать либо для фильтрации логов (внезапно), либо для добавления дополнительного контекста в запись лога. Рассматривайте фильтры, как хуки, вызываемые до обработки итогового лога.
Их можно определить следующим образом:
Или их можно добавить прямо в логгер или обработчик для упрощения фильтрации по уровням (скоро будут примеры).
Обрабатывайте логи и то, как все связано
Обработчики представляют из себя комбинации форматтеров, выходных данных (потоков) и фильтров.
С ними вы можете создавать следующие комбинации:
Выводить все логи из info (фильтр), а потом выводить JSON в консоль.
Выводить все логи, начиная с error (фильтр) в файл, содержащий только сообщение и трассировку стека (форматтер).
Наконец логгеры указывают обработчикам.
Пример logging.dictConfig
Теперь, когда вы понимаете, что делают все эти объекты, давайте писать собственные! Как всегда, я постараюсь показать вам примеры из реальной жизни. Я буду использовать конфигурацию Tryceratops. Вы можете открыть ссылку и посмотреть самостоятельно окончательную конфигурацию.
Шаблон конфигурации логирования
Начнем с такого каркаса, создадим константу LOGGING_CONFIG :
Version всегда будет 1. Это плейсхолдер для возможных следующих релизов. На данный момент версия всего одна.
Я рекомендую оставить значение disable_existing_loggers в False, чтобы ваша система не поглощала другие неожиданные проблемы, которые могут возникнуть. Если вы хотите изменить другие логгеры, я бы порекомендовал их явно переписать (хоть это и скучно).
Корневой логгер можно определить тремя разными способами, что сбивает с толку:
Выбирайте любой! Мне нравится оставлять его снаружи, поскольку так он выглядит очевиднее и подробнее говорит о том, чего я хочу, ведь корневой логгер влияет на все другие определенные логгеры.
Конфигурация логирования: форматтеры
Я дополню пример из Tryceratops примером с JSON из Lumos.
Конфигурация логирования: обработчики
Конфигурация логирования: логгеры и root
Давайте разберемся, что происходит:
Кроме того, обратите внимание, что я могу переписать правила по умолчанию. Через настройки или позже динамически. Например, каждый раз, когда triceratops получает подобный флаг от CLI, он обновляет конфигурацию logging чтобы включить дебаг.
Логирование – это важно, но наличие хорошо структурированных исключений и блоков try/except также важно, поэтому вы можете также прочитать, как профессионально обрабатывать и структурировать исключения в Python.
Всех желающих приглашаем на demo-занятие «Основы ООП». Цели занятия: научиться работать с классами и познакомиться с наследованием.
Краткое содержание:
— мутабельность экземпляров класса
— передача аргументов в инициализатор
— наследование
— переопределение методов
— обращение к методам суперкласса
>> РЕГИСТРАЦИЯ