Русский
Русский
English
Статистика
Реклама

Логирование

Перевод Готовим console.log() правильно

11.11.2020 16:12:52 | Автор: admin
Специально к старту нового потока курса Frontend-разработчик делимся с вами полезным переводом. Конечно же, эта статья не только о тонкостях методов логирования. Автор изучил новые методы логирования, пока разрабатывал свой сторонний проект firecode.io, созданный для подготовки к собеседованиям. Автор рассказывает, как использует различные методы логирования в производственной среде собственного проекта и в чём именно они помогают. Кроме того, автор знакомит нас с платформой AppSignal, созданной, чтобы напрямую уведомлять разработчика о возникающих у пользователя исключениях в приложении. Подробности под катом.





Я считаю себя инженером внутреннего программного обеспечения и, как может подтвердить любой внутренний инженер, большая часть нашей жизни уходит на мониторинг, поиск и устранение неисправностей, а также отладку наших приложений. Фундаментальное правило разработки ПО: программное обеспечение будет давать сбои. Новых разработчиков от опытных отличает то, как они планируют эти сбои. Надежное и эффективное логирование важная часть планирования на случай неудач и, в конечном счёте, смягчения их последствий. Как и в случае разработки бэкенда, логирование может быть полезно в разработке программного обеспечения фронтенда, но оно гораздо больше, чем просто поиск и устранение неисправностей и отладка. Эффективное фронтенд-логирование, кроме того, может сделать разработку продуктивной, быстрой и радостной.

Несмотря на то, что я разделяю и практикую разработку через тестирование, мне нравятся гибкость, богатство информации и надёжность кода, предоставляемые разработчикам фронтенда, которые эффективно используют console.log(). Я решил поделиться некоторыми советами и хитростями, которые изучил и включил в свой рабочий процесс во время работы над Firecode.io в надежде, что некоторые из них помогут сделать ваш рабочий процесс разработки немного продуктивнее и веселее. С удовольствием разделю эти советы на две широкие категории: быстрое и грязное логирование, когда вы активно собираете и отлаживаете приложение, и долговременная запись в лог для понимания, когда ваше приложение работает, как ожидалось, а когда нет.

Советы по быстрому, грязному логированию разработки с console.log()

.

Не используйте console.log()

.
Да, правда. Я не пользуюсь console.log(). Хотя Ладно. Я пишу обёртки, которые используют console.log. Подробнее об этом в разделе логирования в производственной среде. Но, если вы хотите логировать что-то в приложении, чтобы увидеть, что происходит, используйте console.trace(). В дополнение ко всему из console.log() этот метод выводит всю трассировку стека, чтобы вы точно знали, откуда идёт сообщение.



Используйте имена вычисляемых свойств ES6 для идентификации объектов и чтобы не путать их с именами переменных.


Это просто используйте синтаксис вычисляемых свойств ES6: оберните объекты, которые вы хотите логировать, в фигурные скобки вот так: console.log({user}), а не console.log(user). Логирование аккуратное: ключ имя переменной, а значение сам объект. Такой подход особенно полезен, когда вы спешите и хотите логировать несколько объектов одной командой console.log().



Отображение уровней логирования: ERROR, WARN, INFO


console.log(param) по умолчанию имеет значение INFO однако в вашем распоряжении 3 других уровня логирования, которыми вы не должны пренебрегать console.debug(), console.warning() и console.error(). Помимо различий в форматировании (заметили разные цвета?) консоль разработчика в браузере позволяет легко отфильтровывать логи разных уровней с помощью удобного выпадающего списка, ненужные логи убираются вот так:





При логировании списков элементов пользуйтесь console.table()


Одна из моих любимых функций говорит сама за себя. Если вам когда-нибудь понадобится логировать список объектов, дайте шанс console.table().



Дебажим быстро с помощью debugger


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





Тонкое профилирование с console.profile() и console.time()


Хотите профилировать поток пользователя в вашем приложении, чтобы найти горячие точки? Укажите триггер console.profile(profileName) в начале профилируемого кода и console.profileEnd(profileName) в его конце, чтобы исследовать профиль процессора в потоке.





Кроме того, можно точно измерить, сколько времени занимает выполнение потока, поместив console.time(id) в его начале и console.timeEnd(id) в его конце.



Подсчёт количества выполнений кода через console.count()


Это одна из тех функций, для которых я не нашёл особого применения, тем не менее польза от нее есть: console.count(label) помогает точно узнать, сколько раз выполняется фрагмент кода, это полезно при поиске и устранении состояния гонки и в других ситуациях.



Красивое логирование с помощью CSS


Безусловно, это моя любимая консольная функция, которую я широко использую при логировании в производственной среде (подробнее об этом в соответствующем разделе). Ближе к сути: мы можем использовать строки форматирования, чтобы форматировать сообщения лога. Здесь %c модификатор для кода CSS, а всё, что после него, это сообщение.



Можно стилизовать несколько элементов, расширив строку через %s, вот так:



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



Логирование через console.log() в производственной среде

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

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

Не пользуюсь console.log()


Вместо этого я написал обёртку, которая содержит логику условного логирования, основанную на уровне логирования, который, в свою очередь, основывается на глобальной переменной бэкенда.

Внимание! Впереди фрагменты кода TypeScript. Если вы не знакомы с TypeScript, думайте о нём, как о надмножестве JavaScript с типами, на которых сделан акцент (это грубое упрощение). Иначе говоря, const str = "some string"; превращается в const str: string = "some string" типы добавляются после имени переменной с двоеточием.

Разрабатывая Firecode.io, я написал собственный фронтенд-фреймворк, который использует RxJS, но содержит знакомые концепции, такие как компоненты, из других популярных фреймворков, к примеру, React и Vue. При этом добавлены и другие концепции: движки для блоков тяжёлого для процессора кода, каналы для сообщений WebSocket и клиенты для HTTP-запросов. Очень важна была визуализация совместной работы всех этих частей, поэтому я реализовал пользовательское форматирование в классе-обёртке Logger, этот класс форматирует и визуально отделяет журналы от каждой части приложения.



Вместо вызова console.log("Cache SET to", {value}) я вызываю Logger.debug("Cache set to", {value}, Framework.Cache). В классе Logger есть перечисление TypeScript, сопоставляющее каждый компонент фреймворка с цветом:



Так я во время разработки визуально концентрируюсь на компонентах приложения. Например, когда я хочу посмотреть, что делает WsRequestCache, я могу отключиться от всех логов, кроме бирюзовых.

Защитите логи установкой уровня логирования на бэкенде


Я настроил Firecode.io на включение логирования на уровне отладки по умолчанию для пользователей-администраторов через переменную JavaScript, она устанавливается бэкендом. При этом предприимчивые пользователи все еще могут найти и установить соответствующие флаги в консоли разработчика, чтобы включить точный журнал. Это лучше, чем ситуация, когда каждому пользователю приложения по умолчанию представлены все логи, и лучше, чем постпроцессор, полностью удаляющий логи из приложения в производственной среде. Установка уровня логирования на бэкенде в Ruby on Rails:

const logLevel: number = <%= @app.get_log_level_for_user %>

И в классе Logger:

class Logger {   ...   ...   static info(...) {     shouldLog(Level.INFO) && console.log(...);        ...   }}

Логируйте возможные ошибки и уведомляйте о них


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

class Logger {   ...   ...   static error(e) {     if (shouldLog(Level.ERROR)) {       console.error(e);     }     appsignal.sendError(e);   }}

AppSignal содержит интеграции для передачи ваших ошибок в службы исходящих уведомлений, таких как Slack, PagerDuty и OpsGenie, вы даже можете подключить инструмент управления проектами, например JIRA или Trello, чтобы автоматически создавать Issues и баги для удобства вашей команды.

Итоги


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



И два дополнения. Я перестраиваю Firecode.io с нуля, добавляя совершенно новый набор вопросов для собеседований по кодированию JavaScript, Java, Python и Scala.

Если вы заинтересованы в написании кода для подготовки к собеседованию, который адаптируется к вашему стилю обучения и доставляет удовольствие, зарегистрируйтесь, указав свой адрес электронной почты здесь. При запуске нового Firecode.io я предоставлю вам бесплатную трёхмесячную подписку. Я буду размещать больше материалов о создании веб-приложений промышленного масштаба (таких как Firecode.io) с нуля в качестве стороннего проекта. Если вы новичок в JavaScript и хотите понять, как объектноориентированный JavaScript и прототипное наследование работают под капотом, ознакомьтесь с моей любимой книгой по этой теме The Principles of Object-Oriented JavaScript, и, если вам интересно узнать больше о том, почему вместо JS следует использовать TypeScript, ознакомьтесь с Effective TypeScript.

А помимо специальной литературы вам поможет промокод HABR, добавляющий 10 % к скидке на баннере.

image



Рекомендуемые статьи


Подробнее..

Удобное логирование на бэкенде. Доклад Яндекса

28.11.2020 12:19:52 | Автор: admin
Что-то всегда идет не по плану. Приходится отвечать на вопросы, Что сломалось?, Почему тормозит? и Почему мы не увидели этого раньше?. На примере простого приложения Даниил Галиев zefirior из Яндекс.Путешествий показал, как отвечать на эти вопросы и какие инструменты в этом помогут. Настроим логирование, прикрутим трассировку, разложим ошибки, и все это в удобном интерфейсе.

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



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

Небольшой дисклеймер: я буду говорить такие слова, как ручка и локаль. Поясню. Ручка возможно, яндексовый сленг, это обозначает ваши API, http или gRPC API или любые другие комбинации букв перед APU. Локаль это когда я разрабатываю на ноутбуке. Вроде бы я рассказал обо всех словах, которые я не контролирую.

Приложение Книжная лавка


Начнем. Наш стартап это Книжная лавка. Главной фичей этого приложения будет продажа книг, это все, что мы хотим сделать. Дальше немного начинки. Приложение будет написано на Flask. Все сниппеты кода, все инструменты общие и абстрагированы от Python, поэтому их можно будет интегрировать в большинство ваших приложений. Но в нашем докладе это будет Flask.

Действующие лица: я, разработчик, менеджеры и мой любимый коллега Эраст. Любые совпадения случайны.



Давайте немного про структуру. Это приложение с микросервисной архитектурой. Первый сервис Books, хранилище книг с метаданными о книгах. Он использует базу данных PostgreSQL. Второй микросервис микросервис доставки, хранит метаданные о заказах пользователей. Cabinet это бэкенд для кабинета. У нас нет фронтенда, в нашем докладе он не нужен. Cabinet агрегирует запросы, данные из сервиса книг и сервиса доставки.



По-быстрому покажу код ручек этих сервисов, API Books. Это ручка выгребает данные из базы, сериализует их, превращает в JSON и отдает.



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



И последняя ручка ручка кабинета. В ней немного другой код. Ручка кабинета запрашивает данные из сервиса доставки и из сервиса книг, агрегирует ответы и отдает пользователю его заказы. Всё. Со структурой приложения мы по-быстрому разобрались.

Базовое логирование в приложении


Теперь давайте про базовое логирование, то, которое мы впилили. Начнем с терминологии.



Что нам дает Python? Четыре базовые, главные сущности:

Logger, входная точка логирования в вашем коде. Вы будете пользоваться каким-то Logger, писать logging.INFO, и все. Ваш код больше ничего не будет знать о том, куда сообщение улетело и что с ним дальше произошло. За это уже отвечает сущность Handler.

Handler обрабатывает ваше сообщение, решает, куда его отправить: в стандартный вывод, в файл или кому-нибудь на почту.

Filter одна из двух вспомогательных сущностей. Удаляет сообщения из лога. Другой стандартный вариант использования это наполнение данными. Например, в вашей записи нужно добавить атрибут. Для этого тоже может помочь Filter.

Formatter приводит ваше сообщение к нужному виду.



На этом с терминологией закончили, дальше к логированию прямо на Python, с базовыми классами, мы возвращаться не будем. Но вот пример конфигурации нашего приложения, которая раскатана на всех трех сервисах. Здесь два главных, важных для нас блока: formatters и handlers. Для formatters, есть пример, который вы можете увидеть здесь, шаблон того, как будет выводиться сообщение.

В handlers вы можете увидеть, что logging.StreamHandler использован. То есть мы выгружаем все наши логи в стандартный вывод. Всё, с этим закончили.

Проблема 1. Логи разбросаны


Переходим к проблемам. Для начала проблема первая: логи разбросаны.

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

Теперь вопрос. Прибегает к нам менеджер и спрашивает: Там сломалось, помоги! Вы бежите. У вас же все логируется, это великолепно. Вы заходите на первую машинку, смотрите там ничего нет по вашему запросу. Заходите на вторую машинку ничего. И так далее. Это плохо, это надо как-то решать.



Давайте формализуем результат, который мы хотим увидеть. Я хочу, чтобы логи были в одном месте. Это простое требование. Немного круче то, что я хочу поиск по логам. То есть да, оно лежит в одном месте и я умею грепать, но было бы прикольно, если бы были какие-нибудь tools, крутые фичи помимо простого грепа.

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



Решение, которое можно использовать, ElasticSearch. Давайте его попробуем поднять. Какие плюсы ElasticSearch нам даст? Это интерфейс с поиском логов. Сразу из коробки есть интерфейс, это вам не консолька, а единственное место хранения. То есть главное требование мы отработали. Нам не нужно будет ходить по серверам.

В нашем случае это будет довольно простая интеграция, и с недавним релизом у ElasticSearch выпустился новый агент, который отвечает за большинство интеграций. Они там сами впилили интеграции. Очень круто. Я составлял доклад чуть раньше и использовал filebeat, так же просто. Для логов все просто.

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



В первую очередь нам нужно будет развернуть агент, который будет отправлять наши логи в Elastic. Вы регистрируете аккаунт в Elastic и дальше добавляете в ваш docker-compose. Если у вас не docker-compose, можете поднимать ручками или в вашей системе. В нашем случае добавляется вот такой блок кода, интеграции в docker-compose. Всё, сервис настроен. И вы можете увидеть в блоке volumes файл конфигурации filebeat.yml.



Вот пример filebeat.yml. Здесь настроен автоматический поиск логов контейнеров docker, которые крутятся рядом. Кастомизован выбор этих логов. По условию можно задавать, вешать на ваши контейнеры лейблы, и в зависимости от этого ваши логи будут отправляться только у определенных контейнеров. С блоком processors:add_docker_metadata все просто. В логи добавляем немножечко больше информации о ваших логах в контексте docker. Необязательно, но прикольно.



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



Тут я бы и сам спросил: а чего, как грепать-то? Что из себя представляет поиск по логам, что там можно сделать?

Да, из коробки в таком подходе, когда у нас текстовые логи, есть небольшой затык: мы можем по тексту задать запрос, например message:users. Это выведет нам все логи, у которых есть подстрока users. Можно пользоваться звездочками, большинством других юниксовых wild cards. Но кажется, этого недостаточно, хочется сделать сложнее, чтобы можно было нагрепать в Nginx раньше, как мы это умеем.



Давайте немного отступим от ElasticSearch и попытаемся сделать это не с ElasticSearch, а с другим подходом. Рассмотрим структурные логи. Это когда каждая ваша запись лога это не просто текстовая строчка, а сериализованный объект с атрибутами, которые любая ваша сторонняя система может сериализовать, чтобы получить уже готовый объект.

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

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

