Настройки логирования#

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

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

Это используется для:

  • Отладки — логирование включается только для проблемного сценария, не засоряя общий лог.

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

  • Аудита — все изменения важных объектов фиксируются с привязкой к контексту и тегами.

Всё это работает без изменения кода и перезапуска — настройки применяются динамически.

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

  1. Регистрирует настройки логирования для методов класса.

  2. Задаёт условия применения логирования для этих настроек.

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

Совет

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

Настройки можно открыть через раздел Сущности > Классы, открыть карточку нужного класса, в карточке открыть закладку Настройки логирования.

Ключевые возможности#

  • Логирование можно настроить точечно для конкретных методов, типов объектов или подклассов.

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

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

  • Регистрация настроек программно через API.

Структура#

Лог#

Btk_Log «Лог» — V-коллекция, используемая для хранения записей о событиях, действиях пользователей и системных операциях в Global. В отличие от стандартного аудита, лог позволяет выполнить пользовательскую настройку записываемых в таблицу сообщений.

Структура данных Btk_Log позволяет сохранять информацию о:

  • Объекте-источнике (экземпляр класса).

  • Вызываемой процедуре (процедуре, инициировавшей запись в лог).

  • Типе лога.

  • Логирующей сессии (транзакции, в рамках которой была произведена вставка в лог).

  • Теге (служит для структурирования информации).

  • Тексте лога.

  • Вызываемом методе (текстовое поле, в котором описана цепочка вызовов методов, приведших к записи).
    Пример: api1.proc1 > api2.proc2.

  • Дополнительных характеристиках (в формате JSON).

Типы логов#

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

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

Код

Назначение

Системное наименование

Приоритет

FATAL

Критическая ошибка

PDM_LOG_FATAL

0

ERROR

Ошибка

PDM_LOG_ERROR

10

WARNING

Предупреждение

PDM_LOG_WARNING

20

INFO

Информационное сообщение

PDM_LOG_INFO

30

DEBUG

Отладочная информация

PDM_LOG_DEBUG

40

TRACE

Трассировка выполнения

PDM_LOG_TRACE

50

BLOCK

Блокировка

PDM_LOG_BLOCK

0

Приоритет показывает порядок фильтрации типов логов. Чем меньше числовое значение приоритета, тем выше важность типа лога. Если в условии разрешён INFO, система также разрешит более важные типы: WARNING, ERROR, FATAL и BLOCK.

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

Использование лога

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

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

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

  3. В программный код добавить обработку, выполняющую вставку записи в таблицу Btk_Log при срабатывании соответствующего триггера события.

  4. Выполнить настройку логирования для разграничения записей.

Для консультантов по внедрению Global ERP

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

Дерево настроек логирования#

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

Pm_CBStatementDocApi.processDocsByCBS (родитель)
└── Pm_CBStatementDocApi.processDocs (дочерний метод)

Ниже приведена ER-диаграмма связи Btk_Log, настроечных классов и непосредственно самого класса, на котором необходимо включить логирование.

diag

Условия логирования#

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

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

11

Зачем нужны условия?

  • Фильтрация записей в лог в зависимости от:

    • типа лога;

    • типа объекта, например только для документов типа Выписка банка;

    • подкласса, например только для исходящих документов.

  • Сокращение объёма логов в продакшене.

  • Включение детального логирования только для определённых сценариев или объектов.

Как работают условия?

  1. Регистрация условия происходит через Btk_LogClassSettingCondApi() или через интерфейс.

  2. При записи в лог с bUseSetting = true и внутри логирующей сессии система:

    • определяет, какой метод вызывает insertMsg;

    • проверяет, есть ли для метода зарегистрированные условия;

    • сравнивает тип лога сообщения с типом лога, разрешённым в условиях;

    • применяет иерархию типов логов.

  3. Условия проверяются только для того метода, в котором выполняется запись в лог. Условия, заданные для родительских методов, не применяются к дочерним методам.

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

Совет

Например, если в условии разрешён INFO, будут записываться сообщения INFO, WARNING, ERROR, FATAL и BLOCK. Сообщения DEBUG и TRACE записываться не будут.

Логирующая сессия#

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

Без логирующей сессии:

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

  • Настройки класса игнорируются полностью, даже если указано bUseSetting = true.

  • Нет возможности применить фильтрацию по типу объекта, подклассу или типу лога.

Зачем нужна сессия:

  • Объединение нескольких логируемых сообщений в одну сессию.

  • Изоляция настроек — сессия гарантирует, что настройки одного потока или процесса не влияют на другие. Даже если создается дочерняя логирующая сессия для записи лога, исходная сессия не изменится.

  • Определение контекста вызова — система знает, в каком методе происходит запись и кто его вызвал.

  • Применение условий — только внутри сессии система проверяет, разрешено ли писать лог для текущего метода, типа объекта, подкласса и типа лога.

