Skip to main content

05 Система журналирования

Logger

Logger является неотъемлемой частью ядра и его экземпляр всегда доступен в сервисе по адресу this.Logger Инициализация логгера:

import {ServiceHost,Logger} from '@morphcluster/core'
import {LoggerBackendStore } from '@morphcluster/logger'
...
host = new ServiceHost("TestHost")
const logger = new Logger()
//Добавление бэкэнда (об этом ниже)
logger.backends.push(new LoggerBackendStore(host))
//Добавляем логгер в хост, чтобы он автоматически распространил его по сервисам в хосте
host.Logger = logger
//Ниже, начинаем добавление сервисов
host.addService(new Test())

LoggerBackend

LoggerBackend - специальный клиент для отправки данных полученным логгером. Встроенный LoggerBackendStore работает с сервисом (в т.ч. удаленно) LogStorePlain, который сохраняет данные в PostgreSQL. Инициализация LogStorePlain:

import {LogStorePlain} from '@morphcluster/logger'
...
//Ниже, начинаем добавление сервисов
//Хранилище логов
host.addService(new LogStorePlain(host,{
  "pg": {
    //uri для подключения postgresql
    "uri": "postgresql://postgres:123@127.0.0.1:5432/carabi",
    //schema из postgresql
    "schema": "logger",
    //timezone pg клиента
    "timezone": "Europe/Moscow"
  }
}))

Отправка сообщений

Чтобы написать начальное сообщение необходимо создать цепочку.

//payload - любой объект, который будет сохранен в журнале (или null)
let payload = {"test":123}
//options - опции записи журнала
let options = {
  //Уровень важности сообщения:
  //1 - отладка, 10 - обычный, 50 - предупр., 100 - ошибка
  level:10,
  //Сервис, который создал цепочку (обычно this.name)
  service: "HelloWorld",
  //Логин текущего пользователя сессии (если есть)
  username: options.username
}
let log = await this.Logger.createWriter("Сообщение", payload, options)

createWriter вернет дочерний объект класса Logger. Этим объектом можно продолжить цепочку, и его, желательно, передать во все вызовы внутри этого работы. Если в полученном объекте (log в примере) вызвать еще раз createWriter, то получится древовидная структура (необходимо для сетевых запросов). Продолжить цепочку (следующее сообщение) можно командой:

let payload = {"test":123}
await log.write("Сообщение", payload, {level:10})

После окончания цепочки (текущей работы), ее необходимо закрыть:

log.close()

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

let log = await this.Logger.createWriter(...)
try {
  ...
  log.close()
} catch(e) {
  log.writeException(e)
  ...
}

Если перехватывать сообщение не нужно, можно использовать следующую конструкцию:

let log = await this.Logger.createWriter(...)
await log.wrap(async() => {
  //Здесь можно использовать log
  log.write(...)
})
//Здесь log уже будет закрыт, если произошло исключение - оно будет записано
//Но само исключение не будет перехвачено

Журналирование между сервисами

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

await this.HelloWorld.hello(data, workspace, log)

Исключение - если в схеме сервиса у этого запроса "noLogs":true

И наоборот, если ваш сервис вызывает, например FastifyRest по http запросу, то у вас уже есть готовая цепочка:

async hello(request, workspace, log) {
  await log.write("Hello to Logger")
  ...
}

Закрывать лог дожен тот метод, где он создан.

Когда для выполнения необходим логгер (напр. выполнение запроса), но записывать журнал не нужно, можно передать лог-заглушку, без заполнения backends:

const log = new Logger()
await this.HelloWorld.hello(data, workspace, log)