Меня это напрягало. Я хочу написать: Прилетел запрос. Дальше: Такой-то, такой-то, такой-то, очень просто, очень по-айтишному.



Давайте дальше. Договоримся: логировать будем в формате JSON, это простой формат. Сразу ElasticSearch поддерживается, filebeat, которым мы сериализуем и попробуем впилить. Это не очень сложно. Для начала вы добавляете из библиотеки pythonjsonlogger в блок formatters JSONFormatter файлика settings, где у нас хранится конфигурация. У вас в системе это может быть другое место. И дальше в атрибуте format вы передаете, какие атрибуты вы хотите добавлять в ваш объект.

Блок ниже это блок конфигурации, который добавляется в filebeat.yml. Здесь из коробки есть интерфейс у filebeat для парсинга JSON-логов. Очень круто. Это все. Для этогов вам больше ничего писать не придется. И теперь ваши логи похожи на объекты.



Что мы получили в ElasticSearch? В интерфейсе вы сразу видите, что ваш лог превратился в объект с отдельными атрибутами, по которым вы можете искать, создавать фильтрации и делать сложные запросы.



Давайте подведем итог. Теперь наши логи имеют структуру. По ним несложно грепать и можно писать интеллектуальные запросы. ElasticSearch знает об этой структуре, так как он распарсил все эти атрибуты. А в kibana это интерфейс для ElasticSearch можно фильтровать такие логи с помощью специализированного языка запросов, который предоставляет Elastic Stack.

И это проще, чем грепать. Греп имеет довольно сложный и крутой язык. Там очень много можно написать. В kibana можно сделать многие вещи проще. С этим разобрались.

Проблема 2. Тормоза


Следующая проблема тормоза. В микросервисной архитектуре всегда и везде бывают тормоза.



Тут немного контекста, расскажу вам историю. Ко мне прибегает менеджер, главное действующее лицо нашего проекта, и говорит: Эй-эй, кабинет тормозит! Даня, спаси, помоги!

Мы пока ничего не знаем, лезем в Elastic в наши логи. Но давайте я расскажу, что, собственно, произошло.



Эраст добавил фичу. В книгах мы теперь отображаем не айдишник автора, а его имя прямо в интерфейсе. Очень круто. Сделал он это вот таким кодом. Небольшой кусок кода, ничего сложного. Что может пойти не так?

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

Давайте расскажу. У меня был опыт: мы работали с Django, и у нас в проекте был реализован кастомный прекэш. Много лет все шло хорошо. В какой-то момент мы с Эрастом решили: давай пойдем в ногу со временем, обновим Django. Естественно, Django ничего не знает о нашем кастомном прекэше, и интерфейс поменялся. Прикэш отвалился, молча. На тестировании это не отловили. Та же самая проблема, просто ее сложнее было отловить.

Проблема в чем? Как я помогу вам решить проблему?



Давайте расскажу, что я делал до того, как начал решать именно проблему поиска тормозов.

Первое, что делаю, иду в ElasticSearch, у нас он уже есть, помогает, не нужно бегать по серверам. Я захожу в логи, ищу логи кабинета. Нахожу долгие запросы. Воспроизвожу на ноутбуке и вижу, что тормозит не кабинет. Тормозит Books.

Бегу в логи Books, нахожу проблемные запросы собственно, он у нас уже есть. Точно так же воспроизвожу Books на ноутбуке. Очень сложный код ничего не понимаю. Начинаю дебажить. Тайминги довольно сложно отлавливать. Почему? Внутри SQLAlchemy довольно сложно это определить. Пишу кастомные тайм-логгеры, локализую и исправляю проблему.



Мне было больно. Сложно, неприятно. Я плакал. Хочется, чтобы этот процесс поиска проблемы был быстрее и удобнее.

Формализуем наши проблемы. Сложно по логам искать, что тормозит, потому что наш лог это лог несвязанных событий. Приходится писать кастомные таймеры, которые показывают нам, сколько выполнялись блоки кода. Причем непонятно, как логировать тайминги внешних систем: например, ORM или библиотек requests. Надо наши таймеры внедрять внутрь либо каким-то Wrapper, но мы не узнаем, от чего оно тормозит внутри. Сложно.



Хорошее решение, которое я нашел, Jaeger. Это имплементация протокола opentracing, то есть давайте внедрим трассировку.

Что дает Jaeger? Это удобный интерфейс с поиском запросов. Вы можете отфильтровать долгие запросы или сделать это по тегам. Наглядное представление потока запросов, очень красивая картинка, чуть позже покажу.

Тайминги логируются из коробки. С ними ничего не надо делать. Если вам нужно про какой-то кастомный блок проверить, сколько он выполняется, вы можете его обернуть в таймеры, предоставляемые Jaeger. Очень удобно.



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



Проваливаемся в этот запрос, и видим большую портянку SQL-подзапросов. Мы прямо наглядно видим, как они исполнялись по времени, какой блок кода за что отвечал. Очень круто. Причем в контексте нашей проблемы это не весь лог. Там есть еще большая портянка на два-три слайда вниз. Мы довольно быстро локализовали проблему в Jaeger. В чем после решения проблемы нам может помочь контекст, который предоставляет Jaeger?



Jaeger логирует, например, SQL-запросы: вы можете посмотреть, какие запросы повторяются. Очень быстро и круто.



Проблему мы решили и сразу видим в Jaeger, что все хорошо. Мы проверяем по тому же запросу, что у нас теперь нет подзапросов. Почему? Предположим, мы проверим тот же запрос, узнаем тайминг посмотрим в Elastic, сколько запрос выполнялся. Тогда мы увидим время. Но это не гарантирует, что подзапросов не было. А здесь мы это видим, круто.



Давайте внедрим Jaeger. Кода нужно не очень много. Вы добавляете зависимости для opentracing, для Flask. Теперь о том, какой код мы делаем.

Первый блок кода это настройка клиента Jaeger.

Затем мы настраиваем интеграцию с Flask, Django или с любым другим фреймворком, на который есть интеграция.

install_all_patches самая последняя строчка кода и самая интересная. Мы патчим большинство внешних интеграция, взаимодействуя с MySQL, Postgres, библиотекой requests. Мы все это патчим и именно поэтому в интерфейсе Jaeger сразу видим все запросы с SQL и то, в какой из сервисов ходил наш искомый сервис. Очень круто. И вам не пришлось много писать. Мы просто написали install_all_patches. Магия!

Что мы получили? Теперь не нужно собирать события по логам. Как я сказал, логи это разрозненные события. В Jaeger это одно большое событие, структуру которого вы видите. Jaeger позволяет отловить узкие места в приложении. Вы просто делаете поиск по долгим запросам, и можете проанализировать, что идет не так.

Проблема 3. Ошибки


Последняя проблема ошибки. Да, я лукавлю. Я не помогу вам избавиться от ошибок в приложении, но я расскажу, что с ними можно делать дальше.



Контекст. Вы можете сказать: Даня, мы логируем ошибки, у нас есть алерты на пятисотки, мы настроили. Чего ты хочешь? Логировали, логируем и будем логировать и отлаживать.

По логам вы не знаете важность ошибки. Что такое важность? Вот у вас есть одна крутая ошибка, и ошибка подключения к базе. База просто флапнула. Хочется сразу видеть, что эта ошибка не так важна, и если нет времени, не обращать на нее внимания, а фиксить более важную.

Частота появления ошибок это контекст, который может нам помочь в ее отладке. Как отслеживать появление ошибок? Преподолжим, у нас месяц назад была ошибка, и вот она снова появилась. Хочется сразу найти решение и поправить ее или сопоставить ее появление с одним из релизов.



Вот наглядный пример. Когда я впиливал интеграцию с Jaeger, то немного поменял свою API. У меня изменился формат ответа приложения. Я получил вот такую ошибку. Но в ней непонятно, почему у меня нет ключа, lots в объекте order, и нет ничего, что мне бы помогло. Мол, смотри ошибку здесь, воспроизведи и самостоятельно отлови.



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



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



Посмотрим на нашем примере. Проваливаемся в ошибку KeyError. Сразу видим контекст ошибки, что было в объекте order, чего там не было. Я сразу по ошибке вижу, что мне приложение Delivery отдало новую структуру данных. Кабинет просто к этому не готов.



Что дает sentry, помимо того, что я перечислил? Формализуем.

Это хранилище ошибок, в котором можно искать их. Для этого есть удобные инструменты. Есть группировка ошибок по проектам, по похожести. Sentry дает интеграции с разными трекерами. То есть вы можете следить за вашими ошибками, работать с ними. Вы можете просто добавлять задачу в ваш контекст, и все. Это помогает в разработке.

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



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

Интегрировали трассировку. Теперь мы наглядно можем следить за потоком данных в нашем приложении.

И добавили удобный инструмент для работы с ошибками. Они в нашем приложении будут, как бы мы ни старались. Но мы будем фиксить их быстрее и качественнее.

Что еще можно добавить? Само приложение готово, оно есть по ссылке, вы можете посмотреть, как оно сделано. Там поднимаются все интеграции. Например, интеграции с Elastic или трассировки. Заходите, смотрите.

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

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

Проверяйте. Если на вас это не сказывается, круто. Вы получили только плюсы и не решаете проблемы с тормозами. Не забывайте об этом. Всем спасибо, что слушали.
Подробнее..

ALog плюс один логгер для С приложений

12.12.2020 18:17:24 | Автор: admin

Система логирования ALog первоначально разрабатывалась для использования в серверных приложениях. Первая реализация ALog была выполнена в 2013 году, на тот момент я и подумать не мог, что спустя семь лет буду писать про нее статью на Хабр. Но, видимо, на все воля случая Сейчас уже и не вспомню, что именно искал на просторах интернета, когда мне на глаза попалась статья Сравнение библиотек логирования. Я решил бегло просмотреть её в ознакомительных целях. По мере знакомства с материалом в голове возникла мысль: "А где же в этом 'табеле о рангах' находится мой логгер?". Чтобы это выяснить был создан небольшой проект LoggerTest для тестирования систем логирования.

Асинхронный логгер

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

  1. У логгера нет ограничения по памяти (обычно серверы не испытывают недостатка в ОЗУ) 2

  2. Стоимость вызова логгера в рабочих потоках должна быть минимальной. Данная характеристика важна для любой программы, но для серверной - особенно, где стоимость процессорных ресурсов может быть достаточно высока.

[1] ALog не является самостоятельной библиотекой, это всего лишь несколько модулей в составе библиотеки общего назначения.

[2] Использование ALog в ситуациях отличных от тестовых не приводит к существенному потреблению оперативной памяти, что позволяет использовать логгер на ARM-системах с небольшим объемом ОЗУ.

Участники тестирования

Первоначально для сравнения была выбран только Spdlog. Это было сделано по нескольким причинам:

  1. В исходной статье Spdlog показал неплохие результаты - фактически второе место;

  2. Логгер асинхронный (синхронные логгеры меня не интересовали в принципе, по причине их низкой производительности в многопоточных приложениях);

  3. Под впечатлением от статьи, P7 казался просто недосягаемым, поэтому вначале я его даже не рассматривал в качестве участника тестирования.

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

Для тестов было отобрано три логгера::

  1. G3log (версия 1.3.3, gitrev: f1eff42b)

  2. P7 (версия 5.5)

  3. Spdlog (версия 2.x, gitrev: f803e1c7)

G3log был добавлен просто для статистики. У этого логгера не очень высокая скорость сохранения данных на диск, что и было подтверждено тестами. Последним в сравнение был включен P7. Хочу сказать спасибо разработчикам P7, за помощь в написании тестового примера.

Формат тестовых сообщений

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

У Spdlog и G3log формат префикса можно менять, что позволяет сделать лог-сообщения похожими на сообщения ALog, и таким образом обеспечить примерно одинаковый объем записываемой информации.

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

Внешний вид префиксов для лог-сообщений:

  • ALog ----------------------------------------------------- 15.10.2020 19:39:23.981457 DEBUG2 LWP18876 [alog_test.cpp:35 LoggerTest]

  • Spdlog ---------------------------------------------------[2020-10-15 20:22:55.165] [trace] LWP19519 [spdlog_test.cpp:76 LoggerTest]

  • G3log -----------------------------------------------------2020/10/15 20:24:48 836329 DEBUG [g3log_test.cpp->thread_func:36]

Тестовый стенд

  • OS: Ubuntu 20.04

  • Compiler: GCC 8.4.0 C++14

  • CPU: Intel Core i7 2700K, 4 ядра, 8 потоков (4.5GHz, разгон)

  • RAM: 32Gb (DDR3-1600, XMP 8-8-8-24-2N)

  • SSD: Samsung 860 Pro 512Gb3

  • Количество итераций в тесте: 5

  • Количество записываемых строк: 5 000 000

[3] Любопытный момент: скорость сохранения логов на HDD диск (TOSHIBA HDWD120) оказалась выше чем на SSD.

Тестирование

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

Прямые характеристики:

  • Logging time - усредненное время, за которое все тестовые сообщения будут добавлены в систему логирования;

  • Flush time - усредненное время, за которое все тестовые сообщения будут сохранены на диск (это время отсчитывается от начала теста, поэтому включает в себя значение Logging time).

Косвенные характеристики:

  • Memory usage (max/average) - пиковое и среднее потребление памяти логгером в тесте (берутся худшие показатели из выполненных итераций);

  • CPU usage (max/average) - пиковое и среднее потребление ресурсов процессора. За 100% принимается полная загрузка одного ядра процессора (берутся худшие показатели из выполненных итераций).

Дополнительные условия:

  1. В P7 размер пула был установлен в 1 Гб (/P7.Pool=1048576). Для P7 это абсолютно сверх меры, но все участники на старте должны иметь более-менее одинаковые условия;

  2. Для Spdlog размер очереди установлен в 3 млн. сообщений. Уменьшение её размера будет сказываться на показателе Logging time. Логгер работает в режиме async_overflow_policy::block, что запрещает ему отбрасывать "старые" сообщения если очередь переполнена.

Тест 4 потока (режим сборки: release, ключ компилятора -O2)

ALog

G3log

P7

Spdlog

Logging time (sec)

1.325060

2.91048

4.27096

2.489934

Flush time (sec)

3.051857

23.1829

4.66385

2.489951

Logging per/sec

3788071

1720496

1170852

2008105

Flush per/sec

1638855

215697

1072226

2008092

Memory usage (max, MB)

1468

2343

86

1170

Memory usage (avg, MB)

1302

2310

85

1095

CPU usage (max, %)

106

87

57

100

CPU usage (avg, %)

39

18

37

67

По параметру Flush time ALog так и не смог опередить Spdlog. Поэтому можно сказать, что по Flush time Spdlog - лидер. Правда тут есть одна оговорка: это преимущество сохраняется до тех пор, пока размер очереди больше либо равен 3 * 10^6. "Ложка дегтя" заключается в том, что память под очередь сообщений выделяется в момент создания Spdlog, и остается занятой на всем протяжении работы логгера. В данном тесте Spdlog использовал 1170Мб. Остальные участники тестирования выделяют память по мере повышения нагрузки, и освобождают по мере понижения.

Тест 4 потока (режим сборки: debug, ключ компилятора -O0)

ALog

G3log

P7

Spdlog

Logging time (sec)

3.080949

5.59882

4.69356

7.591786

Flush time (sec)

4.717017

38.5406

5.05907

7.591814

Logging per/sec

1625193

893396

1065342

658611