Как работает:

При вызове Btk_LogApi().withLogSession(...) система:

  • Сохраняет в контексте имя метода и его идентификатор.

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

  • Начинает отслеживать все последующие вызовы insertMsg(..., bUseSetting = true) внутри блока.

  • При записи в лог:

    • Проверяет, зарегистрирован ли метод в дереве настроек.

    • Проверяет, активны ли все его родители.

    • Применяет только его собственные условия.

    • Принимает решение о записи сообщения в лог.

Ключевые API#

Btk_LogApi (основной API для записи логов)
├── Btk_LogClassSettingApi (регистрация методов в дереве настроек)
├── Btk_LogTypeApi (работа с типами логов)
├── Btk_LogClassSettingCondApi (управление условиями логирования)
└── Btk_LogSession (управление контекстом логирования)

Создание#

Создание настроек#

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

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

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

Регистрация методов, на которых происходит запись в лог:

// Регистрация верхнеуровневой (родительской) настройки для метода Pm_CBStatementDocApi.processDocsByCBS
val idSettingParent = Btk_LogClassSettingApi().register("Pm_CBStatementDocApi.processDocsByCBS", idClass)

// Регистрация дочерней настройки для метода Pm_CBStatementDocApi.processDocs,
// вызываемого из метода Pm_CBStatementDocApi.processDocsByCBS (с указанием родителя)
val idSettingChild = Btk_LogClassSettingApi().register("Pm_CBStatementDocApi.processDocs", idClass, idSettingParent)

Создание условий#

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

Регистрация условий, по которым проверяется настройка логирования:

// Регистрация условия на классе
Btk_LogClassSettingCondApi().registerByClass(idSettingParent, Btk_LogTypeApi().findByMnemoCode("INFO"))

// Регистрация условия на типе объекта
Btk_LogClassSettingCondApi().registerByObjectType(idSettingChild, Btk_LogTypeApi().findByMnemoCode("INFO"), idOTCBStatementDoc)

// Регистрация условия на подклассе
Btk_LogClassSettingCondApi().registerBySubClass(idSettingChild, Btk_LogTypeApi().findByMnemoCode("WARNING"), idSBCBStatementDoc)

Создание логирующей сессии#

Логирующая сессия задается только из кода.

Пример регистрации логирующей сессии и записи в лог с учетом настроек:

// Открытие логирующей сессии от объекта логирования
def processDocsByCBS(idpCBStatement: NLong): Unit = {
  val ropvCBStatement = Pm_ClientBankStatementApi().load(idpCBStatement)

  // Открытие логирующей сессии
  Btk_LogApi().withLogSession("Pm_CBStatementDocApi.processDocsByCBS", ropvCBStatement.gid) {
    // Запись в лог
    Btk_LogApi().insertMsg(
      "INFO"
      ,ropvCBStatement
      ,"Начало обработки банковской выписки"
      ,"Обработка банковской выписки"
      ,bUseSetting = true // Параметр указывает, что при записи учитывается настройка на классе
    )

    // Исполняемая процедура
    processDocs(refreshByParent(ropvCBStatement).map(_.get(_.id)).toList)
  }
}

// Открытие логирующей сессии от класса
def processDocs(idapCBStatementDoc: List[NLong]): Unit = {
  // Открытие логирующей сессии
  Btk_LogApi().withLogSession("Pm_CBStatementDocApi.processDocsByCBS", idClass) {
    // Исполняемая процедура

    // Запись в лог
    Btk_LogApi().insertMsg(
      "INFO"
      ,ropvCBStatementDoc
      ,"Завершение обработки банковской выписки"
      ,"Обработка банковской выписки"
      ,bUseSetting = true // Параметр указывает, что при записи учитывается настройка на классе
    )
  }
}

Примечание

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

Пример:

lazy val sLogSettingProcessDocsByCBS = "Pm_CBStatementDocApi.processDocsByCBS" // sLSProcessDocsByCBS
lazy val sLogSettingProcessDocs = "Pm_CBStatementDocApi.processDocs" // sLSProcessDocs

Использование#

Примеры использования#

Интерпретация банковской выписки:

