Целью этого руководства является предоставление вам фундаментальной информации и практических приемов, которые помогут вам более эффективно отслеживать и устранять неполадки в своих службах.
При разработке приложений журналирование часто упускается из виду, но оно является важнейшим компонентом построения надежной и наблюдаемой системы. Правильные методы ведения журналов могут улучшить видимость вашего приложения, углубить понимание его внутренней работы и улучшить общее состояние приложения.
Включение механизмов журналирования по умолчанию в точки входа вашего приложения очень полезно. Эта автоматическая регистрация может фиксировать важные взаимодействия и потенциально включать аргументы точки входа. Однако очень важно проявлять осторожность, поскольку регистрация конфиденциальной информации, такой как пароли, может представлять угрозу конфиденциальности и безопасности.
Каждое существенное действие, выполняемое вашим приложением, должно создавать запись в журнале, особенно те действия, которые изменяют его состояние. Такой исчерпывающий подход к ведению журнала является ключом к быстрому выявлению и устранению проблем при их возникновении, обеспечивая прозрачное представление о работоспособности и функциональности вашего приложения. Такое усердие в регистрации упрощает диагностику и обслуживание.
Принятие соответствующих уровней журналирования имеет решающее значение для управления и интерпретации огромного количества данных, генерируемых вашим приложением. Классифицируя журналы по их серьезности и актуальности, вы гарантируете быстрое выявление и устранение критических проблем, в то время как менее срочная информация остается доступной, не перегружая ваши усилия по мониторингу.
Ниже приведены рекомендации по эффективному использованию уровней журналирования:
Уровень | Описание и примеры | Разрешенное использование | Не принимаются |
---|---|---|---|
| Фатальные события, которые останавливают работу системы. например, потеряно соединение с базой данных | Критические системные ошибки | Некритические ошибки, такие как неудачные попытки входа пользователя в систему. |
| Возникла проблема, но система может продолжить выполнение и завершить запрошенную операцию. | Возможные проблемы, ведущие к проблемам | Регулярные изменения состояния |
| Понимание обычных функций приложения, таких как создание учетной записи пользователя или запись данных. | Изменения состояния | Операции только для чтения без изменений |
| Подробная диагностическая информация, такая как начало/окончание процесса. | Шаги процесса регистрации не изменяют состояние системы. | Регулярные изменения состояния или высокочастотные операции |
| Самый подробный уровень, включая входы/выходы методов. | Понимание хода и деталей процесса | Регистрация конфиденциальной информации |
При регистрации действий в вашем приложении включение идентификаторов непосредственно участвующих объектов имеет решающее значение для связи информации журнала с данными базы данных. Иерархический подход помогает вам быстро найти все журналы, связанные с определенной частью вашего приложения, путем связывания элементов с их родительскими группами или категориями.
Например, вместо того, чтобы регистрировать только идентификатор чата, когда сообщение не может быть отправлено, вам следует также регистрировать идентификаторы комнаты чата и компании, которой он принадлежит. Таким образом, вы получите больше контекста и сможете увидеть более широкое влияние проблемы.
Failed to send the message - chat=$roomId, chatRoomId=chatRoomId, company=$companyId
Ниже приведен пример того, как могут выглядеть производственные журналы при использовании иерархического подхода:
Стандартизация форматов журналов во всех командах может значительно облегчить чтение и понимание ваших журналов. Вот некоторые стандартизированные префиксы, которые следует учитывать:
Отделение имен и значений переменных от тела сообщений журнала дает несколько преимуществ:
Log message - valueName=value
Вот примеры хорошо структурированных записей журнала, соответствующих обсуждаемым передовым практикам:
2023-10-05 14:32:01 [INFO] Successful login attempt - userId=24543, teamId=1321312 2023-10-05 14:33:17 [WARN] Failed login attempt - userId=536435, teamId=1321312
Эти примеры демонстрируют:
Ниже приведен пример того, как могут выглядеть производственные журналы при использовании предлагаемых практик:
Чтобы эффективно связать журналы с определенным действием пользователя, крайне важно включить в журналы traceId
или, как его еще называют, correlationId
. Идентификатор должен оставаться одинаковым во всех журналах, созданных логикой, запускаемой этой точкой входа, обеспечивая четкое представление о последовательности событий.
Хотя некоторые службы мониторинга, такие как Datadog, предоставляют группировку журналов «из коробки», это также можно реализовать вручную. В приложении Kotlin, использующем Spring, вы можете реализовать идентификатор трассировки для запросов REST с помощью HandlerInterceptor.
@Component class TraceIdInterceptor : HandlerInterceptor { companion object { private const val TRACE_ID = "traceId" } override fun preHandle(request: HttpServletRequest, response: HttpServletResponse, handler: Any): Boolean { val traceId = UUID.randomUUID().toString() MDC.put(TRACE_ID, traceId) return true } override fun afterCompletion(request: HttpServletRequest, response: HttpServletResponse, handler: Any, ex: Exception?) { MDC.remove(TRACE_ID) } }
Этот перехватчик генерирует уникальный traceId
для каждого запроса, добавляя его в MDC в начале запроса и удаляя после завершения запроса.
Реализация такого агрегирования журналов позволит вам фильтровать журналы, как показано в примере ниже.
Во многих системах объекты могут использовать либо UUID
, либо Long
идентификаторы в качестве основных идентификаторов, тогда как в некоторых системах могут использоваться оба типа идентификаторов для разных целей. Понимание значения каждого типа для целей ведения журнала имеет решающее значение для принятия осознанного выбора.
Вот список вещей, которые следует учитывать:
Читабельность: Long
идентификаторы легче читаются и они значительно короче, особенно если они не находятся в верхней части Long
диапазона.
Уникальное значение: идентификаторы UUID
обеспечивают уникальность во всей системе, позволяя вам искать журналы с использованием идентификатора, не сталкиваясь с проблемами конфликтов идентификаторов. Конфликты здесь означают, что существует вероятность того, что два объекта из несвязанных таблиц БД будут иметь одинаковый Long
идентификатор.
Ограничения системы : в системах, которые используют длинные первичные ключи в качестве идентификаторов объектов, добавление случайного идентификатора UUID
обычно является простым, в распределенной системе с идентификаторами объектов UUID
может быть сложно или дорого иметь Long
идентификаторы специально для регистрации.
Существующие журналы. Согласованность типов идентификаторов, используемых в журналах, имеет решающее значение, по крайней мере, для каждого объекта. Если система уже создает журналы для некоторых объектов и вы не планируете изменять их все, лучше придерживаться типа, который уже используется для идентификации объекта. Регистрация обоих идентификаторов может рассматриваться в течение переходного периода, но постоянное наличие нескольких идентификаторов приведет к излишнему загромождению журналов.
Правильные методы ведения журналов необходимы для эффективного наблюдения за услугами. Включив комплексное ведение журналов, соответствующие уровни журналов, идентификаторы трассировки и стандартизированные форматы журналов, вы можете значительно расширить свои возможности по мониторингу и устранению неполадок в ваших приложениях. Эти методы улучшают ясность и согласованность ваших журналов, упрощая диагностику и быстрое устранение проблем.
Спасибо, что нашли время прочитать этот пост!