Flush per/sec

1060223

129736

988428

658609

Memory usage (max, MB)

1241

1840

57

1170

Memory usage (avg, MB)

1071

1811

56

1130

CPU usage (max, %)

106

100

58

118

CPU usage (avg, %)

44

21

36

73

Тест в debug-режиме интересен с точки зрения падения производительности. Очевидно, что проседание есть, но оно не такое катастрофическое (десятки раз), как об этом говорится в Сравнение библиотек логирования. Возможно, причина в том, что тесты проводились на Linux.

Тест 1 поток (режим сборки: release, ключ компилятора -O2)

ALog

G3log

P7

Spdlog

Logging time (sec)

3.936475

8.43987

1.93741

3.090048

Flush time (sec)

4.029064

22.5557

2.32743

3.090063

Logging per/sec

1270377

596768

2580784

1618186

Flush per/sec

1241177

221687

2148340

1618178

Memory usage (max, MB)

84

1353

53

392

Memory usage (avg, MB)

55

1350

52

383

CPU usage (max, %)

50

64

21

64

CPU usage (avg, %)

25

21

11

44

В этом тесте P7 восстановил свой статус-кво. Запись в систему логирования 5 млн. сообщений в один поток за ~1.9 секунды выглядит просто недосягаемо! Скорость сохранения данных на диск тоже весьма впечатляет.

Здесь ALog уступает Spdlog в Logging time. Valgrind по этому поводу "говорит", что одна из причин низкой производительности кроется в использовании функции std::vsnprintf(). При сборке тестов под С++17 появляется возможность минимизировать использование std::vsnprintf(). При этом Spdlog все еще впереди, но отставание уже минимально (~0.15 сек). В то же время ALog улучшает другие показатели: максимальный объем занимаемой памяти уменьшился до 44 Мб, а пиковое потребление ресурсов процессора сократилось до 33%.

Для Spdlog размер очереди сообщений уменьшен до 1 млн. записей, что позволило сократить объем занимаемой памяти с 1170 Мб до 392 Мб. Дальнейшее уменьшение размера очереди ведет к ухудшению Logging time. Еще один штрих: сборка Spdlog под С++17 даёт незначительное (примерно на 0.1 секунды) увеличение Logging time, не затрагивая остальные результаты.

Промежуточный итог

Что ж, тщеславие удовлетворено. Пусть ALog не первый в этом тестировании, но тем не менее выглядит вполне достойно, а по параметру Logging time в многопоточном режиме работы - безусловный лидер.

Но что же дальше?! Эффективность работы системы логирования безусловно важна, но есть и другие критерии, которые целесообразно принимать во внимание. Например: возможность конфигурирования, фильтрация, удобство записи лог-сообщений в рабочем коде. Уже в процессе подготовки тестов пришла мысль, что наверное не следует ограничиваться только сравнением логгеров, и было бы неплохо рассказать об особенностях использования ALog. Это я и собираюсь сделать в следующих разделах.

Немного плагиата

Собственно, в чем плагиат: из статьи Сравнение библиотек логирования я решил позаимствовать авторские названия некоторых разделов, чтобы обеспечить ассоциативность восприятия материала.

Документация и зависимости

Документация: отсутствует, к сожалению. Что бы сгладить этот недостаток, был создан небольшой проект LoggerDemo с примерами использования логгера. Сейчас их шесть:

  • Demo 01: Вывод в лог "Hello world";

  • Demo 02: Вывод сообщений в файл;

  • Demo 03: Вывод сообщений из разных потоков;

  • Demo 04: Фильтрация лог-сообщений по имени модуля, с последующим сохранением в разные лог-файлы;

  • Demo 05: Конфигурирование лог-системы при помощи конфиг-файлов4;

  • Demo 06: Форматированный вывод через log_format (тестовое использование)

Для сборки потребуется QtCreator 4.12 или выше. Сборочная система QBS.

Примечание: сборка под ОС Windows не выполнялась, но проект тривиален, поэтому проблем возникнуть не должно.

Зависимости: ALog - не самостоятельное решение, он является составной частью библиотеки общего назначения SharedTools. Для такого подхода есть пара причин:

  • ALog достаточно маленький - всего три программных модуля5:

    1. logger - непосредственно сам логгер;

    2. config - модуль конфигурирования;

    3. format - модуль для форматированного вывода сообщений.

  • ALog зависит от некоторых модулей общего назначения из SharedTools. Поэтому я посчитал нецелесообразным выносить логгер в отдельную библиотеку, и делать ее зависимой от SharedTools.

[4] Для сборки примера Demo 5 требуется Qt-framework.

[5] Под программным модулем понимается пара cpp/h файлов, или один h-файл.

Тип логгера, контроль потребления памяти и потокобезопасность

  • Логгер асинхронный, порядок добавления сообщений можно считать атомарным (используется спин-блокировка), что не исключает возможности перемешивания сообщений. Однако, для диапазона точности, в котором работает логгер (микросекунды), явления перемешивания вряд ли могут быть обнаружены. За семь лет использования логгера я не сталкивался с подобными проявлениями.

  • Контроль потребления памяти отсутствует.

Обработка сбоев процесса

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

void prog_abort(){    log_error << "Program aborted";    alog::logger().flush();    alog::logger().waitingFlush();    alog::logger().stop();    abort();}

Стиль логирования и вывод (sink)

Вывод (sink): В ALog механизм вывода называется Saver (дословно: хранитель, в вольной трактовке: средство/механизм сохранения). В дальнейшем будет использоваться именно этот термин.

По умолчанию поддерживаются вывод в файл, и в stdout. В качестве демо-примера, сделан вывод в syslog. Правда, на момент его написания, syslog не мог похвастаться высокой скоростью работы, конечно с той поры может что и поменялось.

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

Споры о том, какой способ вывода лучше не закончатся наверное никогда. На мой взгляд, у потокового оператора есть одно важное преимущество: его можно переопределять, что позволяет системе логирования работать с пользовательскими типами данных. А возможность выводить лог-сообщения сразу через несколько saver-ов добавляет системе логирования ещё больше гибкости.

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

Используем структуру TaskLog для группировки необходимых параметров логирования.

struct TaskLog{    int taskId;    int userId;    string status;};

На следующем шаге реализуем потоковый оператор 6.

namespace alog {Line& operator<< (Line& line, const TaskLog& tl){    if (line.toLogger())        line << "TaskId: "   << tl.taskId             << "; UserId: " << tl.userId             << "; Status: " << tl.success;    return line;}} // namespace alog

Реализацию saver-a для записи сообщений в базу данных я оставлю за рамками этой статьи по причине существенного объема кода. Отмечу только, что saver нужно будет создать и зарегистрировать в системе логирования при старте программы. Далее в рабочем коде приложения вызываем логгер:

TaskLog tl {10, 20, "success"};log_info << "Task complete. " << tl;

В лог-файл будет добавлена строка: 15.10.2020 19:39:23 INFO LWP18876 [example.cpp:35] Task complete. TaskId: 10; UserId: 20; Status: success И одновременно с этим система логирования вставит в таблицу базы данных запись из трех полей: TASK_ID, USER_ID, STATUS. Конечно, реализация потокового оператора и saver-a для вывода информации в базу данных потребует определенных усилий, но это может быть оправдано удобством последующего использования.

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

