Настройки логирования#
Механизм управления логированием на уровне отдельных методов класса обеспечивает контроль над записью в журнал событий без вмешательства в бизнес-логику приложения.
Он позволяет включать логирование точечно — только для нужных методов, типов документов или уровней сообщений (INFO, WARNING и т.д.).
Это используется для:
Отладки — логирование включается только для проблемного сценария, не засоряя общий лог.
Мониторинга ошибок — можно настроить логирование ошибок, предупреждений и более важных событий, игнорируя информационные, отладочные и трассировочные сообщения.
Аудита — все изменения важных объектов фиксируются с привязкой к контексту и тегами.
Всё это работает без изменения кода и перезапуска — настройки применяются динамически.
Для формирования механизма управления логированием разработчик в коде:
Регистрирует настройки логирования для методов класса.
Задаёт условия применения логирования для этих настроек.
Использует логирующую сессию, чтобы записи в лог учитывали настройки и условия.
Совет
В стандартной поставке существует ограниченный набор классов, на которых включено логирование.
Настройки можно открыть через раздел Сущности > Классы, открыть карточку нужного класса, в карточке открыть закладку Настройки логирования.
Ключевые возможности#
Логирование можно настроить точечно для конкретных методов, типов объектов или подклассов.
Поддержка иерархического дерева методов, при котором активность настроек наследуется от родительских узлов, а условия проверяются только для текущего метода.
Автоматическая фильтрация записей по типу лога (ошибка, информационное сообщение, предупреждение и др.) с учётом иерархии, заданной пользователем.
Регистрация настроек программно через API.
Структура#
Лог#
Btk_Log «Лог» — V-коллекция, используемая для хранения записей о событиях, действиях пользователей и системных операциях в Global. В отличие от стандартного аудита, лог позволяет выполнить пользовательскую настройку записываемых в таблицу сообщений.
Структура данных Btk_Log позволяет сохранять информацию о:
Объекте-источнике (экземпляр класса).
Вызываемой процедуре (процедуре, инициировавшей запись в лог).
Типе лога.
Логирующей сессии (транзакции, в рамках которой была произведена вставка в лог).
Теге (служит для структурирования информации).
Тексте лога.
Вызываемом методе (текстовое поле, в котором описана цепочка вызовов методов, приведших к записи).
Пример:api1.proc1 > api2.proc2.Дополнительных характеристиках (в формате JSON).
Типы логов#
Тип лога определяет назначение записи и используется в условиях логирования, чтобы система могла определить, нужно ли сохранять сообщение для текущего метода, объекта или подкласса.
В таблице приведена базовая коробочная настройка типов логов. Для конкретной базы или проекта состав типов может отличаться: типы логов могут быть добавлены, изменены или удалены.
Код |
Назначение |
Системное наименование |
Приоритет |
|---|---|---|---|
|
Критическая ошибка |
|
|
|
Ошибка |
|
|
|
Предупреждение |
|
|
|
Информационное сообщение |
|
|
|
Отладочная информация |
|
|
|
Трассировка выполнения |
|
|
|
Блокировка |
|
|
Приоритет показывает порядок фильтрации типов логов. Чем меньше числовое значение приоритета, тем выше важность типа лога. Если в условии разрешён INFO, система также разрешит более важные типы: WARNING, ERROR, FATAL и BLOCK.
FATAL и BLOCK имеют одинаковый приоритет 0, но остаются разными типами логов. FATAL используется для критических ошибок, BLOCK — для событий блокировки.
Использование лога
Для обеспечения записи сообщения в лог необходимо выполнить следующие шаги:
Определить класс, к которому подключается
Btk_Log, объекты которого будут выступать в качестве источника записи в лог.В программный код добавить создание логирующей сессии.
В программный код добавить обработку, выполняющую вставку записи в таблицу
Btk_Logпри срабатывании соответствующего триггера события.Выполнить настройку логирования для разграничения записей.
Для консультантов по внедрению Global ERP
Для обеспечения записи сообщения в лог необходимо создать задачу (ДП) с указанием класса, к которому необходимо подключить Btk_Log, и описанием содержимого лога (значения, устанавливаемые в поля лога).
Дерево настроек логирования#
На закладке Настройка логирования для класса отображается дерево последовательно вызванных методов, на которых происходит запись в лог.

