Логируй больше! 1С + UPD + GELF + Graylog




Наличие производительной платформы управления логами — это жизненно важный элемент в ландшафте систем с высокой активностью. Рассмотрим один из подходов логирования активности систем на платформе 1С в Graylog.

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

Вопрос управления логами становится все более острым и для систем на платформе 1С:Предприятие , т.к. области их применения, объемы обрабатываемых данных и нагрузки растут. Встроенная в платформу 1C подсистема логирования не всегда удовлетворяет критериям скорости, архитектуры и удобства использования, а так же требует использования сторонних средств для более глубокого анализа и визуализации.

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

Исходим из того, что:

— в нашей инфраструктуре уже развернут Graylog-кластер (примеры статья для версии 2.4, но на текущий момент уже доступна версия Graylog 3.0);

— у нас есть доступ к Graylog правами администратора;

— для передачи по UDP используем замечательную ВК от Сергея Serginio Смирнова;

Способов интеграции с Graylog достаточно много. Нас интересует предоставляемое API, протокол передачи данных UDP и формат GELF (Graylog Extended Log Format). Почему UDP, а не HTTP, думаю, понятно, хотя возможность передачи GELF-сообщений по HTTP так же есть.
Создадим новый GELF UDP Input: в консоли Graylog System -> Inputs -> Select input: GELF UDP -> Launch new input

SystemInputs

 

LaunchNewGELFUDPInput

 

Input запущен и готов к работе:

InputCreated

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

В документации Graylog формат GELF и его ограничения хорошо описаны. Ограничимся тем, что GELF-сообщение — это данные в JSON-формате, содержащие набор обязательных, не обязательных и произвольных полей.
Предположим, что мы логируем входящий/исходящий http-трафик нашей системы. Тогда пример структуры GELF-сообщения может иметь вид:

//    GELF-сообщение
//
Функция ПолучитьСтруктуруЗаписиЛога() Экспорт

СтруктураЗаписиЛога = Новый Структура;

// обязательные поля
СтруктураЗаписиЛога.Вставить("version", "1.1");
СтруктураЗаписиЛога.Вставить("host", ИмяКомпьютера());
СтруктураЗаписиЛога.Вставить("short_message", "");
СтруктураЗаписиЛога.Вставить("full_message", "");
СтруктураЗаписиЛога.Вставить("level", 6);

// произвольные поля
СтруктураЗаписиЛога.Вставить("_systemName", ИмяТекущейБазы());        // имя текущей системы
СтруктураЗаписиЛога.Вставить("_baseUrl", "");                        // базовый URL запроса
СтруктураЗаписиЛога.Вставить("_relativeUrl", "");                    // относительный URL запроса
СтруктураЗаписиЛога.Вставить("_requestHeaders", "");                // заголовки запроса
СтруктураЗаписиЛога.Вставить("_requestBody", "");                    // тело запроса
СтруктураЗаписиЛога.Вставить("_responseResult", "");                // результат ответа (технологическое поле)
СтруктураЗаписиЛога.Вставить("_responseStatusCode", "");            // код состояния http
СтруктураЗаписиЛога.Вставить("_responseHeaders", "");                // заголовки ответа
СтруктураЗаписиЛога.Вставить("_responseBody", "");                    // тело ответа
СтруктураЗаписиЛога.Вставить("_duration", "");                        // длительность обработки http-вызова
СтруктураЗаписиЛога.Вставить("_logType", "");                        // тип лога: innerRequest-входящий вызов; outerRequest-исходящий вызов
СтруктураЗаписиЛога.Вставить("_messageType", "");                    // произвольный тип сообщения
СтруктураЗаписиЛога.Вставить("_objectUid", "");                        // UID объекта базы, с которым может быть связан текущий вызов
СтруктураЗаписиЛога.Вставить("_objectType", "");                    // тип объекта базы, с которым может быть связан текущий вызов

Возврат СтруктураЗаписиЛога;

КонецФункции

 

Сформируем новое сообщение лога:

//
//
Функция ПолучитьЗаписьЛогаHTTPЗапроса(ТипЗаписиЛога, БазовыйURL = "", ОтносительныйURL = "", ЗапросЗаголовки = "", ЗапросТело = "", ОтветЗаголовки = "", ОтветТело = "", КодСостояния = 200, Результат = 1, Объект = "", Длительность = 0, УровеньСообщения = 6) Экспорт

СтруктураЗаписиЛога = ПолучитьСтруктуруЗаписиЛога();

ОбъектУникальныйИдентификатор = ?(ЗначениеЗаполнено(Объект), СокрЛП(Объект.УникальныйИдентификатор()), "");
ОбъектТип = СокрЛП(ТипЗнч(Объект));