{ //Block for alog::Line    alog::Line logLine = log_verbose << "Threshold ";    if (threshold > 0.5)        logLine << "exceeded";    else        logLine << "is normal";    logLine << " (current value: " << threshold << ")";}

Форматированный вывод реализован как надстройка над потоковыми операторами. Таким образом форматированный вывод будет работать со всеми типами данных для которых реализован оператор <<. Пример с выводом TaskLog будет выглядеть так:

TaskLog tl {10, 20, "success"};log_info << log_format("Task complete. %?", tl);

Возможно комбинирование потоковых операторов и форматированного вывода:

log_info << "Task complete." << log_format(" %?", tl);

[6] В примере приведена базовая (упрощенная) реализация потокового оператора.

Инициализация логгера

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

Для stdout программная инициализация состоит из одной строки:

alog::logger().addSaverStdOut(alog::Level::Info);

Инициализация для вывода в файл будет чуть сложнее:

const char* saverName = "default";const char* filePath = "/tmp/logger-demo.log";alog::Level logLevel = alog::Level::Debug;bool logContinue = true;{ //Block for SaverPtr    SaverPtr saver {new SaverFile(saverName, filePath, logLevel, logContinue)};    logger().addSaver(saver);}

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

Пример секции инициализации логгера:

### YAML syntax #### Конфигурирование системы логированияlogger:    # Уровень логирования. Допускаются следующие значения: error, warning, info,    # verbose, debug, debug2. По умолчанию используется info    level: verbose        # Определяет будет ли пересоздаваться log-файл при перезапуске программы.    # (флаг: true/false). Если параметр равен 'false', то log-файл будет    # пересоздаваться при каждом перезапуске программы, в противном случае    # логирование будет выполняться в существующий файл    continue: true        # Наименование файла логирования    file: /var/opt/application/log/application.log        # Наименование файла логирования в Windows    file_win: ProgramData/application/log/application.log        # Определяет файл конфигурирования сейверов и фильтров для системы логирования    conf: /etc/application/application.logger.conf        # Определяет файл конфигурирования сейверов и фильтров для системы логирования в Windows    conf_win: ProgramData/application/config/application.logger.conf        filters:        # Наименование фильтра      - name: default        type: module_name        mode: exclude            modules: [            VideoCap,            VideoJitter,        ]

В этой конфигурации будет создан saver по умолчанию7, с выводом в файл /var/opt/application/log/application.log. Параметры logger.conf и logger.filters отвечают за механизмы расширенной фильтрации, их назначение будет рассмотрено в разделе Настройка фильтрации.

В примере Demo 05 показано как создается saver по умолчанию с использованием файла конфигурации. А пример ниже демонстрирует, как можно переконфигурировать систему логирования в процессе работы программы:

// Время последней модификации конфиг-файла приложенияstd::time_t configBaseModify = 0;// Время последней модификации конфиг-файла для логераstd::time_t configLoggerModify = 0;void init(){    // Время модификации файлов на момент старта программы    configBaseModify = config::baseModifyTime();    configLoggerModify = config::loggerModifyTime();}// Таймер-функцияvoid configModifyTimer(){    bool modify = false;    std::time_t configModify = config::baseModifyTime();    if (configBaseModify != configModify)    {        modify = true;        configBaseModify = configModify;        config::base().rereadFile();        log_verbose << "Config file was reread: " << config::base().filePath();        alog::configDefaultSaver();    }    configModify = config::loggerModifyTime();    if (configLoggerModify != configModify)    {        modify = true;        configLoggerModify = configModify;        alog::configExtensionSavers();    }    if (modify)        alog::printSaversInfo();}

[7] Наименование saver-a по умолчанию всегда 'default'

Точность времени

Все сообщения в ALog фиксируются с точностью до 1 микросекунды, что покрывает потребности большинства существующих приложений. Но выводится это время только при уровне логирования Debug2 (режим трассировки). Такая точность временных меток необходима для режима подробной диагностики, для менее интенсивных режимов логирования вполне достаточно точности в 1 секунду. Данный подход несколько сокращает объем лог-файла. При разработке собственного saver-a разработчик может выводить микросекунды на любом уровне логирования.

Доступ к логгеру

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

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

Настройка фильтрации

ALog, как и большинство современных логгеров, поддерживает фильтрацию по уровням логирования. Имеются следующие варианты:

enum Level {None = 0, Error = 1, Warning = 2, Info = 3, Verbose = 4, Debug = 5, Debug2 = 6};

Debug2 - соответствует режиму трассировки, название имеет исторические причины.

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

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

  1. При использовании глобального логгера все программные модули будут выдавать сообщения с заданным уровнем логирования, в результате чего получается много лишней, избыточной информации;

  2. Использование нескольких экземпляров логгера в попытке уйти от проблемы из п.1 выглядит не очень удачной идеей. На мой взгляд, система логирования должна быть простой и единообразной в использовании из любой части программы. Программист не должен задумываться над тем, каким экземпляром логгера ему пользоваться;

  3. Объем файлов логирования может быть очень большим (см. п.1). Иногда объемы лог-файлов исчисляются десятками гигабайт, то есть существует риск отказа операционной системы из-за отсутствия свободного места на диске;

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

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

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

  1. Все модули, со штатными и трассировочными уровнями логирования пишут свои сообщения в один файл. Таким образом сохраняется последовательная хронология событий;

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

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

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

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

Ранее говорилось, что уровень логирования связан непосредственно с saver-ом, то есть весь процесс разделения сообщений происходит именно внутри механизма вывода. Управляет процессом разделения система фильтров, которая оставляет только те сообщения, которые удовлетворяют критериям фильтрации. Сейчас поддерживаются следующие критерии (типы) фильтрации:

Тип фильтрации

Описание

module_name

По именам логических модулей

log_level

По уровню логирования (это вариант одного файла с разными уровнями логирования)

func_name

По именам функций (механизм реализован, но фактически не востребован)

file_name:line

По именам файлов (читай: по именам программных модулей) и номерам строк

thread_id

По идентификаторам потоков

content

По контенту сообщения

Более подробно типы фильтрации описаны тут. Файл demo05.logger.conf содержит пример конфигурации логгера для Demo 05

Осталось рассмотреть вопрос с наименованием логических модулей. Можно использовать решение "в лоб", но выглядит оно не очень

alog::logger().debug(alog_line_location, "Module1") << "Message";

Как часто бывает в таких ситуациях - на помощь приходят макросы.

// Определяем макросы в начале модуля#define log_error_m   alog::logger().error   (alog_line_location, "Module1")#define log_warn_m    alog::logger().warn    (alog_line_location, "Module1")#define log_info_m    alog::logger().info    (alog_line_location, "Module1")#define log_verbose_m alog::logger().verbose (alog_line_location, "Module1")#define log_debug_m   alog::logger().debug   (alog_line_location, "Module1")#define log_debug2_m  alog::logger().debug2  (alog_line_location, "Module1")...void moduleFunc1(){    for (int i = 0; i < 10; ++i)    {        log_debug_m << "Func1. Message " << i;        usleep(10);    }}...// Удаляем макросы в конце модуля#undef log_error_m#undef log_warn_m#undef log_info_m#undef log_verbose_m#undef log_debug_m#undef log_debug2_m

В cpp-файлах #undef можно опустить, но не стоит так делать в заголовочных файлах. В этом случае результат работы лог-фильтров может оказаться неожиданным.

Поддержка юникода

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

Ротация файлов

ALog не поддерживает какие либо механизмы по ротации лог-файлов, так как с этой задачей прекрасно справляется logrotate.

Некоторые приемы использования

Здесь описаны некоторые приемы использования ALog, которые не попали в другие разделы. Делаю это на тот случай, если вдруг кому-то в голову придет мысль о применении ALog в своей программе.

Сложные трассировочные сообщения лучше использовать внутри условия:

if (alog::logger().level() == alog::Level::Debug2){    log_debug2_m << "Message was sent to socket"                 << ". Id: " << message->id()                 << ". Command: " << CommandNameLog(message->command())                 << ". Type: " << message->type()                 << ". ExecStatus: " << message->execStatus();}

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

log_debug2_m << "Message was sent to socket";

Наименование программного модуля получено из другой программы

std::string s = ".../python/example1.py";const char* file = alog::__file__cache(s);alog::logger().info(file, 0, 10, "Python") << "Message from python-script";

Не так давно в ALog была добавлена оптимизация позволяющая извлекать наименование программного модуля из макроса __FILE__ в процессе компиляции, и это реально ускорило работу логгера. Но у такой оптимизации есть обратная сторона: адреса строк содержащих наименования модулей не должны меняться на протяжении жизни программы. В принципе, с макросом __FILE__ все так и есть. Но иногда возникает желание (или необходимость) сделать сквозное логирование для внутренних и внешних (работающих в другом процессе) компонентов приложения. В данном случае наименования модулей получаемые из внешней программы имеют динамические адреса строк, а следовательно их нельзя передавать в ALog. Для решения этой проблему была создана функция alog::__file__cache(), она преобразует динамические строки в псевдостатические, что вполне устраивает логгер.

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

Пример подмены функции логирования logmvnc() для драйвера Intel Movidius Neural Compute Stick:

void logmvnc(enum mvLog_t level, const char* file, int line, const char* func,             const char* format, ...) asm ("logmvnc");void logmvnc(enum mvLog_t level, const char* file, int line, const char* func,             const char* format, ...){    va_list args;    va_start(args, format);    int len;    char buff[1024] = {0};    auto removeLastN = [&len, &buff]()    {        if ((len < int(sizeof(buff))) && (buff[len - 1] == '\n'))            buff[len - 1] = '\0';    };    switch (level)    {        case MVLOG_DEBUG:            if (alog::logger().level() == alog::Level::Debug2)            {                len = vsnprintf(buff, sizeof(buff) - 1, format, args);                removeLastN();                alog::logger().debug2(file, func, line, "Movidius") << buff;            }            break;        case MVLOG_WARN:            {                len = vsnprintf(buff, sizeof(buff) - 1, format, args);                removeLastN();                alog::logger().warn(file, func, line, "Movidius") << buff;            }            break;        case MVLOG_ERROR:        case MVLOG_FATAL:        case MVLOG_LAST:            {                len = vsnprintf(buff, sizeof(buff) - 1, format, args);                removeLastN();                alog::logger().error(file, func, line, "Movidius") << buff;            }            break;        default:            {                // LEVEL_INFO                len = vsnprintf(buff, sizeof(buff) - 1, format, args);                removeLastN();                alog::logger().info(file, func, line, "Movidius") << buff;            }    }    va_end(args);}

[8] Это возможно не для всех библиотек.

Вместо заключения

Когда я только начинал работу над этим материалом, то полагал, что ALog уже достаточно устоявшееся, отработанное решение. Но мои представления были неверными. Оказалось, что есть много аспектов, требующих переоценки или доработки. Какие-то правки вносились в процессе тестирования, а какие-то уже в процессе написания этой статьи. Благодаря всей этой затее были созданы два новых проекта: LoggerTest и LoggerDemo. Оба проекта открыты для развития, поэтому если у кого-то возникнут вопросы по работе логгера - постараюсь, по мере возможности, на них ответить. Может быть ответы воплотятся в новых примерах. Касательно LoggerTest - думаю, я далеко не единственный, кто пишет свой логгер, так что если есть желание помериться цифирьками - приходите.

Подробнее..

Логирование событий в Oracle PLSQL

31.03.2021 16:16:13 | Автор: admin
рис. Старый "дедовский" debug кодарис. Старый "дедовский" debug кода

Добрый день! Работая разработчиком Oracle PL/SQL, часто ли вам приходилось видеть в коде dbms_output.put_line в качестве средства debug-а? Стоит признать, что к сожалению, большинство (по моему личному мнению и опыту) разработчиков Oracle PL/SQL не уделяет должного внимания логированию как к спасательному кругу в случае возникновения ошибок. Более того, большая часть разработчиков не совсем понимает зачем нужно логировать информацию об ошибках и самое главное, не совсем понимают что делать и как использовать эту информацию в будущем.

Предисловие

Данным постом хотел бы начать цикл статей посвященных Логированию ошибок в Oracle PL/SQL. В первую очередь донести мысль до многих разработчиков, о том как можно построить функционал фиксации, хранения логов в БД. На своем опыте продемонстрировать поэтапный процесс создания полноценного логирования в БД. Рассказать как нам удалось создать логирование ошибок, разработать единую нумерацию событий для их дальнейшей идентификации, как поверх логирования натянуть мониторинг событий, создать функционал позволяющий увидеть все текущие ошибки в БД в виде таблиц (с указанием частоты возникновения ошибок и кол-ва и т.д.), графиков (отразить динамику роста кол-ва ошибок) и правильно распределить ресурсы для устранения тех или иных ошибок.

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

Введение

В этой и следующих статьях будет показано как реализованный функционал логирования позволяет фиксировать факт возникновения не только ошибок (сообщение с типом Error), но и сообщений с предупреждением (тип Warning), информативных сообщений (с типом Info) и т.д., поэтому, в рамках данных статей введём термин - Модель логирования событий (далее по тексту - "модель") или коротко Логирование событий, где под "событием" подразумевается некоторое ожидаемое действие, возникшее в ходе штатной/внештатной работы алгоритма.

Модель логирования позволяет реализовать:

  1. Единый подход в обработке и хранении событий

  2. Собственную нумерацию и идентификацию событий происходящих в БД

  3. Единый мониторинг событий

  4. Анализ событий происходящих в БД

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

Единый подход в обработке и хранении событий

Основной идеей "Единого подхода в обработке и хранении событий" заключается в создании одного одновременно простого и в тоже время очень сложного правила: "Все объекты базы данных (функции, процедуры) в обязательном порядке должны завершаться блоком обработки исключений с последующим логированием события". Простота заключается в том, что легко, в команде разработчиков, на словах договориться об исполнении данного правила. Сложность же заключается в том, что данное правило должно быть установлено на ранних этапах создания вашей БД и выполняться обязательно на протяжении всего жизненного цикла. Внедрить функционал логирования в уже существующие и действующие БД очень сложно (практически не возможно).

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

Наверное сейчас кто-то из читателей может возразить: "Зачем в обязательном порядке?". А всё очень просто, если вы разработчик PL/SQL и вы не согласны с этим правилом, то вот вам пример. Посмотрите на свой текущий проект более внимательно. Скорее всего вы найдете какое-нибудь логирование событий реализованное кем-то, когда-то. Вспомните сколько раз вы обращались к этому логированию при решении багов. Именно в таких ситуациях, когда есть срочность по времени в исправлении бага, вы или ваши коллеги начинают использовать dbms_output.put_line в качестве экспресс-дебага (быстрый способ получения значений переменных используемых в коде). Согласитесь, что для исправления бага мало знать в какой процедуре, в каком запросе и на какой строке возникла ошибка, необходимо знать параметры запроса на которых возникает ошибка. И вот тут нам на помощь приходит "Логирование событий", потому что помимо места возникновения ошибки мы узнаем параметры вызова процедуры, в которой возникает ошибка и это очень упрощает исправление бага.

Первая статья посвящена базовому функционалу Логирования событий. В простейшей реализации это одна общая таблица и пакет процедур для работы с ней. Для создания и демонстрации логирования, нам необходимо реализовать следующие объекты БД (весь список объектов с их исходными кодами представлен в Git):

  1. Таблица messagelog - единая таблица логов. Именно в данной таблице будет храниться информация о дате и времени события, об объекте где происходит событие, типе события с указанием кода, текста и параметров. В нашем примере, столбец backtrace вынесен в отдельную таблицу messagelog_backtrace для удобства.

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

    Структура таблицы

    Название столбца

    Тип данных

    Комментарий

    id

    number primary key

    Первичный ключ таблицы

    objname

    varchar2(60)

    Содержит наименование объекта из которого было инициировано событие. Чаще всего это комбинация имя_пакета.имя_процедуры

    msgtype

    varchar2(3)

    Тип события, например: ERR - сообщение от ошибке; INF - информационное сообщение; WRN - сообщение с предупреждением и т.д.

    insertdate

    date

    Дата и время создания записи о событии

    msgcode

    varchar2(10)

    Код ошибки, чаще всего это SQLCODE, либо код ошибки из справочника ошибок (об этом будет отдельная статья)

    msgtext

    varchar2(500)

    Текст ошибки, чаще всего это SQLERRM, либо текст ошибки из справочника ошибок

    paramvalue

    varchar2(500)

    Наименования параметров процедуры/функции и их значения. При анализе ошибок удобно видеть с каким параметрами была запущена та или иная процедура, выполнить повторный запуск этой процедуры с указанными параметрами и воспроизвести ошибку

    backtrace

    varchar2(4000)

    Записывается результат функции dbms_utility.format_error_backtrace, либо иная дополнительная информация

    Исходный код таблицы
    create table messagelog(id         number(38)      not null,                        msgtype    varchar2(3)     not null,                        objname    varchar2(60)    default null,                        insertdate date            default sysdate,                        msgcode    varchar2(10)    default null,                        msgtext    varchar2(4000)  default null,                        paramvalue varchar2(4000)  default null,                        constraint pk_messagelog_id primary key (id))partition by range (insertdate)  interval (numtoyminterval(3, 'MONTH'))    (partition p1 values less than (to_date('01.01.2020', 'DD.MM.YYYY'))); 
    

    *Исходный код других используемых объектов смотрите в Git

    В данной статье не будем подробно описывать способы индексирования и партицирования таблицы, всё зависит от целей и задач логирования. Самый предпочтительный вид хранения таблицы это партицирование по столбцу insertdate с интервалом 3 месяца.

    Отдельно стоит упомянуть про столбцы msgtext, paramvalue и backtrace. По своему опыту скажу, что указанные столбцы в 60% - 70% случаев будут пустыми и это очень сильно не нравится архитекторам и/или руководителям отделов разработки. Поэтому иногда есть смысл вынести указанные столбцы в отдельную таблицу с привязкой по id записи. В нашем примере столбец backtrace вынесен в отдельную таблицу messagelog_backtrace (см. исходный код в git).

  2. Пакет pkg_msglog - содержит набор процедур необходимых для логирования событий. В нашем примере будут представлены одна "корневая" и две процедуры для логирования событий: "ERR" - событие об ошибке; "WRN" - предупреждающее событие. Вызов корневой процедуры выполняется в автономной транзакции и делается это для того, чтобы была возможность выполнить сохранение данных об ошибке с откатом всех предыдущих изменений.

    Исходный код процедур пакета
    -- процедура логирования ошибокprocedure p_log_err(p_objname    in varchar2,                    p_msgcode    in varchar2,                    p_msgtext    in varchar2 default null,                    p_paramvalue in varchar2 default null,                    p_backtrace  in varchar2 default null)  is  begin    p_insert_log(p_msgtype_    => 'ERR',                 p_objname_    => p_objname,                 p_insertdate_ => sysdate,                 p_msgcode_    => p_msgcode,                 p_msgtext_    => p_msgtext,                 p_paramvalue_ => p_paramvalue,                 p_backtrace_  => p_backtrace);  end p_log_err;    -- корневая процедура логирования  procedure p_insert_log(p_msgtype_    in varchar2,                         p_objname_    in varchar2,                         p_insertdate_ in date,                         p_msgcode_    in varchar2,                         p_msgtext_    in varchar2 default null,                         p_paramvalue_ in varchar2 default null,                         p_backtrace_  in varchar2 default null)  is    v_id   messagelog.id%type;    pragma autonomous_transaction;  begin    insert into messagelog(msgtype,                           objname,                           insertdate,                           msgcode,                           msgtext,                           paramvalue,                           backtrace)        values(p_msgtype_,               p_objname_,               p_insertdate_,               p_msgcode_,               p_msgtext_,               p_paramvalue_,               p_backtrace_)    return id      into v_id;    if trim(p_backtrace_) is not null then      insert into messagelog_backtrace(id,                                       backtrace)      values(v_id,             trim(p_backtrace_));    end if;    commit;  end p_insert_log;
    

    *Исходный код других используемых объектов смотрите в Git

  3. Тестовый пакет pkg_clients - пакет с демонстрационными процедурами поиска и создания пользователей. Обратите внимание, что процедура p_insert_user выполняет функции аудита события - "создание нового пользователя".

    Исходный код процедур поиска и создания пользователей
    create or replace package body pkg_clients as  procedure p_insert_user(p_login_     in varchar2,                          p_firstname_ in varchar2,                          p_lastname_  in varchar2,                          p_id_        out number)  is    v_id clients.id%type;  begin    insert into clients(login,                        firstname,                        lastname)        values(upper(p_login_),               p_firstname_,               p_lastname_)    return id      into v_id;    if v_id > 0 then      -- аудит события - создание нового пользователя      pkg_msglog.p_log_wrn(p_objname    => 'pkg_clients.p_insert_user',                           p_msgcode    => '101',                           p_msgtext    => 'Создан новый пользователь с id = '||v_id,                           p_paramvalue => 'p_login = '||p_login_                                             ||', p_firstname = '||p_firstname_                                             ||', p_lastname = '||p_lastname_);    end if;    commit;  exception    when others then      pkg_msglog.p_log_err(p_objname    => 'pkg_clients.p_insert_user',                           p_msgcode    => SQLCODE,                           p_msgtext    => SQLERRM,                           p_paramvalue => 'p_login_ = '||p_login_                                             ||', p_firstname_ = '||p_firstname_                                             ||', p_lastname_ = '||p_lastname_,                           p_backtrace  => dbms_utility.format_error_backtrace);      rollback;  end p_insert_user;    procedure p_create_user(p_login     in varchar2,                          p_firstname in varchar2,                          p_lastname  in varchar2,                          p_id        out number)  is    v_id clients.id%type;  begin    begin      select id        into v_id        from clients       where login = upper(p_login);    exception      when no_data_found then        p_insert_user(p_login_     => p_login,                      p_firstname_ => p_firstname,                      p_lastname_  => p_lastname,                      p_id_        => v_id);    end;       p_id := v_id;  exception    when others then      pkg_msglog.p_log_err(p_objname    => 'pkg_clients.p_create_user',                           p_msgcode    => SQLCODE,                           p_msgtext    => SQLERRM,                           p_paramvalue => 'p_login = '||p_login                                             ||', p_firstname = '||p_firstname                                             ||', p_lastname = '||p_lastname,                           p_backtrace  => dbms_utility.format_error_backtrace);  end p_create_user;end pkg_clients;
    

    *Исходный код других используемых объектов смотрите в Git

Демонстрация логирования событий

Для демонстрации работы выполним три стандартных кейса с помощью созданного ранее пакета pkg_clients.

  1. (Ошибочная ситуация) Создание пользователя с длиной имени превышающей допустимое значение в таблице

    Пример 1
    рис. пример запуска процедурырис. пример запуска процедуры

    Результат

    рис. описание ошибкирис. описание ошибки
  2. (Ошибочная ситуация) Создание пользователя с пустым значением имени или фамилии (предположим, что есть такое требование)

    Пример 2
    рис. пример запуска процедурырис. пример запуска процедуры

    Результат

    рис. описание ошибкирис. описание ошибки
  3. (Успешная ситуация) Создание нового пользователя с аудитом события

    Пример 3
    рис. пример запуска процедурырис. пример запуска процедуры

    Результат

    рис. описание ошибкирис. описание ошибки

    Обратите внимание, что все демонстрационные процедуры завершаются блоком exception when others then с дальнейшим вызовом процедуры логирования события вне зависимости от "важности" процедуры.

Заключение

В заключении я повторюсь, что гораздо проще и правильнее добавлять логирование событий в обработку исключений при первом написании процедуры (функции) т.к. это не занимает много времени. Гораздо сложнее в будущем, внести правки в большое количество объектов БД и это займет гораздо больше времени на написание кода, тестирование и т.д.

По своему опыту отмечу, что пакет pkg_msglog (со всеми дополнительными объектами логирования) должен быть создан самым первым в проектируемой БД и в дальнейшем другие объекты создаются с логированием событий. Также, часто в командах появляются разработчики, которые говорят: "Зачем логировать все процедуры (функции)? Давайте вести логирование только важных и нужных процедур (функций)!". Вот здесь скрывается самая главная и распространенная ошибка. У всех нас разное понимание что есть "важная и нужная" процедура (функция). По сути, вы создаете логирование ошибок, которое "как бы и есть, но как бы его нет" т.е. все в команде знают, что в БД есть лог ошибок, но в нужный момент (например, при возникновении "блокера" на продакшн) вы обнаружите что в логах нет никакой информации об ошибке. Именно в такие моменты и возникает ошибочное мнение, что логировать события бесполезно и бессмысленно, а ведь это не так.

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

Спасибо за внимание.

Подробнее..

Перевод Многоразовый шаблон логирования на Python для всех ваших приложений в Data Science

14.05.2021 18:17:37 | Автор: admin

Идеальный способ отлаживать и отслеживать приложения хорошо определённые, информативные и удобно структурированные логи. Они являются необходимым компонентом любого малого, среднего или крупного проекта на любом языке программирования, не только на Python. Не используйте print() или корневой логгер по умолчанию, вместо этого настройте логирование на уровне проекта. К старту нового потока курса по Data Science, мы перевели статью, автор которой решил поделиться своим шаблоном для логирования. Не лишним будет сказать, что этот шаблон пришёлся по душе многим специалистам от дата-сайентистов профессионалов и до разработчиков ПО разного уровня.


Я стал работать с модулем логирования Python пару лет назад и с тех пор изучил бесчисленное количество руководств и статей в Интернете о том, как работать с ним эффективно, с наилучшей настройкой для своих проектов.

Все они хорошо объясняют, как настроить систему ведения лога для одного скрипта Python. Однако почти невозможно найти статью, которая объясняет, как настроить библиотеку ведения журнала Python для использования в масштабах всего приложения, а также то, как правильно интегрировать и удобно обмениваться информацией о ведении лога во всех модулях проекта.

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

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

Приступим к делу!

Создадим простой проект на Python

Объяснение новой концепции всегда следует проводить сначала в терминах проще, не отвлекаясь на справочную информацию. Учитывая это, давайте пока инициализируем простой проект.

Создайте папку под названием 'MyAwesomeProject'. Внутри неё создайте новый файл Python с именем app.py. Этот файл будет точкой старта приложения. Я буду использовать этот проект для создания простого рабочего примера шаблона, о котором говорю.

Откройте свой проект в VSCode (или в предпочитаемом редакторе). Теперь создадим новый модуль для настройки логирования на уровне приложения. Назовем его logger. С этой частью мы закончили.

Создаём логгер уровня приложения

Это основная часть нашего шаблона. Создадим новый файл logger.py. Определим корневой логгер и воспользуемся им для инициализации логгера уровня приложения. Настало время немного покодить. Несколько импортов и название нашего приложения:

qimport loggingimport sysAPP_LOGGER_NAME = 'MyAwesomeApp'

Функция, которую мы будем вызывать в нашем app.py:

def setup_applevel_logger(logger_name = APP_LOGGER_NAME, file_name=None):     logger = logging.getLogger(logger_name)    logger.setLevel(logging.DEBUG)    formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")    sh = logging.StreamHandler(sys.stdout)    sh.setFormatter(formatter)    logger.handlers.clear()    logger.addHandler(sh)    if file_name:        fh = logging.FileHandler(file_name)        fh.setFormatter(formatter)        logger.addHandler(fh)    return logger

Мы определим наш логгер с уровнем по умолчанию DEBUG и, чтобы структурировать сообщения логгера, воспользуемся Formatter. Затем присвоим его нашему обработчику, чтобы писать сообщения в консоль. Далее мы также обязательно включаем файл, в котором можем дополнительно хранить все наши сообщения лога. Это делается через логирование FileHandler. Наконец, мы возвращаем логгер.

Необходима ещё одна функция, которая гарантирует, что наши модули могут вызывать логгер, когда необходимо. Определите функцию get_logger.

def get_logger(module_name):       return logging.getLogger(APP_LOGGER_NAME).getChild(module_name)

Также, чтобы работать с модулем как с пакетом, по желанию мы можем создать папку logger и поместить в нее этот файл. Если мы сделаем это, нам также нужно будет включить в папку файл _init.py и написать такую строку:

from .logger import *

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

Устанавливаем логгер модульного уровня

Для лучшего понимания шаблона можно сделать простой модуль, чтобы протестировать логгер. Давайте определим простой module.py.

import loggerlog = logger.get_logger(__name__)def multiply(num1, num2): # just multiply two numbers    log.debug("Executing multiply function.")    return num1 * num2

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

Запустите сценарий и протестируйте логгер

Соорудим app.py.

import loggerlog = logger.setup_applevel_logger(file_name = 'app_debug.log')import mymodulelog.debug('Calling module function.')mymodule.multiply(5, 2)log.debug('Finished.')

Заметили, как мы импортируем модуль после инициализации логгера? Да, это обязательно. Теперь убедитесь, что ваш каталог содержит эти файлы:

Папка проектаПапка проекта

Наконец, запустите скрипт этой командой:

python3 app.py

Вы получите вывод вроде такого:

Структура каталогов также должна измениться: в ней должен появиться новый файл log. Проверим его содержимое.

Файл логовФайл логов

Заключение

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

А если вам интересна сфера Data Science и вы думаете поучиться по ссылке можете ознакомиться с программой курса и специализациями, которыми можно овладеть на нём.

Узнайте, как прокачаться и в других специальностях или освоить их с нуля:

Другие профессии и курсы
Подробнее..

Перевод Machine learning в анализе логов Netflix

21.09.2020 16:09:27 | Автор: admin

Представьте лог на 2,5 гигабайта после неудачной сборки. Это три миллиона строк. Вы ищете баг или регрессию, которая обнаруживается на миллионной строке. Вероятно, найти одну такую строку вручную просто невозможно. Один из вариантов diff между последней успешной и упавшей сборкой в надежде на то, что баг пишет в журналы необычные строки. Решение Netflix быстрее и точнее LogReduce под катом.

Netflix и строка в стоге лога


Стандартный md5 diff работает быстро, но выводит не меньше сотни тысяч строк-кандидатов на просмотр, поскольку показывает различия строк. Разновидность logreduce нечёткий diff с применением поиска k ближайших соседей находит около 40 000 кандидатов, но отнимает один час. Решение ниже находит 20 000 строк-кандидатов за 20 минут. Благодаря волшебству открытого ПО это всего около сотни строк кода на Python.

Решение комбинация векторных представлений слов, которые кодируют семантическую информацию слов и предложений, и хеша с учетом местоположения (LSH Local Sensitive Hash), который эффективно распределяет приблизительно близкие элементы в одни группы и далёкие элементы в другие группы. Комбинирование векторных представлений слов и LSH великолепная идея, появившаяся менее десяти лет назад.
Примечание: мы выполняли Tensorflow 2.2 на CPU и с немедленным выполнением для трансферного обучения и scikit-learn NearestNeighbor для k ближайших соседей. Существуют сложные приближенные реализации ближайших соседей, что были бы лучше для решения проблемы ближайших соседей на основе модели.

Векторное представление слов: что это и зачем?


Сборка мешка слов с k категориями (k-hot encoding, обобщение унитарного кодирования) типичная (и полезная) отправная точка дедупликации, поиска и проблем сходства неструктурированного и полуструктурированного текста. Такой тип кодирования мешка со словами выглядит как словарь с отдельными словами и их количеством. Пример с предложением log in error, check log.

{"log": 2, "in": 1, "error": 1, "check": 1}


Такое кодирование также представляется вектором, где индекс соответствует слову, а значение количеству слов. Ниже показывается фраза log in error, check log" в виде вектора, где первая запись зарезервирована для подсчета слов log, вторая для подсчета слов in и так далее:

[2, 1, 1, 1, 0, 0, 0, 0, 0, ...]

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

Посмотрим на словарь и векторные представления фразы problem authentificating. Слова, соответствующие первым пяти векторным записям, вообще не появляются в новом предложении.

{"problem": 1, "authenticating": 1}

Получается:

[0, 0, 0, 0, 1, 1, 0, 0, 0, ...]

Предложения problem authentificating и log in error, check log семантически похожи. То есть они по существу одно и то же, но лексически настолько различны, насколько это возможно. У них нет общих слов. В разрезе нечёткого diff мы могли бы сказать, что они слишком похожи, чтобы выделять их, но кодирование md5 и документ, обработанный k-hot с kNN этого не поддерживает.

Сокращение размерности использует линейную алгебру или искусственные нейронные сети для размещения семантически похожих слов, предложений или строк лога рядом друг с другом в новом векторном пространстве. Применяются векторные представления. В нашем примере log in error, check log может иметь пятимерный вектор для представления:

[0.1, 0.3, -0.5, -0.7, 0.2]

Фраза problem authentificating может быть

[0.1, 0.35, -0.5, -0.7, 0.2]


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

На самом деле вы заменили бы тысячи или более размерностей словаря всего лишь представлением в 100 размерностей, богатыми информацией (а не пятью). Современные подходы к снижению размерности включают разложение по сингулярным значениям матрицы совместной встречаемости слов (GloVe) и специализированные нейронные сети (word2vec, BERT, ELMo).

А как насчет кластеризации? Вернёмся к логу сборки


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

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

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

Вектор представления фразы log in error, check error может быть сопоставлен с двоичным числом 01. Затем 01 представляет кластер. Вектор problem authentificating с большой вероятностью также может быть отображен в 01. Так LSH обеспечивает нечёткое сравнение и решает обратную задачу нечёткое различие. Ранние приложения LSH были над многомерными векторными пространствами из набора слов. Мы не смогли придумать ни одной причины, по которой он не работал бы с пространствами векторного представления слов. Есть признаки, что другие думали так же.



Выше изображено использование LSH при размещении символов в той же группе, но в перевернутом виде.

Работа, которую мы сделали для применения LSH и векторных представлений в разрезе обнаружение текстовых выбросов в логах сборки, теперь позволяет инженеру просматривать небольшую часть строк лога, чтобы выявить и исправить ошибки, потенциально критичные для бизнеса. Также он позволяет достичь семантической кластеризации практически любой строки лога в режиме реального времени.

Теперь этот подход работает в каждой сборке Netflix. Семантическая часть позволяет группировать кажущиеся непохожими элементы на основе их значения и отображать эти элементы в отчетах о выбросах.

Несколько примеров


Любимый пример семантического diff. 6892 строк превратились в 3.



Другой пример: эта сборка записала 6044 строки, но в отчете осталась 171. Основная проблема всплыла почти сразу на строке 4036.



Конечно, быстрее проанализировать 171 строку, чем 6044. Но как вообще мы получили такие большие логи сборок? Некоторые из тысяч задач сборки это стресс-тесты для потребительской электроники выполняются в режиме трассировки. Трудно работать с таким объёмом данных без предварительной обработки.



Коэффициент сжатия: 91366/455 = 205,3.

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

Заключение


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

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

Если у вас есть какие-либо вопросы о возможностях в Netflix, обращайтесь к авторам в LinkedIn: Stanislav Kirdey, William High

А как вы решаете проблему поиска в логах?

image

Узнайте подробности, как получить востребованную профессию с нуля или Level Up по навыкам и зарплате, пройдя онлайн-курсы SkillFactory:



Подробнее..

Трассировка и логирование в микросервисах как мы втаскивали единый стандарт на 30 независимых команд

20.01.2021 12:12:03 | Автор: admin
Сервисы падали, падают и будут падать

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



Например, наша команда сопровождения маркетинга в Skyeng знала: пользователь с таким-то айдишником нажал в личном кабинете кнопку Сменить преподавателя постучался в наш сервис, дальше ушло три сообщения, в очереди было 2 вызова сервисов и эти сервисы ответили 200. Но на самом деле, что было у команд сопровождения учителей или биллинга, к которым мы стучались, не знал никто

Тогда мы придумали инструмент, чтобы маркировать трафик


В компании есть стандарт окружения: PHP 7.x, Symfony 4.x, RabbitMQ для системы сообщений, GuzzleHTTP для внешних и своих ip, Docker. Благодаря тому, что нам более-менее известно, что будет в каждой команде, мы написали библиотеку, которая помогла бы сделать трассировку запроса по всей системе с первого клика.

У нас два основных транспорта HTTP и RabbitMQ. Запрос приходит или от пользователя, или по cron.

  • Мы берем любой HTTP-запрос, а прежде чем прокинуть дальше, добавляем заголовок request-id от NGINX, у которого возможность генерации вшита прямо в модуле.
  • В случае с cron, сами генерируем request-id внутри нашего бандла.

Дальше прокидываем request-id всем следующим получателям в нашем облаке:

  • Автоматически добавляем в Guzzle-клиент для продолжения передачи request-id через HTTP при синхронных запросах к нашим сервисам.
  • Автоматически добавляем request-id ко всем AMQP-продюсерам для асинхронных запросов к нашим сервисам.
  • Также во всех местах, куда мы вклиниваемся для отправки, прикручен Jaeger opentracing это поможет построить всё дерево/карту запросов. Таким образом наш внутренний request-id в качестве атрибута уходит в трейсы opentracing.

Opentracing, маркировка через request-id и складывание в правильные места первые шаги к тому, чтобы в компании появились хорошие логи. Когда команды подключат нашу библиотеку, схема выше начнет работать: request-id начнет передаваться по HTTP и AMQP, каждый сервис будет знать, в рамках какого глобального запроса он сейчас работает. И мы увидим, как запросы расползаются по нашей инфраструктуре.

Логирование inspired by Java


Мы посчитали, что делая сложную композицию из нескольких классов, логер не нужно вставлять в конструктор. Он должен находиться сбоку и подключаться де-факто. В Java для этого, например, есть SLF4J аннотация.

В PHP нет таких аннотаций, поэтому многие прокидывают в конструктор логеры, как дополнительную зависимость. Нам не нравился такой подход, поэтому мы сделали trait, который служит почти как аннотация (т.к. мы используем Symfony, то trait опирается на функционал autowiring в результате получается практически как в Java).

Что мы логируем? Помимо request-id, это:

  • runtime-id и process-id идентификаторы текущего потока и процесса, внутри которых может обслуживаться несколько request-id,
  • user-id так как в приложениях есть авторизация, этот идентификатор должен быть везде,
  • имя канала канал можно использовать как бэкап для пользователя, если отвалилась база данных и что-то не записалось.
  • id контейнера, id сервиса, id задачи и прочее все возможные мета-данные о Docker-контейнере. Зачем? У нас был момент, когда приложение было раскатано на 3 контейнера, а взял и сглючил один конкретный, потому что он был развернут на глючном воркере.
  • Цепочки исключений на уровне базы, сервиса, всегда создаем исключения более высокого уровня, потому что часто именно в последнем исключении и кроется А что было-то?.
  • имя приложения в процессе оказалось, что многие команды ориентируются на имя репозитория, а приложение внутри не знает, как его зовут: пришлось дособирать эту информацию и дописывать upname в yaml-файл.
  • плюс служебные команды и переменные окружения.

Также есть вещи, которые можно записать заранее: запуск контроллера, запуск консьюмера. Можно заранее проставить уровень debug он не будет попадать никуда, но если нужно, сможете включить на тестинге или на проде, посмотреть, что у вас происходит вплоть до запросов в базу.

В чем хранить логи на проде и тестингах


На приложения у нас заводится New Relic, но мы дополнительно остановились на разделении записей между Sentry и Kibana.

В Sentry падает то, что требует немедленной реакции. Мы заходим в отдельный проект, видим, сколько событий конкретного типа ошибок. Так как в Sentry летит только то, что надо исправлять, тут нет оверхеда, пока у нас не появляется куча ошибок.



Наша панелька с небоскребами если они растут, значит, кто-то сегодня закончит работу попозже.

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



Самое главное не класть ошибки, которые вы не собираетесь править. Например, access denied сюда не летит.

Но в Sentry есть и свой минус он не очень хорошо справляется с большим потоком. Если вы, например, поделили на 0 и выкатили в прод, хранилище может лечь. Плюс надо помнить о том, что ошибка в одном сервисе может породить вал ошибок в другом сервисе. Решение этой проблемы выходит за тему статьи, но в целом смотрите в сторону паттерна circuit breaker.

Лог всего, что не ошибки, мы прокидываем как JSON в Kibana. Для этого сборщик Beats ходит, собирает файлы, наш модуль для Filebeat отправляет их в Elasticsearch, а дальше мы уже в Kibana все вычитываем. Правильнее было бы вычитывать логи с вывода контейнера и местами мы так и делаем.



Наша Kibana Выглядит она как Kibana хороший полнотекстовый поиск, свой язык запросов, всякие поля.

Хранится все: лог действий пользователя в контроллерах, начало и конец запроса в консьюмере, реббитовые запросы, работа бизнес-логики. А сбоку есть Grafana, по ней можно построить разные метрики.

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

Куда вас пошлют Пошаговый разбор, как внедрить у себя


Зато понимающие люди будут говорить: Он внедрил логи по всей компании

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

Чтобы логи стали единообразными, их идею надо продать. Вот какую многоходовочку соорудили мы.

Шаг 0. Начните с себя. Чтобы показать другим, как это классно, легко и красиво, вам нужно тестовое внедрение. Мы начали с собственной команды в маркетинге как раз было около 20 сервисов. Заодно поняли, какие вопросы и сложности скорее всего возникнут и начали готовиться к ним.

  • Например, сделали документацию с примерами и типовыми кейсами: как искать по логам, что делать, если нужен свой канал. Часть кейсов оказалось удобнее показать на уровне видео. Да, это значит, нужно делать презентацию, писать видео но в итоге оно того стоит.

Шаг 1. Вы идете к CTO и тимлидам. Вы думаете на языке PHP, они уже бизнесом, фичами, деньгами. Аргументируйте на их языке:

  • С библиотекой все будет быстрее подниматься, снизим шанс падения по той же причине, в бизнесе заложено, что что-то упало. Теперь заложим, с какой скоростью оно встает: performance, скорость восстановления это бизнес-метрики.
  • Будет проще новичкам, бизнесу зачастую проще нанять дополнительных разработчиков, чем перекидывать людей с других проектов. Но когда компания построена на микросервисах, часто получается, что единообразия в версиях ядра нет. Библиотека дает хотя бы частичное единообразие.

Шаг 2. Ок, зашли к начальству и возбудили его. Теперь приходим к командам. Их первая и естественная реакция тебя послать, потому что ты вроде как принес им дополнительной работы, а начальство теперь может спрашивать про логи.

  • Красота и удобство Sentry продают. Начните с них.
  • Минимизируйте телодвижения разработчиков. Именно поэтому мы выбрали уровень библиотеки. Если делать здоровые конфиги или правила, из-за которых придётся менять код, никто этого делать не будет. А здесь мы сами написали бандлы, которые в большинстве своем конфигурируют себя сами и сами встраиваются, куда им надо. Их можно встраивать без конфигурации вообще но если надо, их конфигурацию можно перекрыть.
  • Плюс удобный trait для добавления логера, документация с заранее объявленными каналами, готовыми примерами доработок и расширения конфигов. Так, чтобы не надо было городить что-то свое при внедрении.

Да, часть рабочего времени в ближайшие месяцы вы все равно будете просто ходить по командам, спрашивать А как оно там?, делать ревью внедрения и так далее. Но поверьте, оно того стоит.

p.s. Пост основан на опыте и рассказах Вани, Макса и Димы, которые вместе делали этот проект. Мы не придумали, от чьего аккаунта это публиковать, и залили от аккаунта компании. В комментариях ребята готовы ответить от своих аккаунтов.

p.p.s. В посте использованы цитаты из докладов Макса и Вани про этот проект.
Подробнее..

Мониторинг и логирование внешних сервисов в кластер Kubernetes

19.06.2020 18:10:12 | Автор: admin
image

Доброго всем.

Я не нашел в сети обобщенного руководства по логированию и сбору метрик со сторонних сервисов в системы развернутые в Kubernetes. Публикую свое решение. Данная статья подразумевает, что у вас уже имеется рабочий Prometheus и другие службы. В качестве примера источника данных внешнего stateful-сервиса будет использоваться СУБД PostgreSQL в контейнере Docker. В компании используется пакетный менеджер Helm, ниже по тексту будут примеры на нем. Для всего решения мы готовим собственный чарт, включающий в себя вложенные чарты всех используемых сервисов.

Логирование


Многие компании для сбора и просмотра и централизации логов используется стэк технологий Elasticsearch + Logstash + kibana, сокращённо ELK. В нашем случае нет необходимости индексировать контент и я применил более легковесный Loki. Он доступен в виде пакета Helm, мы добавили его как subchart изменив values для ingress и pv под нашу систему.

values.yaml
ingress:  enabled: true  annotations:     kubernetes.io/ingress.class: nginx  hosts:    - host: kube-loki.example.domain      paths:         - /  tls: []....persistence:  type: pvc  enabled: true  accessModes:    - ReadWriteOnce  size: 100Gi  finalizers:    - kubernetes.io/pvc-protection  existingClaim: "pv-loki"


Для отправки логов на инстанс Loki используем Loki Docker Logging Driver.

Необходимо установить это дополнение на все Docker хосты, с которых желаете получать логи. Есть несколько способов указать демону, как использовать дополнение. Я выбор драйвера произвожу в yaml Docker Compose, который является частью Ansible playbook.

postgres.yaml
    - name: Run containers      docker_compose:        project_name: main-postgres        definition:          version: '3.7'          services:            p:              image: "{{ postgres_version }}"              container_name: postgresql              restart: always              volumes:                - "{{ postgres_dir }}/data:/var/lib/postgresql/data"                - "{{ postgres_dir }}/postgres_init_scripts:/docker-entrypoint-initdb.d"              environment:                POSTGRES_PASSWORD: {{ postgres_pass }}                POSTGRES_USER: {{ postgres_user }}              ports:                - "{{ postgres_ip }}:{{ postgres_port }}:5432"              logging:                driver: "loki"                options:                  loki-url: "{{ loki_url }}"                  loki-batch-size: "{{ loki_batch_size }}"                  loki-retries: "{{ loki_retries }}"...

где loki_url: kube-loki.example.domain/loki/api/v1/push

Метрики


Собираються метрики с PostgreSQL с помощью postgres_exporter для Prometheus. Продолжение вышеуказанного файла Ansible playbook.

postgres.yaml
...            pexp:              image: "wrouesnel/postgres_exporter"              container_name: pexporter              restart: unless-stopped              environment:                DATA_SOURCE_NAME: "postgresql://{{ postgres_user }}:{{ postgres_pass }}@p:5432/postgres?sslmode=disable"              ports:                - "{{ postgres_ip }}:{{ postgres_exporter_port }}:9187"              logging:                driver: "json-file"                options:                  max-size: "5m"...


Для большей наглядности имен внешних stateful-сервисов пропишем через Endpoints.

postgres-service.yaml
apiVersion: v1kind: Endpointsmetadata:  name: postgres-exportersubsets:  - addresses:      - ip: {{ .Values.service.postgres.ip }}    ports:      - port: {{ .Values.service.postgres.port }}        protocol: TCP---apiVersion: v1kind: Servicemetadata:  name: postgres-exporter  labels:    chart:  "{{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}"spec:  ports:    - protocol: TCP      port: {{ .Values.service.postgres.port }}      targetPort: {{ .Values.service.postgres.port }}


Настройка Prometheus для получения данных postgres_exporter производится правкой values в subchart.

values.yaml
scrape_configs:...  - job_name: postgres-exporter    static_configs:      - targets:          - postgres-exporter.applicationnamespace.svc.cluster.local:9187        labels:          alias: postgres...


Для визуализации полученных данных установите соответствующий Dashboard в
Grafana и настройте источники данных. Это так же можно сделать через values в subchart Grafana.

Как это выглядит


Надеюсь, что эта краткая статья помогла вам понять основные идеи, заложенные в данном решении, и сохранит время при настройке мониторинга и логирования внешних сервисов для Loki/Prometheus в кластере Kubernetes.
Подробнее..

Перевод Как спокойно спать, когда у вас облачный сервис основные архитектурные советы

19.08.2020 18:13:30 | Автор: admin
LOST by sophiagworld

В этой статей собраны некоторые общие шаблоны, помогающие инженерам работать с масштабными сервисами, к которым делают запросы миллионы пользователей.

По опыту автора, это не исчерпывающий список, но действительно эффективные советы. Итак, начнем.

Переведено при поддержке Mail.ru Cloud Solutions.

Начальный уровень


Перечисленные ниже меры относительно просты в реализации, но дают высокую отдачу. Если вы раньше их не предпринимали, то будете удивлены значительными улучшениями.

Инфраструктура как код


Первая часть советов заключается в том, чтобы реализовать инфраструктуру как код. Это означает, что у вас должен быть программный способ развертывания всей инфраструктуры. Звучит замысловато, но на самом деле мы говорим о следующем коде:

Развертывание 100 виртуальных машин

  • с Ubuntu
  • 2 ГБ RAM на каждой
  • у них будет следующий код
  • с такими параметрами

Вы можете отслеживать изменения в инфраструктуре и быстро возвращаться к ним с помощью системы управления версиями.

Модернист во мне говорит, что можно использовать Kubernetes/Docker, чтобы сделать всё выше перечисленное, и он прав.

Кроме того, обеспечить автоматизацию, можно с помощью Chef, Puppet или Terraform.

Непрерывная интеграция и доставка


Для создания масштабируемого сервиса важно наличие конвейера сборки и теста для каждого пул-реквеста. Даже если тест самый простой, он, по крайней мере, гарантирует, что код, который вы деплоите, компилируется.

Каждый раз на этом этапе вы отвечаете на вопрос: будет ли моя сборка компилироваться и проходить тесты, валидна ли она? Это может показаться низкой планкой, но решает множество проблем.


Нет ничего прекраснее, чем видеть эти галочки

Для этой технологии можете оценить Github, CircleCI или Jenkins.

Балансировщики нагрузки


Итак, мы хотим запустить балансировщик нагрузки, чтобы перенаправлять трафик, и обеспечить равную нагрузку на всех узлах или работу сервиса в случае сбоя:


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

Обычно балансировщики нагрузки настраиваются в том облаке, которым вы пользуетесь.

RayID, сorrelation ID или UUID для запросов


Вам когда-нибудь встречалась ошибка в приложении с сообщением вроде такого: Что-то пошло не так. Сохраните этот id и отправьте его в нашу службу поддержки?


Уникальный идентификатор, correlation ID, RayID или любой из вариантов это уникальный идентификатор, который позволяет отслеживать запрос в течение его жизненного цикла. Это позволяет отследить весь путь запроса в логах.


Пользователь делает запрос к системе A, затем А связывается с B, та связывается с C, сохраняет в X и затем запрос возвращается в A

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

Средний уровень


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

Централизованное ведение журналов


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

Хотя это звучит как забавное приключение, однако, лучше убедиться, что у вас есть возможность поиска по всем журналам из одного места. Я решил задачу централизации журналов с помощью встроенной функциональности стека ELK: здесь поддерживается сбор журналов с возможностью поиска. Это действительно поможет решить проблему с поиском конкретного журнала. В качестве бонуса вы можете создавать диаграммы и тому подобные забавные штуки.


Функциональность стека ELK

Агенты мониторинга


Теперь, когда ваша служба введена в строй, нужно убедиться, что она работает без сбоев. Лучший способ сделать это запустить несколько агентов, которые работают параллельно и проверяют, что она работает и выполняются базовые операции.

На этом этапе вы проверяете, что запущенная сборка хорошо себя чувствует и нормально работает.

Для небольших и средних проектов я рекомендую Postman для мониторинга и документирования API. Но в целом просто следует убедиться, что у вас есть способ узнать, когда произошел сбой, и получить своевременное оповещение.

Автомасштабирование в зависимости от нагрузки


Это очень просто. Если у вас есть виртуальная машина, обслуживающая запросы, и она приближается к тому, что 80% памяти занято, то можно либо увеличить ее ресурсы, либо добавить в кластер больше виртуальных машин. Автоматическое выполнение этих операций отлично подходит для эластичного изменения мощности под нагрузкой. Но вы всегда должны быть осторожны в том, сколько денег тратите, и установить разумные лимиты.


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

Система экспериментов


Хорошим способом безопасно развернуть обновления станет возможность протестировать что-то для 1% пользователей в течение часа. Вы, конечно, видели такие механизмы в действии. Например, Facebook показывает части аудитории другой цвет или меняет размер шрифта, чтобы посмотреть, как пользователи воспринимают изменения. Это называют A/B-тестированием.

Даже выпуск новой функции можно запустить как эксперимент, а затем определить, как ее выпускать. Также вы получаете возможность вспоминать или изменять конфигурацию на лету с учетом функции, которая вызывает деградацию вашего сервиса.

Продвинутый уровень


Здесь советы, которые довольно сложно реализовать. Вероятно, вам потребуется немного больше ресурсов, поэтому небольшой или средней компании будет трудно с этим справиться.

Сине-зеленые развертывания


Это то, что я называю эрланговским способом развертывания. Erlang стали широко использовать, когда появились телефонные компании. Для маршрутизации телефонных звонков стали применять программные коммутаторы. Основная задача программного обеспечения этих коммутаторов заключалась в том, чтобы не сбрасывать вызовы во время обновления системы. У Erlang есть прекрасный способ загрузки нового модуля без падения предыдущего.

Этот шаг зависит от наличия балансировщика нагрузки. Представим, что у вас версия N вашего программного обеспечения, а затем вы хотите развернуть версию N+1.

Вы могли бы просто остановить службу и развернуть следующую версию в то время, которое считаете удобным для ваших пользователей, и получить некоторое время простоя. Но предположим, что у вас действительно строгие условия SLA. Так, SLA 99,99% означает, что вы можете уходить в офлайн только на 52 минуты в год.

Если вы действительно хотите достичь таких показателей, нужно два деплоя одновременно:

  • тот, который есть прямо сейчас (N);
  • следующая версия (N+1).

Вы указываете балансировщику нагрузки перенаправить процент трафика на новую версию (N+1), в то время как сами активно отслеживаете регрессии.


Здесь у нас есть зеленый деплой N, который нормально работает. Мы пытаемся перейти к следующей версии этого деплоя

Сначала мы посылаем действительно небольшой тест, чтобы посмотреть, работает ли наш деплой N+1 с небольшим количеством трафика:


Наконец, у нас есть набор автоматических проверок, которые мы в конечном итоге запускаем до тех пор, пока наше развертывание не будет завершено. Если вы очень-очень осторожны, также можете сохранить свое развертывание N навсегда для быстрого отката в случае плохой регрессии:


Если хотите перейти на еще более продвинутый уровень, пусть всё в сине-зеленом деплое выполняется автоматически.

Обнаружение аномалий и автоматическое смягчение последствий


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


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

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

Вот и всё!


Этот список приоритетов избавит вас от многих проблем, если вы поднимаете облачный сервис.

Автор оригинальной статьи приглашает читателей оставлять свои комментарии и вносить изменения. Статья распространяется как open source, пул-реквесты автор принимает на Github.

Что еще почитать по теме:

  1. Go и кэши CPU.
  2. Kubernetes в духе пиратства с шаблоном по внедрению.
  3. Наш канал Вокруг Kubernetes в Телеграме.
Подробнее..

Перевод Руководство по отладке бессерверных приложений

08.03.2021 12:14:02 | Автор: admin

Эволюция бессерверной архитектуры

Все началось в 1953 году, когда компания IBM выпустила свой первый коммерческий компьютер. И вот сегодня мы обсуждаем бессерверную архитектуру. За прошедшие годы вычислительная техника не только совершила настоящую революцию в том, как строится работа современных компаний, но и претерпела огромные преобразования сама по себе.

После ряда успешных (и не очень) проектов по развертыванию фреймворков на корпоративных инфраструктурах и в облаке, была сформулирована концепция фреймворка FaaS (Function as a Service). Его задача обеспечить запуск приложений в контейнерах без сохранения состояния. Это дает разработчикам возможность сконцентрироваться на самом коде, а не на управлении сложной инфраструктурой и связанными с ней ресурсами. Это привело к изобретению бессерверной архитектуры, ориентированной исключительно на исполнение двоичных файлов приложений, при этом все необходимые ресурсы управляются сторонним провайдером и принадлежат ему. По своей сути бессерверная архитектура позволила предприятиям не только сильнее сосредоточиться на разработке основных приложений, но и существенно снизить накладные расходы.

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

Что такое отладка в бессерверной среде?

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

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

Различия в отладке бессерверных и монолитных приложений

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

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

Кроме того, монолитные приложения поддерживают удаленную отладку. Вы можете подключить IDE к удаленным серверам, дав разработчикам привилегированный доступ. Несмотря на то, что это в принципе возможно, применять этот метод целесообразно только в тех случаях, когда локальное моделирование вряд ли даст результаты. Бессерверные приложения по природе своей не поддерживают удаленную отладку в силу отсутствия доступа на уровне ОС и сервера.

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

Типы отладочных фреймворков для бессерверных приложений

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

Таким образом, отладочные фреймворки для бессерверных приложений основываются на двух различных подходах с использованием AWS Lambda:

  1. Локальная отладка бессерверных приложений

  2. Отладка бессерверных приложений для облака

Локальная отладка бессерверных приложений

При разработке ПО важнейшая часть отладки происходит при локальном запуске. Модель бессерверных приложений (SAM) это решение, которое позволяет разработчикам запускать свои приложения локально на бессерверной платформе Amazon на базе AWS Lambda. В пакет решения входит два компонента: спецификация шаблона (SAM Template Specification) и интерфейс командной строки (SAM Command Line Interface). Спецификация шаблона позволяет определить приложение с помощью прав доступа, событий, API-интерфейсов и функций. При этом интерфейс командной строки позволяет вызывать функции локально, осуществлять пошаговую отладку функций, а также упаковывать и развертывать приложения в облаке. Здесь можно ознакомиться с полным руководством по использованию AWS SAM для локальной отладки на платформе AWS Lambda.

Отладка бессерверных приложений для облака

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

  • Первый этап тестирование API-шлюза. Вот некоторые из наиболее популярных инструментов для тестирования конечных точек API и шлюзов: SoapUI, Postman и Curl.

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

  • На следующем этапе изучается поведение приложения с помощью логов AWS CloudWatch. Функции AWS Lambda по умолчанию настроены на отправку логов в CloudWatch. Затем решение создает для каждой функции класс LogGroup, включающий все события, вызываемые этой функцией. Еще для отслеживания событий приложений можно использовать такие решения как Splunk и ELK.

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

Популярные средства отладки бессерверных приложений

Вы, конечно, можете отлаживать свое приложение с помощью встроенных средств, таких как CloudWatch и X-RAY, но все же это ручной и весьма утомительный процесс, требующий использования сразу нескольких решений. Однако существуют и самостоятельные инструменты для отладки бессерверных приложений. Они существенно упрощают процесс благодаря интегрированному интерфейсу и удобному процессу очистки всех используемых функций и сервисов. Вот некоторые из этих инструментов:

Faasly.io

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

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

Мониторинг без изменения кода Вы можете мгновенно оценить работоспособность своих распределенных систем в режиме реального времени, даже не прикасаясь к отлаживаемому коду.

AWS Cloudwatch

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

Инструменты SLS-Dev

Инструментарий Serverless Development Tools это открытый фреймворк с набором инструментов для разработчиков бессерверных платформ и приложений. Этот набор инструментов следует парадигме cloud-native, что способствует ускоренному развитию инноваций и их внедрению в промышленную эксплуатацию. Поскольку этот набор подразумевает оплату по мере использования, вы платите только за фактическое время работы и тестирования своего приложения на платформе. Притом что базовая версия включает почти весь критически важный функционал инструментария, расширенная версия под названием SLS-DevTools Guardian помогает оперативно обнаруживать проблемы по мере их возникновения и проводить отладку в режиме реального времени.

Lumigo

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

Dashbird

Если ваше приложение разработано на базе AWS Lambda, то Dashbird должен стать одним из ваших самых любимых инструментов отладки и оптимизации кода. Этот инструмент нацелен на обнаружение ряда распространенных сбоев в работе AWS Lambda, включая проблемы с памятью, тайм-ауты, ошибки времени выполнения, исключения и ошибки конфигурирования. Dashbird также оперативно предупредит вас о сбоях через Slack или по электронной почте и поможет сохранить непрерывную доступность сервиса. Помимо этого, можно интегрировать Dashbird с AWS X-RAY и получать информацию обо всех событиях и вызовах функций.

SignalFX

SignalFx это решение для облачного мониторинга, способное интегрироваться с основными поставщиками услуг, включая Google Cloud Functions, Azure Functions и AWS Lambda. Этот инструмент обеспечивает мониторинг производительности в режиме реального времени и непрерывную прозрачность для всех ваших бессерверных функций. Благодаря обширному списку функций, включающему метрики с малой задержкой, оптимизацию затрат, обнаружение холодного запуска и мониторинг времени выполнения, SignalFx неуклонно набирает популярность в сообществах разработчиков.

Заключительные мысли

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

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

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

Подробнее..

Перевод Стать инженером DevOps в 2021 году подробное руководство

21.01.2021 12:12:35 | Автор: admin

Что касается нынешнего ИТ-рынка, среда DevOps один из лучших вариантов для ИТ-специалистов с точки зрения заработной платы и карьерного роста. И мне довольно часто задают вопрос: Как стать инженером DevOps?

В этом блоге я попытаюсь ответить на него на примере своего собственного опыта работы DevOps в различных организациях.

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

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

DevOps (обособленное сочетание разработки и эксплуатации) это культура, движение или практика, которая подчеркивает сотрудничество и общение как разработчиков программного обеспечения, так и других специалистов в области информационных технологий (ИТ) при автоматизации процесса доставки программного обеспечения и изменений инфраструктуры.

Из этого определения ясно, что DevOps не зависит от каких-либо инструментов или технологий. Это философия, согласно которой разные ИТ-команды должны работать вместе для достижения лучших и быстрых результатов за счет постоянной обратной связи.

Вот интересный график тенденций, показывающий популярность DevOps за последние 5 лет.

Организациям, пытающимся внедрить у себя DevOps, требуются люди с навыками совместной работы, готовые изменять и внедрять новые технологии, с хорошим пониманием систем, средств автоматизации, инструментов CI/CD, систем контроля версий и сетей, с опытом использования инструментов управления проектами и т. д. Это необходимо для того, чтобы приложение было запущено на рынок без особых задержек.

Кроме того, проект или конвейер, разработанный командой, должен обеспечивать небольшие обновления или выпуски без серьёзного ручного вмешательства. Этого можно добиться только в том случае, если произойдет культурный сдвиг в работе команды.

Как стать инженером DevOps

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

В этой статье объясняется, как вам следует подготовиться к использованию инструментов и технологий для адаптации и работы в культуре DevOps.

В этой статье я рассмотрел многие темы. Новичок не может быть мастером всего. Однако наличие достаточного количества знаний в этих областях поможет вам продолжить карьеру в DevOps.

Поймите культуру DevOps

Прежде всего, необходимо понять культуру DevOps. Всё дело в объединении людей для эффективной работы над общей целью.

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

Люди перестанут указывать пальцем на различные проблемы, как только поймут, что в случае задержки или проблемы с реализацией проекта все его участники несут одинаковую ответственность.

Как только вы начнете практиковать культуру DevOps, вы перестанете говорить, что это синоним CI/CD и автоматизации.

Узнайте больше о системах *nix

Мы живем в эпоху, когда не можем жить без систем Linux/Unix. Вы должны лучше понять и получить практические знания о различных дистрибутивах Linux, широко используемых организациями (RHEL, Centos, Ubuntu, CoreOS и т.д.).

Согласно тематическому исследованию Linux Foundation, 90 % рабочей нагрузки в публичных облаках обрабатывается на Linux.

Вот еще одно интересное исследование Redhat, в котором показаны различные дистрибутивы Linux, используемые в публичных облаках:

Теперь у вас есть достаточно причин, по которым вам стоит сосредоточиться на Linux.

Когда дело доходит до Linux, это всё про терминал, графический интерфейс менее предпочтителен в мире *nix.

Для запуска Linux-серверов вы можете использовать VirtualBox или AWS/GCP/Azure и множество других облачных платформ.

Начать изучение можно со следующего:

  • Разберитесь в процессе загрузки Linux.

  • Установите и настройте веб-серверы (Apache, Nginx, Tomcat и т.д.). И узнайте, как работают веб-серверы.

  • Узнайте, как работают процессы Linux.

  • Узнайте, как работает SSH.

  • Почитайте о различных файловых системах.

  • Изучите ведение системного журналирования, мониторинга и устранения неполадок.

  • Узнайте о важных протоколах (SSL, TLS, TCP, UDP, FTP, SFTP, SCP, SSH).

  • Научитесь управлять сервисами и попробуйте создать сервис самостоятельно (Initd, Systemd).

  • Попробуйте разместить статические и динамические сайты на веб-серверах.

  • Настройте балансировщики нагрузки и реверс-прокси (Nginx, HAproxy и т.д.).

  • Сломайте что-нибудь и научитесь устранять неполадки.

Разберитесь, как работают компоненты инфраструктуры

Основным строительным блоком любой организации является её инфраструктура. Это может быть облачный или локальный центр обработки данных. Общее понимание компонентов инфраструктуры является обязательным для человека, который хочет практиковать или работать в среде DevOps. Было бы полезно, если бы вы имели базовое представление о содержимом этого списка:

  • Сети

    • Подсеть

    • Публичная сеть

    • Частная сеть

    • Обозначения CIDR

    • Статические/динамические IP-адреса

    • Firewall

    • Прокси

    • NAT

    • Внешний и локальный DNS

    • Диагностика сети

    • VPN

  • Хранилище

    • SAN

    • Бэкапы

    • NFS

  • Отказоустойчивость(HA)

    • Кластер

    • Механизмы отказоустойчивости

    • Аварийное восстановление

  • Безопасность

    • PKI Infrastructure

    • SSL-сертификаты

  • Технология единого входа(SSO)

    • Active Directory/LDAP

  • Балансировщики нагрузки

    • Балансировка на разных уровнях модели OSI (L4, L7)

    • Алгоритмы балансировки нагрузки

    • Reverse Proxy

Могло быть и больше пунктов, но я выделил только ключевые компоненты ИТ-инфраструктуры.

Научитесь автоматизировать

Автоматизация стала важным аспектом каждой организации. Мы больше не создаем серверы вручную. Мы это автоматизируем.

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

  • Для среды разработки

    • Vagrant

    • Docker Desktop

    • Minikube

    • Minishift

  • Для обслуживания инфраструктуры

    • Terraform

    • CLI (соответствующего облачного провайдера)

  • Для управления конфигурацией

    • Ansible

    • Chef

    • Puppet

    • Saltstack

  • Управление образами ВМ

    • Packer

Контейнеры, распределенные системы и Service Mesh

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

Как только вы изучите Docker, можете попробовать его инструменты кластеризации и оркестрации, такие как Kubernetes, Docker Swarm и т.д. Эти платформы лучше всего подходят для архитектуры на основе микросервисов.

Вот интересная тенденция использования Kubernetes по данным Datadog:

А вот пятилетняя тенденция роста поисковых запросов по Kubernetes:

Кроме того, многие инженеры проявляют интерес к изучению Kubernetes, и в 2021 году немало людей получат сертификаты по этой технологии (CA, CKD и CKD).

Service mesh это выделенный слой инфраструктуры с низкой задержкой для обеспечения взаимодействия между сервисами. Он даёт массу возможностей для межсервисного взаимодействия: балансировки нагрузки, шифрования трафика, авторизации, трассировки, обнаружения сервисов (service discovery) и использования паттерна автоматического выключения (circuit breaker), с которым можно ознакомиться тут. Service mesh это сложная тема, когда дело касается распределенных систем. Если вы новичок в работе с инструментами для контейнеров, вы можете изучить это после получения хороших знаний об архитектуре на основе микросервисов.

Журналирование и мониторинг

Журналирование и мониторинг очень важные аспекты инфраструктуры.

Большинство приложений, развернутых в инфраструктуре, будут создавать журналы. Основываясь на дизайне архитектуры, журналы будут передаваться и храниться в отдельном слое инфраструктуры.

Каждая компания будет иметь отдельный уровень инфраструктуры под журналирование. Обычно используются такие стеки, как Splunk, ELK и Graylog. Кроме того, существует несколько SaaS-компаний, таких как Loggly, которые предоставляют инфраструктуру для централизованного хранилища журналов.

Разработчики, системные администраторы и команда безопасности используют системные журналы для мониторинга, диагностики неполадок, аудита приложений и инфраструктуры.

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

Также на основании правил, настроенных в системах мониторинга, будут срабатывать оповещения. Например, оповещение может быть в виде уведомления в Slack или Telegram, задачи в Jira, простого email, SMS-сообщения или даже звонка на телефон. Все схемы оповещения могут разниться от компании к компании.

Как инженер DevOps, вы должны иметь доступ к журналам и уметь устранять неполадки во всех средах (Dev, QA, Stage, Prod). Понимание регулярных выражений очень важно для построения запросов в любом инструменте централизованного хранилища журналов.

Понимание лучших практик в сфере кибербезопасности (DevSecOps)

DevSecOps ещё одна область, связанная с интеграцией практик безопасности на каждом этапе DevOps. Википедия говорит:

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

Обзор инфобезопасности в 2020 показывает распределение разных кибератак по регионам:

В облачных средах криптомайнинг является распространенной атакой. В основном это происходит, когда секреты облачного доступа хранятся плохо, так что хакеры получают к ним доступ.

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

А вот основные стандартные практики DevSecOps, опубликованные Redhat:

Hashicorp Vault отличный инструмент для управления секретами. Существует множество рабочих процессов для управления секретами среды.

Изучайте программирование

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

Например, для написания конвейера Jenkins в декларативном виде (как код) требуется знания Groovy; кастомный модуль Ansible требует знания Python; для написания оператора Kubernetes требуется опыт работы с Go.

Обратите внимание на следующие языки программирования, которые часто используются для написания скриптов или небольших программ:

  • Bash/Shell

  • Python

  • Go

Go действительно становится популярным в сфере DevOps. Его используют многие инструменты. Например, Kubernetes и Terraform написаны на Go. JFrog исследовал внедрение Go во время GopherCon, и 18 % респондентов заявили, что используют этот язык для работы, связанной с DevOps.

Изучите Git, научитесь документировать, узнайте о GitOps

Очень важно применять систему контроля версий ко всему, что вы делаете (кроме паролей и секретов). Git лучший инструмент для этого. Для него доступно множество руководств, и изучение важных операций с Git не займет много времени.

Вы можете начать с Github или Bitbucket в качестве удаленного репозитория кода.

А как только вы поймете Git, изучите GitOps. Что этот такое?

GitOps способ реализации непрерывного развертывания облачных приложений. Он сосредоточен на ориентированном на разработчиков опыте работы с инфраструктурой, используя инструменты, с которыми разработчики уже знакомы, включая Git и инструменты непрерывного развертывания.

Ещё очень важно документировать всё, что вы делаете. Каждый репозиторий должен иметь файл README, лучше объясняющий ваш код. Хорошая документация поможет не только вам, но и тем, кто попытается использовать вашу кодовую базу.

Освойте непрерывный жизненный цикл доставки приложений

Когда дело доходит до жизненного цикла доставки приложений, вам необходимо знать три важных концепции.

  • Continuous Integration (Непрерывная интеграция)

  • Continuous Delivery (Непрерывная доставка)

  • Continuous Deployment (Непрерывное развертывание)

Научитесь использовать инструменты CI/CD, такие как Jenkins, Gitlab CI, Travis CI и т.д. Вот хорошее графическое представление процесса CI/CD:

DevOps vs SRE

SRE еще одна развивающаяся тема в сообществе DevOps. Это набор практик и философий, разработанных Google. Вот что компания говорит о DevOps и SRE:

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

Я рекомендую изучить официальные документы от Google:

  1. What is SRE?

  2. SRE vs. DevOps: competing standards or close friends?

Читать, читать и еще раз читать

Нет ничего лучше для приобретения знаний, чем чтение. Прочтите хотя бы один технический блог DevOps, связанный с инженерией. Следите за всеми значимыми инженерными блогами, такими как Netflix, Twitter, Google и т.д. Узнайте, как они используют правильный набор инструментов, стратегии развертывания и свои последние проекты с открытым исходным кодом.

Заключение

Инструменты и процессы, задействованные в DevOps, не ограничиваются тем, что упомянуто в этой статье. Однако это популярные инструменты и технологии с открытым исходным кодом, с которых вы можете начать, чтобы стать инженером DevOps.

А теперь мне интересно услышать от вас:

Что из культуры DevOps вы применяете на практике у себя?

Каков путь становления DevOps инженером вы прошли?

В любом случае, делитесь вашими мыслями об этой статье в комментариях.

Подробнее..

Из песочницы Spring Boot и Filebeat локально без регистрации и смс

22.10.2020 12:07:47 | Автор: admin

В данном руководстве мы рассмотрим подключение и настройку системы логирования в Spring Boot проекте и отправку логов в ELK с помощью Filebeat. Руководство предназначено для разработчиков начального уровня.


Логирование и зачем оно нужно


Когда я только начинал работать программистом один мой старший коллега любил повторять: "Если у тебя нет логов, то у тебя нет ничего". Действительно, столкнувшись с первым же багом на тестовых стендах или хуже того в промышленной среде, первое что нам будет нужно это логи приложения и удобный доступ к ним. За сами логи отвечают разработчики приложения, которые должны обеспечить логирование поведения системы таким образом, чтобы в любой момент можно было понять что происходит с системой и главное что с ней не так.


Следующий вопрос, это удобство доступа к логам. Обычно при локальном тестировании мы видим лог в консоли приложения, а на тестовым стенде в специальных лог файлах на сервере. Удобно ли и безопасно ли каждый раз подключаться к стенду, искать нужную директорию и читать файлы логов оттуда? Практика показывает что нет и это вторая проблема которую призван решить ряд продуктов, обеспечивающих удобный доступ к логам и поиск в них важной информации. Сегодня мы очень кратко поговорим о одной из групп таких продуктов, так называемом стеке ELK (Elasticsearch Logstash Kibana) и более подробно о Filebeat Open source продукте, обеспечивающем удобный механизм доставки логов до ELK.


Три строчки о ELK


  • Logstash получение, модификация логов
  • Elasticsearch хранение и поиск
  • Kibana отображение

Причем здесь Filebeat?


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


Хорошим подходом будет развертывание общего ELK для группы микросервисов, а для каждого микросервиса разворачивать свой Filebeat (тем более что это проще некуда), который будет читать логи микросервиса и отдавать в общий ELK.


Именно такое решение мы попробуем реализовать позже в локальной среде.



Практика


Java 8
ApacheMaven3.6
Spring Boot 2.3.4.RELEASE
Docker


Spring Boot App


Начнем с создания простого Spring Boot приложения, единственной задачей которого будет генерировать логи по запросу. Полный код приложения можно посмотреть/скачать здесь либо повторить по шагам


Создадим проект Spring Boot самостоятельно или используя Spring Initalizr


Дополнительно нам понадобятся зависимости


<dependency>    <groupId>org.springframework.boot</groupId>    <artifactId>spring-boot-starter-web</artifactId></dependency><dependency>    <groupId>net.logstash.logback</groupId>    <artifactId>logstash-logback-encoder</artifactId>    <version>6.4</version></dependency><dependency>    <groupId>org.projectlombok</groupId>    <artifactId>lombok</artifactId></dependency>

  • spring-boot-starter-web т.к. мы будем обращаться к приложению по сети
  • logstash-logback-encoder позволит сформировать записи в лог файле в правильном формате
  • lombok просто для удобства, чтобы писать меньше кода

Здесь полный pom.xml


Главный класс нашего приложения как и в любом простейшем Spring Boot проекте:


@SpringBootApplicationpublic class Application {    public static void main(String[] args) {        SpringApplication.run(Application.class, args);    }}

Класс генератор который будет создавать логи:


@Slf4j@Servicepublic class LogGenerator {    public void generate(int count) {        log.info("Start generating logs");        LongStream.range(0, count)                .forEach(i -> log.info("Log {}", i));    }}

Здесь мы просто создаем от 0 до count записей в логе


Контроллер, который мы будем вызывать для обращения к генератору:


@Slf4j@RestController@RequiredArgsConstructorpublic class LogController {    private final LogGenerator generator;    @GetMapping("/generate")    public ResponseEntity test(@RequestParam(name = "count", defaultValue = "0") Integer count) {        log.info("Test request received with count: {}", count);        generator.generate(count);        return ResponseEntity.ok("Success!");    }}

Контроллер обрабатывает GET запросы вида:
http://localhost:8080/generate?count=10
и запускает генератор передавая ему количество записей


Теперь настроим систему логирования. Для этого добавим в resources файл logback-spring.xml


<?xml version="1.0" encoding="UTF-8"?><configuration>    <appender name="consoleAppender" class="ch.qos.logback.core.ConsoleAppender">        <encoder>            <pattern>%d [%thread] %-5level  %logger{35} - [%mdc] - %msg%n</pattern>        </encoder>    </appender>    <appender name="filebeatAppender" class="ch.qos.logback.core.rolling.RollingFileAppender">        <file>./log/application.log</file>        <append>true</append>        <encoder class="net.logstash.logback.encoder.LogstashEncoder"/>        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">            <fileNamePattern>./log/application.%d.%i.log.gz</fileNamePattern>            <maxFileSize>10MB</maxFileSize>        </rollingPolicy>    </appender>    <root level="INFO">        <appender-ref ref="consoleAppender" />        <appender-ref ref="filebeatAppender" />    </root></configuration>

Здесь мы обьявили два аппендера:


  • consoleAppender пишет записи согласно указанному нами паттерну в привычную нам консоль
  • filebeatAppender пишет записи в файл, причем в качестве енкодера используется LogstashEncoder из той самой библиотеки logstash-logback-encoder
    Назначения данного енкодера кодировать файлы логов в JSON формат, который легко будет понимать Logstash. В противном случае нужно будет конфигурировать сам Logstash что снижает универсальность нашего решения и просто добавит лишней работы по настройке.

Как видно из кода, файл лога будет писаться согласно пути ./log/application.log что соответствует поддиректории log в папке с проектом. Разумеется можно указать любой другой путь. Также при желании можно скорректировать максимальный размер файла maxFileSize


На самом деле на этом можно было бы закончить и перейти к установки и настройке Filebeat но мы сделаем еще кое что.


Добавим в наш проект класс фильтр для запросов:


@Slf4j@Componentpublic class LogFilter extends OncePerRequestFilter {    private static final String REQUEST_ID = "requestId";    @Override    protected void doFilterInternal(HttpServletRequest request,                                    HttpServletResponse response,                                    FilterChain filterChain) throws ServletException, IOException {        String requestId = request.getHeader(REQUEST_ID);        if (requestId == null) {            requestId = UUID.randomUUID().toString();        }        MDC.put(REQUEST_ID, requestId);        try {            log.info("Started process request with {} : {}", REQUEST_ID, requestId);            filterChain.doFilter(request, response);        } finally {            MDC.clear();        }    }}

Назначения этого класса, перехватывать все запросы к контроллеру и генерировать уникальный идентификатор запроса (если таковой не был отправлен в виде хедера requestId), после чего помещает его в MDC (Mapped Diagnostic Context)


MDC.put(REQUEST_ID, requestId);

А finally блоке MDC будет очищаться


MDC.clear();

Ценность такого подхода в том, что теперь все логи записанные в рамках конкретного запроса объединены единым уникальным идентификатором, что может облегчить анализ логов при разборе инцидентов. Дальше мы увидим как просто в интерфейсе Kibana можно будет отфильтровать только логи для каждого конкретного запроса.


После того как ваше приложение будет закончено, запустите его удобным для вас способом, например:


mvn spring-boot:run

После того как приложение стартует, можно вызвать его и убедиться что логи пишутся в файла application.log


curl "localhost:8080/generate?count=10"

Сервис должен отвечать Success!, а в application.log появлятся записи вида:


{   "@timestamp":"2020-10-17T22:39:45.595+03:00",   "@version":"1",   "message":"Writing [\"Success!\"]",   "logger_name":"org.springframework.web.servlet.mvc.method.annotation.HttpEntityMethodProcessor",   "thread_name":"http-nio-8080-exec-3",   "level":"INFO",   "level_value":10000,   "requestId":"77abe5ac-4458-4dc3-9f4e-a7320979e3ae"}

Filebeat


Скачать можно по ссылке Filebeat
В рамках данной статья тестирование проводилось на версии 7.9.2 на macOS
Далее следует распаковать дистрибутив в необходимую директорию.
Для настройки Filebeat вам понадобится всего лишь скорректировать содержимое файла filebeat.xml


Конфигурация уже почти готова, нам нужно только скорректировать блоки inputs и output:


  inputs:  - enabled: true    encoding: utf-8    exclude_files: ['\.gz$']    json:      add_error_key: true      keys_under_root: true      overwrite_keys: true    paths:    - {ваш путь до папки с логами}/*.log    scan_frequency: 10s    type: log

Здесь самое главное, мы указываем Filebeat где лежат файлы логов и как их следует интерпретировать. Для этого мы добавим следующие параметры:


  • keys_under_root наши логи в формате json будут встраивались в существующий json, который Filebeat будет отправлять в Logstash
  • overwrite_keys позволит перезаписывать ключи полей и корректно разрешать конфликты
  • add_error_key Filebeat будет добавлять поля error.message и error.type: json в случае если сформированный нами json оказался некорректным.

output:  logstash:    hosts:    - localhost:5044    ssl:      certificate_authorities:      - {путь до папки с сертификатами}/logstash-beats.crt

Этот блок отвечает за то, куда Filebeat будет отправлять логи. В данном случае указан дефолтный порт с которым будет запущен Logstash (если вы умышленно не будете запускать его на другом порту)
блок ssl.certificate_authorities не обязательный и понадобится в случае если вы будете использовать Logstash защищенный сертификатами (именно такой будем рассматривать мы), в противном случае он вам не нужен.


Этих настроек достаточно для локальной работы Filebeat, за тем лишь исключением что пока ему некуда отдавать данные, т.к. ELK еще не запущен.
Вопрос полного развертывания и конфигурирования всех компонентов ELK выходит за рамки данной статьи. Для упрощения, предлагается воспользоваться готовым docker образом ELK sebp/elk и также предлагается воспользоваться logstash-beats.crt. Его следует скачать и прописать как certificate_authorities в filebeat.xml


Автор запускает посредством docker-compose с пробросом портов:


version: '3.7'services:  elk:    image: sebp/elk    ports:      - "5601:5601" #kibana      - "9200:9200" #elastic      - "5044:5044" #logstash

После запуска ELK можно запустить и Filebeat соответствующей командой в зависимости от вашей операционной систему, например для macOS это:


./filebeat -e run

Что происходит? Наше приложение по запросу генерирует логи, они с помощью LogstashEncoder преобразуются к виду JSON и записываются в файл application.log, а Filebeat в свою очереди регулярно забирает новые записи, преобразует их в соответствии со своей конфигурацией и отправляет в Logstash. Далее с этими данными мы можем работать в интерфейсе Kibana.


Зайдем в интерфейс Kibana по адресу:
http://localhost:5601/


Далее перейдем на вкладку Discover:



Нужно будет создать индекс паттерн:



Затем Kibana покажет вам index от которого ELK уже получил данные. Важно! Если ваше приложение еще ничего не залогировало или Filebeat не успел ничего отправить, то и индекса не появится на этой вкладки. Потому прежде сделайте несколько вызовов нашего сервиса:


curl "localhost:8080/generate?count=100"

Немного подождем и можно будет определить нужный паттерн:



Выберем поля для дефолтной сортировки:



И увидим поля для каждой лог записи нашего приложения. Можно увидеть тот самый requestId который был нами добавлен в MDC ранее:



Теперь во вкладке Discover для нашего индекса можно настроить отображения полей и увидеть, что все логи в рамках одного запроса объединены одним и тем же requestId. Можно развернуть поле JSON и посмотреть полный текст сообщения полученного из Filebeat:


Подробнее..
Категории: Java , Логирование , Spring boot , Filebeat

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru