Рубрики
Без рубрики

Искусство лесозаготовки

Все разработчики пытались отладить свои программы, печатая информацию о жизненном цикле и состоянии в… Помеченный как ведение журнала, java.

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

Основы ведения журнала

Ведение журнала – это гораздо больше, чем просто печать в stderr , однако. Типичные системы ведения журнала делятся на набор уровней ведения журнала, которые обычно определяют аудиторию и семантику события журнала. Например, в Apache Log4j 2 , уровни ведения журнала разделены на следующий набор:

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

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

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

На данный момент у нас есть довольно мощная абстракция над фильтрацией событий журнала, использующая как уровень, так и имя, но мы можем сделать лучше! К событиям журнала можно прикрепить дополнительную часть метаданных: маркеры . Маркер – это простая текстовая строка для обозначения какой-либо сквозной проблемы конкретного сообщения журнала. Это может быть использовано для маршрутизации определенных сообщений журнала в различные системы ведения журнала. Например, предположим, что сообщение журнала помечено маркером ПРЕДУПРЕЖДЕНИЕ . Конфигурация ведения журнала может иметь фильтр для этого маркера, который будет направлять эти сообщения независимо от уровня или имени регистратора в определенное место назначения. Это может быть канал оповещений в Slack или список рассылки оповещений.

В некоторых языках программирования, таких как Java, манипулирование строками считается несколько низкоуровневой операцией, поэтому здесь отсутствуют определенные функции создания шаблонов строк, которые были бы полезны для ведения журнала. Например, для регистрации сообщения, содержащего значения из некоторых локальных переменных, обычно требуется объединение строк, и если это сообщение журнала никогда не отображается, то указанная конкатенация была потрачена впустую. Такие мелочи, как это, могут со временем накапливаться, образуя значительные накладные расходы на производительность, так что мы, безусловно, можем добиться большего! Введите параметризованное сообщение журнала, которое по духу очень похоже на параметризованный SQL-запрос. В Log4j и многих других системах ведения журнала параметры задаются заполнителями {} в сообщении журнала и предоставляются в качестве дополнительных параметров для метода ведения журнала. Например:

logger.debug("User {} logged in", user.getName());

Заполнитель заполняется только в том случае, если включено ведение журнала отладки, поэтому полная строка никогда не вычисляется без крайней необходимости. Этот метод в основном относится к таким языкам, как Java. Например, в Scala-версии Log4j шаблоны строк являются встроенной функцией языка, а макросы используются за кулисами, чтобы избежать отображения шаблона при отключении ведения журнала. Пример:

logger.debug(s"User ${user.getName} logged in")

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

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

Куда Направляются События Журнала?

Теперь, когда мы создали общую структуру для записи и фильтрации событий журнала, что мы можем с ними сделать? Простейшей реализацией обработки событий журнала было бы выводить каждое сообщение журнала на консоль, разделенное новыми строками. Поскольку при этом будет потеряно довольно много контекста, мы обычно включаем дополнительную информацию из события журнала, такую как метка времени, уровень журнала, маркер (если он определен), имя регистратора и имя потока для многопоточных программ. Все поля, которые мы хотим вывести, должны быть настраиваемыми, и на самом деле доступно несколько различных полей , которые мы можем добавить, чтобы предоставить контекст для сообщения журнала. Формат вывода также может использовать структурированный формат, такой как JSON, который легче анализировать, чем сообщения журнала, ориентированные на строки, хотя все инструменты агрегирования журналов и поиска имеют мощные инструменты для извлечения информации о событиях журнала из всех видов форматов.

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

Однако во многих случаях простая печать на консоль, на которую никто не смотрит, не является правильной стратегией. Любому веб-сайту, требующему разумного уровня обслуживания и имеющему более, скажем, нескольких сотен пользователей, обычно требуется несколько серверов для распределения нагрузки. По мере увеличения числа узлов наблюдать за консолью становится просто невозможно. На самом деле, каждый узел может выполнять несколько приложений, поэтому без такой программы, как tmux , нам все равно пришлось бы перенаправлять stderr каждой программы в файл. Имея это в виду, мы можем напрямую настроить платформу ведения журнала для вывода событий журнала в файл вместо stderr . Каждый файл можно отслеживать с помощью такой программы, как tail , чтобы постоянно следить за добавлением новых событий журнала в файл. Этот стиль ведения журнала широко распространен в типичных системах GNU/Linux и BSD, где многие запущенные службы выводят информацию журнала в каталоги /var/log/ . Однако, если это не настроено на периодическую ротацию файлов журналов и удаление старых, то дисковое пространство сервера может в конечном итоге заполниться информацией о журнале! Это задание обычно заполняется такой программой, как logrotate , через Log4j есть rollingfileappender , который предоставляет аналогичную функциональность.

Простой вывод в файл журнала может быть хорошей стратегией для операторов, которые все еще придерживаются подхода “делай это вручную”, но мы можем сделать лучше! Нашей главной целью здесь должно быть сбор журналов со всех наших серверов в центральном месте, доступном для поиска. Одним из таких способов достижения этой цели является использование такого продукта, как ELK , Fluentd , или Серый лог . Эти инструменты предлагают больше, чем просто агрегирование журналов; они предлагают способы фильтрации, сортировки, поиска и оповещения на основе содержимого журналов. Однако, полагаясь на файлы журналов, мы также полагаемся на стабильность отдельных серверов. Получение журналов в сценариях стихийных бедствий, как правило, сложнее, но и гораздо важнее, поэтому давайте улучшим это.

Apache Flume – это проект для сбора и агрегирования больших объемов событий журнала в распределенной вычислительной среде. Это очень полезно в кластерных сценариях, таких как, например, запуск десятков или сотен узлов Apache Hadoop или Apache Spark. Отдельные узлы могут передавать события журнала агенту Flume, и каждый агент отвечает за надежную доставку событий журнала в другое место. В сочетании с приложением Flume это может быть легко использовано в распределенной среде для сбора всех событий журнала в центральном агрегаторе журналов. Указанный агрегатор может быть чем-то сложным, таким как Logstash или Graylog, или, возможно, это может быть что-то простое, например, один основной файл журнала.

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

Здесь можно было бы рассмотреть еще десятки фреймворков, библиотек и инструментов. Ведение журнала – это то, что делают все разработчики, независимо от того, используют они соответствующие инструменты или нет, поэтому неплохо ознакомиться с инструментами и концепциями, чтобы улучшить метаданные, создаваемые приложениями. Разработчики должны тесно сотрудничать с операторами (devops), чтобы найти хороший баланс между подробностью ведения журнала и наблюдаемостью. Управление журналами – сложная тема, которую многие люди склонны упускать из виду, но наличие хорошей архитектуры ведения журналов может помочь сэкономить время во время производственной проблемы. В качестве заключительного замечания для тех, кто использует платформу Java, Apache Log4j 2 является лучшей библиотекой ведения журнала для Java, Scala, Groovy, Kotlin и любого другого языка JVM. Ведение журнала обычно приводит к заметным накладным расходам приложений, и типичное решение состоит в том, чтобы просто отключить ведение журнала, но это в первую очередь устраняет все преимущества ведения журнала! Вместо этого взгляните на цифры и посмотрите, как Log4j можно использовать с минимальными накладными расходами даже в высокочастотных торговых приложениях.

Оригинал: “https://dev.to/jvz/the-art-of-logging-1dh1”