СтруктураЗаписиЛога.short_message = Лев(БазовыйURL, 200);
СтруктураЗаписиЛога.full_message = Лев(БазовыйURL, 400) + ?(ЗначениеЗаполнено(ОтносительныйURL), "/" + Лев(ОтносительныйURL, 200), "");
СтруктураЗаписиЛога.level = УровеньСообщения;

СтруктураЗаписиЛога._logType = ТипЗаписиЛога;
СтруктураЗаписиЛога._baseUrl = БазовыйURL;
СтруктураЗаписиЛога._relativeUrl = ОтносительныйURL;
СтруктураЗаписиЛога._requestHeaders = ЗапросЗаголовки;
СтруктураЗаписиЛога._requestBody = ЗапросТело;
СтруктураЗаписиЛога._responseResult = Результат;
СтруктураЗаписиЛога._responseStatusCode = КодСостояния;
СтруктураЗаписиЛога._responseHeaders = ОтветЗаголовки;
СтруктураЗаписиЛога._responseBody = ОтветТело;
СтруктураЗаписиЛога._objectUid = ОбъектУникальныйИдентификатор;
СтруктураЗаписиЛога._objectType = ОбъектТип;
СтруктураЗаписиЛога._duration = Длительность;

Возврат СтруктураЗаписиЛога;

КонецФункции

 

Выполним отправку сообщения:

ВнешняяСистемаЛогирования = Новый Структура("Сервер, Порт", "graylog.mydomain", 15555);
СтруктураЗаписиЛога = ПолучитьЗаписьЛогаHTTPЗапроса(ТипЗаписиВнешнегоЛога_ВходящееСообщение(), БазовыйURL, ОтносительныйURL, ЗапросЗаголовки, ТелоЗапроса, ОтветЗаголовки, ОтветТело, КодСостояния, Результат, ДлительностьВыполнения);
ВыполнитьЗапросUDP(ВнешняяСистемаЛогирования, JSON(СтруктураЗаписиЛога));

 

Определение используемых методов:

//
//
Функция JSON(Структура)Экспорт

ЗаписьJSON = Новый ЗаписьJSON;
ЗаписьJSON.УстановитьСтроку();
ЗаписьJSON.ПроверятьСтруктуру = Ложь;
ЗаписатьJSON(ЗаписьJSON, Структура);
Возврат ЗаписьJSON.Закрыть();

КонецФункции

//
//
Функция ВыполнитьЗапросUDP(Настройки, ТелоЗапроса, КодировкаСообщения = "utf8") Экспорт

ОписаниеОшибки = "";

Попытка

NetObjectToIDispatch = Новый COMОбъект("NetObjectToIDispatch45");

udpClient  = NetObjectToIDispatch.СоздатьОбъект("System.Net.Sockets.UdpClient");

Хост = Настройки.Сервер;
Порт = Настройки.Порт;

Если НРег(КодировкаСообщения) = "utf8" Тогда
SystemTextEncoding = NetObjectToIDispatch.ПолучитьТип("System.Text.Encoding").UTF8;
ИначеЕсли НРег(КодировкаСообщения) = "unicode" Тогда
SystemTextEncoding = NetObjectToIDispatch.ПолучитьТип("System.Text.Encoding").Unicode;
Иначе
SystemTextEncoding = NetObjectToIDispatch.ПолучитьТип("System.Text.Encoding").ASCII;
КонецЕсли;
sendBytes = SystemTextEncoding.GetBytes(СтрЗаменить(СтрЗаменить(ТелоЗапроса, Символы.ВК, " "), Символы.ПС, " "));

udpClient.Send(sendBytes, sendBytes.getLength(), Хост, Порт);

udpClient.Close();

Исключение

ОписаниеОшибки = ОписаниеОшибки();
Сообщить(ОписаниеОшибки);

КонецПопытки;

NetObjectToIDispatch = Неопределено;
udpClient = Неопределено;
SystemTextEncoding = Неопределено;
sendBytes = Неопределено;

КонецФункции

//
//
Функция ТипЗаписиВнешнегоЛога_ВходящееСообщение() Экспорт Возврат "innerRequest"; КонецФункции

//
//
Функция ТипЗаписиВнешнегоЛога_ИсходящееСообщение() Экспорт Возврат "outerRequest"; КонецФункции

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

graylogExample

 

По сути данный подход реализует ту же функцию отправки данных в грэйлог, которую экранируют агенты, типа logstash, NXlog и т.д.
 

