MorphCluster Logger

Подсистема логирования состоит из двух пакетов:

Серверная часть (@morphcluster/logger)

Пакет регистрирует в ServiceHost четыре сервиса: LogStoreBackend, LogStoreAdmin, LogOraList и вспомогательный PostgresMigrator (из отдельного пакета). Основное хранилище — PostgreSQL, взаимодействие с которым вынесено в сервис LogPgStorage2.

LogStoreBackend

Главный сервис приёма логов. Реализует два запроса: write и close. Полученные данные не пишутся в базу сразу, а накапливаются в оперативной памяти в виде дерева объектов LogRecord (класс ActiveLogs). При закрытии корневого лога вся ветка отправляется в LogPgStorage2.insertLogTree().

// Пример использования (происходит автоматически при подключении LoggerBackendStore)
await logStoreBackend.write({ id, parentId, message, level, ... });
await logStoreBackend.close({ id, timestamp });

Жизненный цикл активного лога:

  1. Клиент вызывает write при создании лога и для каждого дочернего сообщения.
  2. Сообщения собираются в древовидную структуру LogRecord внутри ActiveLogs.
  3. Когда клиент вызывает close для записи (или по тайм-ауту), ActiveLogs проверяет, все ли дочерние записи закрыты. Если да — вся ветка помечается на архивацию и через 5 секунд (flushLogTime) вызывается onArchive, передающий корневой LogRecord в LogPgStorage2.
  4. LogPgStorage2 вставляет всё дерево одной массовой вставкой в таблицу logs2.

Свойства и зависимости:

LogRecord

Модель одной записи лога. Образует древовидную структуру: родитель (parent) и массив дочерних записей (children).

const record = new LogRecord({
  id: 'uuid',
  message: 'Запрос выполнен',
  level: 3,
  service: 'MyService',
  payload: { ... }
});
record.addChild(childRecord);

Основные поля:

Методы:

ActiveLogs

Хранилище всех активных (ещё не заархивированных) записей в памяти. Управляет их жизненным циклом: вставка, закрытие, автоматическое закрытие по тайм-ауту, архивация.

const activeLogs = new ActiveLogs();
activeLogs.onArchive = (rootLog) => { /* сохранить в БД */ };
activeLogs.insert({ id: '...', message: 'Старт' });
activeLogs.close('id', Date.now());

Основные методы и логика:

Свойства:

LogPgStorage2

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

await logPgStorage2.insertLogTree(rootLogRecord);
const result = await logPgStorage2.list({ service: 'MyService', limit: 20 });
const log = await logPgStorage2.byId('uuid');

Управление:

Методы:

Схема БД (подразумевается): таблица logs2 с колонками id, parent_id, message, level, duration, timestamp, order, host, service, username, ip, created, payload, num (автоинкрементный номер для курсорной пагинации).

LogStoreAdmin

Сервис для административного доступа к логам. Все запросы требуют прав администратора (needAdmin: true).

Запросы:

Зависимости: LogStoreBackend, LogPgStorage2.

LogOraList

Специализированное представление для получения логов запросов к Oracle (тип сообщения 'OraQueries/exec' или 'OraQueries/execFunc'). Извлекает из древовидной структуры входные и выходные параметры, собирая их из фиксированной иерархии дочерних записей (4 уровня вложенности). Возвращает плоский список с полями queryName, duration, in_payload, out_payload.

Запрос:

Схемы сервисов

Клиентская часть (@morphcluster/logger-client)

LoggerBackendStore

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

import { LoggerBackendStore } from '@morphcluster/logger-client';

const logStoreBackend = new LoggerBackendStore(host);
logger.backends.push(logStoreBackend);

Принцип работы:

  1. При создании получает ссылку на ServiceHost и подписывается на событие onServiceStarted.
  2. Как только в хосте появляется сервис с именем LogStoreBackend (или LogStoreAcc, если есть), он сохраняет ссылку на него.
  3. До этого момента все вызовы write и close помещаются в очередь (queue).
  4. При обнаружении сервиса очередь «сбрасывается» — все накопленные операции последовательно отправляются в реальный сервис. После этого flushed устанавливается в true, и дальнейшие вызовы выполняются напрямую, без очереди.

Таким образом, логирование не теряется даже на этапе запуска приложения, когда сервис логирования ещё не готов.

Свойства:

Взаимодействие компонентов

  1. Сервис приложения использует обычный Logger из ядра, добавив в него бэкенд LoggerBackendStore.
  2. LoggerBackendStore отправляет запросы write и close в локальный или удалённый LogStoreBackend (через GlobalService).
  3. LogStoreBackend сохраняет все записи в ActiveLogs в оперативной памяти.
  4. Когда ветка лога полностью закрыта, ActiveLogs вызывает LogPgStorage2.insertLogTree(), который одной массовой вставкой записывает всё дерево в PostgreSQL.
  5. Для чтения логов используются сервисы LogStoreAdmin (универсальный) и LogOraList (специализированный для Oracle-запросов). Они читают данные напрямую из ActiveLogs (активные) и из LogPgStorage2 (архивные).

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


Revision #2
Created 21 June 2026 19:40:43 by Admin
Updated 26 June 2026 13:52:59 by Admin