(java || kotlin) && devOps
369 subscribers
6 photos
1 video
6 files
306 links
Полезное про Java и Kotlin - фреймворки, паттерны, тесты, тонкости JVM. Немного архитектуры. И DevOps, куда без него
Download Telegram
Всем привет! Есть такое правило - в логах в ПРОДе не должно быть ничего лишнего. Т.е. с одной стороны логи нужны для хранения стек-трейса исключения и другой полезной при разборе ошибки информации. Есть много инструментов, позволяющих строить по логам метрики, их парсить по заранее настроенной маске, вытаскивая полезную информацию. А с другой стороны логирование, как и любой другой функционал, жрет ресурсы. Как много? Вот так. На картинке показано число обработанных запросов для простейшего контроллера с логированием и без. Причём логирование происходило в RAM диск, т.е не учитываем задержки файловой системы. И шаблон сообщения в лог простой, контекст логирования для тех фреймворков, которые позволяют его настраивать, не используется. Вывод: отказываться от логирования не стоит, асинхрон рулит, а главное про уровни логирования и их настройку на ПРОДе забывать не стоит. Подробнее про условия теста: https://blog.sebastian-daschner.com/entries/logging-performance-comparison #java #logging #performance
Всем привет!

Я уже писал, как логирование может влиять на производительность: https://t.me/javaKotlinDevOps/15
Соберу в одном посте несколько важных опций, касающихся быстродействия log4j. Казалось бы логгер, что там может быть сложного, но...

1) Пару слов про то, почему лучше объявлять логгер статической переменной https://logging.apache.org/log4j/2.x/manual/usage.html

2) Две оптимизации касающиеся шаблонов сообщений: строка с параметрами в виде {} вычисляется лениво если включен нужный уровень логирования. Плюс поддержка передачи параметров в виде лямбд позволяет лениво вычислить значение параметра. https://logging.apache.org/log4j/2.x/manual/api.html
Итого конструкции вида if (logger.isTraceEnabled()) становятся не нужны.

3) логирование может быть синхронным и асинхронным. Последнее очевидно быстрее, но у него есть минусы: в случае ошибок сообщения могут терятся без уведомления прикладного кода, под асинхронное логирование нужен отдельный поток. Детали https://logging.apache.org/log4j/2.x/manual/async.html Там же есть сравнение по производительности, и оно впечатляет.

4) Начиная с версии 2.6 log4j переиспользует временные объекты и т.об. уменьшает нагрузку на сборщик мусора: https://logging.apache.org/log4j/2.x/manual/garbagefree.html
По ссылке говорится об ограничениях garbage free режима и есть графики производительности.

5) Логирование в memory-mapped файлы - по сути это область в памяти, за синхронизацию которой на диск отвечает ОС. https://logging.apache.org/log4j/2.x/manual/appenders.html#MemoryMappedFileAppender

Ну и сравнение производительности с logback и разных Appenders https://logging.apache.org/log4j/2.x/performance.html

P.S. Бросается в глаза, что авторы библиотеки начиная с версии 2 заморочились с производительностью. Куча графиков, статей

#logging #log4j
Всем привет!

На собеседовании я иногда задаю вопрос: приведите пример нарушения принципа Single responsibility. Или альтернативный вариант - а вот если в методе, к примеру, activateCard мы заодно отбросим метрики или залогируем результат - это нарушение принципа или нет.
На первый взгляд ответ - нет. Метрики и логи - это технический код, не бизнес функционал. Он может понадобиться в любом месте кода. Часто такой функционал реализуют с помощью аспектов, т.к. во-первых - это можно реализовать с помощью аспектов, а во-вторых - это красиво))), т.е. некий синтаксический сахар, улучшающий читаемость кода.
Но можно рассмотреть немного другую ситуацию. Предположим, есть код с математическими вычислениям. Или любой алгоритм. Или логика обработки данных. То, что хорошо реализуется в функциональном стиле - входные данные метода, результат, никаких внешних зависимостей. В нём нет внешних взаимодействий, сохранения в хранилище. Чистая логика. В этом случае логирование и метрики - это уже некая обработка полученного результата. Мы же не просто так выводим что-то в лог - это либо данные для разбора ошибки, либо отслеживание пользовательского пути, сбор статистики, отслеживание времени выполнения кода... Т.е. есть отдельная логика по месту и составу того, что мы логируем. Опять же контекст логирования часто требует инициализации, что добавляет ненужные зависимости в нашу логику. Поэтому такой код лучше поместить на уровень выше.
Итого: бизнес функционал и логирование/метрики - да, "чистая" логика - нет.

#logging #metrics #interview_question #code_design #solid #dev_compromises
Всем привет!

Продолжаю серию полезных видео - https://youtu.be/j-i3NQiKbcc
Тут по полочкам расписывает как работает логирование в Java.