16 Comments

  1. KereberoS

    Статья начинается с чудесной фразы: «Думаю, что на сегодняшний день нет необходимости подробно объяснять что такое Graylog».

    Зачем в таком случае писать о Грейлог, если «об этом нет необходимости объяснять»?

    Reply ↓
  2. nicxxx

    Одно дело знать, другое-уметь использовать

    Reply ↓
  3. Scorpion4eg

    Чуть-чуть добавлю. У Graylog есть очень удобный механизм распределения общего потока сообщений на streams. А уже на этот stream можно навесить алерты, в т.ч. в slack.

    Мы так пока следим за ошибками в модулях.

    Ловишь исключение, получаешь сообщение в slack. В истории алертов находишь сообщение. По трек номеру сессии смотришь — а что пользователь делал, чтобы все сломать.

    Reply ↓
  4. Lars Ulrich

    (1) На Пулитцера не претендую, была цель донести реализацию.

    Если Вы знали, как готовить Graylog, то объяснять действительно не нужно. Ну а если НЕ знали, но заинтересовались, то дополнительная цель достигнута 🙂

    Reply ↓
  5. comol

    Статья огонь. Вопросы по теме:

    в чем преимущество GELF формата?

    Объём логов? Скорость записи? Скорость поиска?

    Reply ↓
  6. Scorpion4eg

    (5) Грубо говоря — ни в чем. GELF — Graylog Extended Log Format.

    Это просто один из форматов, которые поддерживает Graylog и кастомные логи проще всего в нем слать

    Reply ↓
  7. comol

    (6) В плане убирания ЖР в graylog в этом случае что мы выиграем?…

    Ещё что-то слышал про проксирование и формирование очереди через graylog… Если поток событий больше чем может переварить СУБД для хранения логов. Ну или если в СУБД для хранения логов нужно запись организовывать пакетно. Или вы только GELF формат у себя используете? А clickhouse graylog не поддерживает не в курсе?

    Reply ↓
  8. Scorpion4eg

    (7) Здесь не подскажу. Если не ошибаюсь Graylog опирается на mongodb.

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

    Но думаю можно завернуть поток логов на связку logstash-rabbitMQ-Graylog. Logstash умеет преобразовывать в gelf, а RabbitMQ возьмет на себя роль балансировщика. Ну и graylog умеет масштабироваться горизонтально — выделить несколько нод на сбор логов.

    Reply ↓
  9. comol

    (8)

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

    А вот тут можно подробнее? Ведь если лог-и собираются, то либо для поиска по ним чего-то либо для анализа…

    И в том и в том случае у вас СУБД, даже если она просто индексирует текстовые файлики. Ведь если там реально нереальный объём их надо индексировать?…

    СУБД не справится даже если оно поверх spark/hadoop или это что-то ооочень многонодное и кластеризованное?

    «не верю» (с).

    И как, кстати, вы умудрились получить в 1С такой поток логов что не справятся специализированные инструменты?

    Reply ↓
  10. Scorpion4eg

    (9) Так. Похоже мы запутались.

    1. Логи собираем для того чтобы, быстро разбираться с исключениями на проде.

    2. У нас нет огромного потока логов. Я бы сказал что у нас наоборот очень скромный поток.

    3. Graylog я решил использовать, потому что а) неперевариваю Kibana б) не хочу писать запросы в Clickhouse для анализа ситуаций в) хочу иметь удобный, гибкий и быстро настраиваемый сервис уведомлений о факапах

    Reply ↓
  11. acsent

    сами ошибки транслируете из ЖР? или тех журнал?

    Reply ↓
  12. Lars Ulrich

    (11) У нас в ЖР кроме критичных ошибок больше ничего не пишется. Интеграционные логи фактически внешние.

    Reply ↓
  13. acsent

    (12) Те вы сразу сами пишите во внешний лог?

    Reply ↓
  14. herfis

    Предлагается писать лог напрямую во внешний лог-агрегатор внешней компонентой через сетку??

    Нууууу, такое…. Базовое логирование таким не заменишь. Собирать какие-то маркеры — можно, в принципе… Но зачем в онлайн упираться?

    Стоп, туплю. Это ж просто удобный способ донести данные из любого источника (тех же первичных логов). Тогда ок. Плюсанул.

    Reply ↓
  15. Lars Ulrich

    (13) Да, сразу во внешний лог.

    Reply ↓
  16. asved.ru
    2084 год. Искусственный интеллект на базе платформы 1C:Universe 128.256.512.65536 все еще поддерживает COM-объекты в силу низкого порога вхождения для разработчика.
    Reply ↓

Leave a Comment

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