def processDocsByCBS(idpCBStatement: NLong): Unit = {
  val ropvCBStatement = Pm_ClientBankStatementApi().load(idpCBStatement)

  Btk_LogApi().withLogSession(sLogSettingProcessDocsByCBS, ropvCBStatement.gid) {
    val sStartProcMsg = s"Запущена интерпретация выписки ${ropvCBStatement.get(_.sNum)} (${ropvCBStatement.gid}) от ${ropvCBStatement.get(_.dStatement).toString("dd.MM.yyyy hh:mm:ss")}."
    Btk_LogApi().atInsertMsg("INFO", ropvCBStatement, sStartProcMsg, "", bUseSetting = true)
    Btk_InfoLogPkg().info(sStartProcMsg)

    val idavCBStatementDoc = refreshByParent(ropvCBStatement).map(_.get(_.id))
    processDocs(idavCBStatementDoc.toList)

    val sFinishProcMsg = s"Завершена интерпретация выписки ${ropvCBStatement.get(_.sNum)} (${ropvCBStatement.gid}) от ${ropvCBStatement.get(_.dStatement).toString("dd.MM.yyyy hh:mm:ss")}."
    Btk_LogApi().atInsertMsg("INFO", ropvCBStatement, sFinishProcMsg, "", bUseSetting = true)
    Btk_InfoLogPkg().info(sFinishProcMsg)
  }
}

Вынесение логики в отдельный метод:

def isDocProcessed(rop: Pm_CBStatementDocApi#ApiRop): Boolean = {
  val ropvCBStatement = Pm_ClientBankStatementApi().load(rop.get(_.idClientBankStatement))

  Btk_LogApi().withLogSession(sLogSettingIsDocProcessed, rop.gid) {
    if (rop.get(_.idStateMC) > 100.nr) {
      val sErrorMsg = s"Процедура интерпретации позиции банковской выписки ${rop.get(_.sNumDoc)} по документу ${ropvCBStatement.get(_.sNum)} (${ropvCBStatement.gid}) от ${ropvCBStatement.get(_.dStatement).toString("dd.MM.yyyy hh:mm:ss")} невозможна, так как она уже обработана пользователем"
      Btk_LogApi().atInsertMsg("ERROR", rop, sErrorMsg, "", bUseSetting = true)
      Btk_InfoLogPkg().error(sErrorMsg)
      true
    } else {
      val sInfoMsg = s"Проверка пользовательской обработки позиции выписки ${rop.get(_.sNumDoc)} по документу ${ropvCBStatement.get(_.sNum)} (${ropvCBStatement.gid}) от ${ropvCBStatement.get(_.dStatement).toString("dd.MM.yyyy hh:mm:ss")}"
      Btk_LogApi().atInsertMsg("INFO", rop, sInfoMsg, "", bUseSetting = true)
      Btk_InfoLogPkg().info(sInfoMsg)
      false
    }
  }
}

Дополнительные примеры настройки и использования можно посмотреть в реализации:

  • ru.bitec.app.pm.clientbank.Pm_CBStatementDocApi#regLogSetting;

  • ru.bitec.app.pm.clientbank.Pm_CBStatementDocApi#processDocs.

Результат логирования#

Результат логирования сохраняется в журнале событий, который настраивается со стороны заказчика.

Просматривать записи можно двумя способами:

  • через общий журнал логов;

  • через журнал логирующих сессий.

Общий журнал логов#

Общий журнал показывает все записи лога.

logs

Все логи системы хранятся в Настройки системы > Сущности > Классы > btk_log.

Журнал логирующих сессий#

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

logs

Он расположен в Настройки системы > Сущности > Классы > btk_logSession.

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

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

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

Записи лога связаны с объектом логирующей сессии по соотношению Btk_Log.idLogSession = Btk_LogSession.id. За счёт этого журнал btk_logSession позволяет сначала выбрать нужную сессию, а затем просмотреть все записи лога, относящиеся к ней.

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

Пример использования этого журнала реализован в приложении Управление денежными сессиями > Отчеты > Журнал событий.

Чистка журнала логов#

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

Примечание

Для ручной очистки реализован API-метод clearBtkLog(), принимающий на вход системное имя класса и количество дней сдвига. К нему подключен джоб, автоматически очищающий таблицу.

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

Детальнее про задания очистки смотри в разделе Менеджер заданий.

Расширение#

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

  1. Зарегистрируйте метод через Btk_LogClassSettingApi().register().

  2. Определите родительский метод в иерархии вызовов.

  3. Настройте условия логирования через Btk_LogClassSettingCondApi().

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

Ограничения#

  • Фильтрация выполняется по типу лога. Например, если разрешён INFO, система записывает сообщения INFO, WARNING, ERROR, FATAL и BLOCK, но не записывает DEBUG и TRACE.

  • Типы с одинаковым приоритетом обрабатываются одинаково при фильтрации. Например, FATAL и BLOCK имеют приоритет 0, но используются для разных событий.

  • Нет автоматического построения дерева вызовов — вся иерархия регистрируется вручную.

  • Нет встроенной валидации — можно зарегистрировать несуществующий метод.

  • Запись в лог происходит только при выполнении всех условий:

    • Метод присутствует в дереве настроек.

    • Все вышестоящие методы активны.

    • Для текущего метода выполняются условия логирования.