Краткий конспект по архитектуре логирования:
1) адаптер - предоставляет API, которое вызывается из кода. На данный момент их 3 - SL4J, JCL (Apache Common Logging) и JBoss Logging. Самый распространенный и рекомендуемый - SLF4J
2) bridge - нужен, когда какая-то библиотека использует не тот адаптер, что мы хотим. По сути адаптер на адаптер, который эмулирует API, вызываемое из кода, и пробрасывает вызовы в нужный адаптер, как правило slf4j. Понятно, что когда у нас есть адаптер на адаптер, есть риск бесконечной рекурсии. Про это надо помнить)
3) движок логгера - компонента, которая пишет логи. Примеры: log4j, log4j2, logback, JUL\JDK (встроенный в JDK)
4) appender - компонент, определяющий физическое место, куда пишутся логи: консоль, диск, БД, MQ... Вот полный список для log4j2 https://logging.apache.org/log4j/2.x/manual/appenders.html
5) фильтры и конверторы - позволяют отфильтровать или преобразовать сообщения на клиенте

Плюс 3 хороших совета из видео:
1) соблюдать гигиену classpath - чистить его от лишних библиотек
2) логи в теории могут стать основой мониторинга, когда мы отбрасываем специальным образом размеченную запись в лог, которая после обработки лога становится событием мониторинга
3) не добавлять как зависимость в свои библиотеки движок логгера. Пусть его выберет потребитель, а не разбирается с вашими транзитивными зависимостями

И 2 полезные утилиты - миграторы на logback и slf4j с альтернативных библиотек логирования.

Из минусов вижу пожалуй один - там идет рассказ о связке slf4j и logback. Если во время создания видео logback сильно обогнал log4j, то сейчас с log4j2 ситуация меняется. Неплохо бы добавить сравнение с log4j2.

#java #logging
Всем привет!

Давно хотел написать небольшую памятку по уровням логирования.

Стандартный набор уровней логирования для бизнес приложений:
TRACE
DEBUG
INFO
WARNING
ERROR

Встречается еще один - FATAL - но на моей практике это что-то разряда OutOfMemory, которое прикладной код не выбрасывает.

TRACE - самый редко встречающийся уровень. Почему? Потому что всегда, когда стоит выбор между трассировкой через логи или запуском в режиме отладчика - нужно выбирать второе. Это банально быстрее, т.к. не требует пересборок. Почему именно пересборки, во множественном числе - потому что у нас ООП, куча классов и методов, с первого раза расставить TRACE правильно вряд ли получится. Единственный минус отладки - если это происходит на тестовом или dev стенде - другие люди не смогут там работать. Т.е. их нужно предупредить. Или еще лучше - иметь минимум 2 подходящих стенда. Когда стенд не подходящий - это ПРОМ (PROD) или PROD-like стенды, куда нет сетевого доступа с компьютера разработчика. Вот это пожалуй единственное место, где может понадобиться TRACE. И если он там понадобился, то возможно у вас проблемы либо на этапе тестирования. Либо какие-то значимые отличия в конфигурации стендов, что тоже может быть поводом задуматься.
Что делать с TRACE - убирать сразу после нахождения проблемы, т.е. в следующем хотфиксе.

DEBUG - используется как правило на тестовых стендах чтобы разобраться с пришедшими от смежников данными и как эти данные влияют на состояние нашего сервиса. Разработка и отладка идет на заглушках, что там придет от смежников до конца не ясно, несмотря на согласованную аналитику - вот этот вот кейс. Если используется для трассировки шагов - см. абзац выше)
Что делать с DEBUG - убирать перед фиксацией ПРОМ релиза. Почему бы не оставить? Даже не из-за производительности, этот момент решить можно, а из-за ухудшения читаемости. Размер кода увеличивается, лог как правило дублирует близлежащий метод в плане доносимой информации, т.е. нарушаем DRY. Если DEBUG во внешней библиотеке - отключаем через настройку уровня логирования для конкретного пакета.

Все уровни далее по большому счету пишутся для сопровождения и тестировщиков.

INFO - нет ошибки, но сервис дошел до некой важной точки, и эта информация поможет при разборе полетов в случае ошибок и сбоев. Я видел кейсы, когда сопровождение запрещало писать на ПРОМ с уровнем INFO, но со временем оно одумывалось, и запрет снимали).

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

ERROR - ошибка, прокидываемая клиенту. Не в виде stack trace конечно) Суть в том, что процесс обработки клиентского запроса прерывается, возвращается ошибка согласно API. Две самые частые ошибки, что я здесь вижу:
а) вывод error на любую ошибку смежника, даже не блокирующую.
б) error из используемой библиотеки, которая для нашего процесса не является блокирующей. В этом случае ее нужно убирать либо через настройку уровня логирования для пакета этой библиотеки, либо через ее доработку (если это возможно).

Эти три уровня убирать перед релизом не нужно, но стоит периодически их просматривать на предмет актуальности.

И последнее - по производительности. Просадка в производительности из-за логирования может быть из-за вычисления параметров даже в том случае, когда текущий уровень логирования выключен. Спасает ленивое вычисление параметров для выводимого в лог сообщения.
Это поддерживается в
а) log4j https://logging.apache.org/log4j/2.12.x/manual/api.html
б) slf4j https://www.slf4j.org/faq.html#logging_performance
через lambda параметры начиная с Java 8. Т.е. в большинстве инсталляций. Ну я надеюсь на это)

Еще просадка может быть из-за количества сообщений. Тогда смотри абзацы про TRACE и DEBUG выше. Еще можно глянуть мой пост про производительность в log4j https://t.me/javaKotlinDevOps/77 и поднять уровень логирования для ПРОМ.

#java #logging
Всем привет!

Продолжим про логи. Какие требования к ним предъявляются?

Для начала поговорим про разработку:

0) библиотека логирования. Очевидная вещь - для кода, работающего в ПРОМе, логи пишутся только через библиотеку логирования. Т.к. библиотека позволяет настраивать место хранения, формат, фильтрацию логов и многое другое.

1) структурированность. Должна быть возможность точного поиска по логам, а, возможно, настройки мониторинга по логам. Для этого ключевые события - в частности ошибки - должны однозначно (!) находится по специфической "метке". Пример: " ERROR " при выводе через log4j с форматом по умолчанию. Аналогично нужны "метки" для конкретных типов ошибок, если их нужно отслеживать.

2) достаточная детализация:
а) число записей. Достаточная детализация = логов не слишком мало, чтобы можно было понять причину ошибку, но и не слишком много. Конечно поиск рулит, но лишний объем - это замедление при передаче, хранении и обработке логов.
б) вывод стектрейсов. По умолчанию стектрейсы нужно логировать. Исключения - однозначно локализуемые в коде ошибки или бизнесовые ошибки.
в) поясняющий текст. Данные, содержащиеся в некой переменной, в лог стоит выводить предваряя их поясняющим текстом. Аналогично с текстом пришедшей извне ошибки.

3) корректность:
а) уровень ошибки. ERROR - должен быть ошибкой, прерывающей обработку запроса клиента. Об этом я писал в посте выше
б) отсутствие дублей. Одна ошибка не должна приводить к нескольким стектрейсам в логе. Т.е. выводить в лог исходное исключение, бросать свое, ловить его и снова выводить в лог - это не корректные логи, затрудняющие их обработку.

4) понятный вывод времени. Казалось бы, при чем тут разработчики? Речь о часовом поясе, а это настройки таймзоны для Java. Если время пишется в GMT - нужно постоянно помнить о смещении при разборе логов, а при разборе инцидентов приводит к некорректным выводам.

5) маскирование персональных данных. Если у вас есть ИБ (информационная безопасность) - требования можно спросить у них. Иначе - нагуглить в интернете. Зачем маскировать - излишний вывод (хранение) персональные данных нарушает закон

#logging
Всем привет!

Последняя статья из цикла про журналирование.

Все разработчики сталкиваются с разбором логов. При отладке, в логах сервиса на тестовых средах и ПРОМ, в логах пайплайна сборки или деплоя... Но не все там находят причину проблемы. Перечислю типовые ошибки при разборе. К слову - со всеми из них я сталкивался на практике. Поехали!

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

2) первопричина. Окей - смотрим весь лог, видим цепочку идущих подряд ошибок. Куда смотреть? На первую ошибку по времени. И последнюю в стектрейсе. Это и есть первопричина.

3) ошибки - не всегда ошибки. К сожалению. Встречаются "вечные" ошибки. Или, более приемлемый вариант, постоянные WARNing-и, говорящие о том, что для вашей среды что-то там не поддерживается. И то, и другое нужно игнорировать. Как это понять? Только насмотренность - после того, как этот лог увидишь в десятый (или пятидесятый) раз.

4) случайные ошибки. Если анализировать достаточно большой лог - там с большой вероятностью будут случайные ошибки. "Моргнула" сеть, рестартовал какой-то сервис... Особенно в тестовом контуре, но и на ПРОМе тоже. Снова вопрос - как понять, что ошибку не стоит разбирать? Первое - сравнить количество ошибок разных типов, второе - снова насмотренность.

5) не те логи. В k8s есть логи контейнера сервиса, а есть логи его сайдкаров. Если брать Openshift - для раскатки новой версии DeploymentConfig сервиса в нем используются т.наз. deploy поды, их логи как правило бесполезны. Если брать сервера приложений - там есть логи собственно сервера, а есть - каждого сервиса, развернутого на нем. Ошибка в логах джобы деплоя часто даёт мало информации о причине падения деплоя, нужно смотреть логи контейнеров. Т.е. вначале нужно определится - где может быть ошибка, которую мы ищем. И в конце концов - если в ваших логах есть ошибка интеграции, но причина ее не понятна - стоит попробовать достать логи смежного сервиса.

6) чужая ошибка. Если у нас нагруженная многопользовательская система - в логах могут встречаться ошибки разных пользователей. А мы сейчас разбираем проблему конкретного. Тут поможет фильтрация по атрибуту логов, содержащего id пользователя. Если конечно такой атрибут есть) Спойлер: должен быть)

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

#logging