Pm_CBStatementDocApi.processDocsByCBS (родитель)
└── Pm_CBStatementDocApi.processDocs (дочерний метод)
Ниже приведена ER-диаграмма связи Btk_Log, настроечных классов и непосредственно самого класса, на котором необходимо включить логирование.

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

Зачем нужны условия?
Фильтрация записей в лог в зависимости от:
типа лога;
типа объекта, например только для документов типа
Выписка банка;подкласса, например только для исходящих документов.
Сокращение объёма логов в продакшене.
Включение детального логирования только для определённых сценариев или объектов.
Как работают условия?
Регистрация условия происходит через
Btk_LogClassSettingCondApi()или через интерфейс.При записи в лог с
bUseSetting = trueи внутри логирующей сессии система:определяет, какой метод вызывает
insertMsg;проверяет, есть ли для метода зарегистрированные условия;
сравнивает тип лога сообщения с типом лога, разрешённым в условиях;
применяет иерархию типов логов.
Условия проверяются только для того метода, в котором выполняется запись в лог. Условия, заданные для родительских методов, не применяются к дочерним методам.
Для дочернего метода также учитывается активность всей цепочки настроек в дереве. Если хотя бы одна родительская настройка неактивна, запись для дочернего метода не выполняется, даже если условия самого дочернего метода подходят.
Совет
Например, если в условии разрешён 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.
Результат логирования#
Результат логирования сохраняется в журнале событий, который настраивается со стороны заказчика.
Просматривать записи можно двумя способами:
через общий журнал логов;
через журнал логирующих сессий.
Общий журнал логов#
Общий журнал показывает все записи лога.

Все логи системы хранятся в Настройки системы > Сущности > Классы > btk_log.
Журнал логирующих сессий#
Для удобства анализа логов в системе предусмотрен отдельный журнал логирующих сессий.

Он расположен в Настройки системы > Сущности > Классы > btk_logSession.
При открытии журнала в верхней части окна отображается список логирующих сессий. В нижней части окна отображаются все записи лога, относящиеся к выбранной сессии.
В отличие от общего журнала, где отображаются все записи по всем сессиям, журнал btk_logSession сначала разделяет данные по сессиям, после чего позволяет просмотреть только те записи, которые относятся к выбранной сессии.
Такой способ просмотра удобно использовать, когда необходимо проанализировать цепочку записей, сформированных в рамках одного выполнения бизнес-операции.
Записи лога связаны с объектом логирующей сессии по соотношению Btk_Log.idLogSession = Btk_LogSession.id. За счёт этого журнал btk_logSession позволяет сначала выбрать нужную сессию, а затем просмотреть все записи лога, относящиеся к ней.
При выполнении одной транзакции, в рамках которой формируется цепочка записей в лог, этим записям присваивается идентификатор соответствующей логирующей сессии.
Пример использования этого журнала реализован в приложении Управление денежными сессиями > Отчеты > Журнал событий.
Чистка журнала логов#
Очистка логов для классов с преднастроенным логированием выполняется автоматически. Она настроена в типовом задании очистки устаревших данных с окном хранения 10 дней в менеджере заданий.
Примечание
Для ручной очистки реализован API-метод clearBtkLog(), принимающий на вход системное имя класса и количество дней сдвига. К нему подключен джоб, автоматически очищающий таблицу.
При создании новых методов логирования необходимо зарегистрировать соответствующую запись в этом задании. Это обеспечивает автоматическое удаление устаревших записей логов и предотвращает неконтролируемый рост объема данных.
Детальнее про задания очистки смотри в разделе Менеджер заданий.
Расширение#
Для добавления новых методов в систему логирования:
Зарегистрируйте метод через
Btk_LogClassSettingApi().register().Определите родительский метод в иерархии вызовов.
Настройте условия логирования через
Btk_LogClassSettingCondApi().Используйте константы для имен методов во избежание ошибок.
Ограничения#
Фильтрация выполняется по типу лога. Например, если разрешён
INFO, система записывает сообщенияINFO,WARNING,ERROR,FATALиBLOCK, но не записываетDEBUGиTRACE.Типы с одинаковым приоритетом обрабатываются одинаково при фильтрации. Например,
FATALиBLOCKимеют приоритет0, но используются для разных событий.Нет автоматического построения дерева вызовов — вся иерархия регистрируется вручную.
Нет встроенной валидации — можно зарегистрировать несуществующий метод.
Запись в лог происходит только при выполнении всех условий:
Метод присутствует в дереве настроек.
Все вышестоящие методы активны.
Для текущего метода выполняются условия логирования.