267 lines
11 KiB
JavaScript
267 lines
11 KiB
JavaScript
/*
|
||
Сервис интеграции ПП Парус 8 с WEB API
|
||
Модуль ядра: протоколирование работы
|
||
*/
|
||
|
||
//----------------------
|
||
// Подключение библиотек
|
||
//----------------------
|
||
|
||
const { validateObject, getNowString, deepClone, makeErrorText } = require("./utils"); //Вспомогательные функции
|
||
const db = require("./db_connector"); //Модуль взаимодействия с БД
|
||
const { LogFileWriter } = require("./log_file_writer"); //Запись протокола в файл
|
||
const {
|
||
SCONSOLE_LOG_COLOR_PATTERN_ERR,
|
||
SCONSOLE_LOG_COLOR_PATTERN_WRN,
|
||
SCONSOLE_LOG_COLOR_PATTERN_INF,
|
||
SLOG_PREFIX_INF,
|
||
SLOG_PREFIX_WRN,
|
||
SLOG_PREFIX_ERR,
|
||
SLOG_PREFIX_PROTOCOL_ERR
|
||
} = require("./constants"); //Общие константы
|
||
const { NLOG_STATE_INF, NLOG_STATE_WRN, NLOG_STATE_ERR } = require("../models/obj_log"); //Схемы валидации записи журнала работы сервиса обмена
|
||
const prmsLoggerSchema = require("../models/prms_logger"); //Схемы валидации параметров функций модуля
|
||
|
||
//--------------------------
|
||
// Глобальные идентификаторы
|
||
//--------------------------
|
||
|
||
//Интервал повторного сообщения об ошибке передачи строки лога по IPC (мс)
|
||
const NLOG_IPC_ERROR_REPORT_INTERVAL = 10000;
|
||
|
||
//Сообщение об останове сервера
|
||
const SMSG_SERVER_STOPPED = "Сервер приложений остановлен";
|
||
|
||
//------------
|
||
// Тело модуля
|
||
//------------
|
||
|
||
//Признак работы в дочернем процессе обработчика
|
||
const isWorkerProcess = () => typeof process.send === "function" && !!process.connected;
|
||
|
||
//Формирование строки лога без ANSI-оформления
|
||
const formatLogLine = (sNow, sPrefix, sMsg) => `${sNow} ${sPrefix}: ${sMsg}`;
|
||
|
||
//Оформление сообщения по типу записи
|
||
const resolveLogStyle = nLogState => {
|
||
let sPrefix = SLOG_PREFIX_INF;
|
||
let sColorPattern = "";
|
||
switch (nLogState) {
|
||
case NLOG_STATE_ERR: {
|
||
sPrefix = SLOG_PREFIX_ERR;
|
||
sColorPattern = SCONSOLE_LOG_COLOR_PATTERN_ERR;
|
||
break;
|
||
}
|
||
case NLOG_STATE_WRN: {
|
||
sPrefix = SLOG_PREFIX_WRN;
|
||
sColorPattern = SCONSOLE_LOG_COLOR_PATTERN_WRN;
|
||
break;
|
||
}
|
||
case NLOG_STATE_INF: {
|
||
sPrefix = SLOG_PREFIX_INF;
|
||
sColorPattern = SCONSOLE_LOG_COLOR_PATTERN_INF;
|
||
break;
|
||
}
|
||
default:
|
||
break;
|
||
}
|
||
return { sPrefix, sColorPattern };
|
||
};
|
||
|
||
//Класс управления протоколом
|
||
class Logger {
|
||
//Конструктор класса
|
||
constructor() {
|
||
this.dbConnector = null;
|
||
this.bLogDB = false;
|
||
this.fileWriter = null;
|
||
this.bStopLogged = false;
|
||
this.nLastIpcErrorAt = 0;
|
||
}
|
||
//Включение/выключение записи протоколов в БД
|
||
setLogDB(bLogDB) {
|
||
if (this.dbConnector) this.bLogDB = bLogDB;
|
||
}
|
||
//Установка объекта для протоколирования в БД
|
||
setDBConnector(dbConnector, bLogDB) {
|
||
if (dbConnector instanceof db.DBConnector) {
|
||
this.dbConnector = dbConnector;
|
||
if (bLogDB === true) this.setLogDB(true);
|
||
else this.setLogDB(false);
|
||
}
|
||
}
|
||
//Удаление объекта для протоколирования в БД
|
||
removeDBConnector() {
|
||
this.dbConnector = null;
|
||
this.setLogDB(false);
|
||
}
|
||
//Открытие файла лога (пустой путь - не открываем)
|
||
openLogFile(sLogPath) {
|
||
//Нет пути - протоколирование в файл не ведётся
|
||
if (!sLogPath || !String(sLogPath).trim()) {
|
||
this.bStopLogged = false;
|
||
return;
|
||
}
|
||
//Создаём писатель при первом реальном открытии
|
||
if (!this.fileWriter) this.fileWriter = new LogFileWriter();
|
||
this.fileWriter.open(sLogPath);
|
||
this.bStopLogged = false;
|
||
}
|
||
//Уведомление в консоль о протоколировании в файл (без записи в файл)
|
||
notifyFileLogConsole() {
|
||
if (this.fileWriter) this.fileWriter.notifyOpenedConsole();
|
||
}
|
||
//Корректное закрытие файла лога
|
||
async closeLogFile() {
|
||
if (this.fileWriter) await this.fileWriter.close();
|
||
}
|
||
//Аварийное синхронное освобождение файла лога
|
||
closeLogFileSync() {
|
||
if (this.fileWriter) this.fileWriter.closeSync();
|
||
}
|
||
//Признак, что сообщение об останове уже выдано
|
||
wasStopLogged() {
|
||
return this.bStopLogged === true;
|
||
}
|
||
//Фиксация выдачи сообщения об останове
|
||
markStopLogged() {
|
||
this.bStopLogged = true;
|
||
}
|
||
//Подготовка сообщения об останове сервера
|
||
prepareServerStoppedMsg(nCode) {
|
||
const nExitCode = typeof nCode === "number" ? nCode : 0;
|
||
this.markStopLogged();
|
||
return `${SMSG_SERVER_STOPPED} (код: ${nExitCode})`;
|
||
}
|
||
//Выдача сообщения об останове сервера
|
||
async notifyServerStopped(nCode) {
|
||
await this.warn(this.prepareServerStoppedMsg(nCode));
|
||
}
|
||
//Синхронная выдача сообщения об останове сервера (авария / process.on("exit"))
|
||
notifyServerStoppedSync(nCode) {
|
||
this.warnSync(this.prepareServerStoppedMsg(nCode));
|
||
}
|
||
//Сообщение об ошибке передачи строки лога по IPC
|
||
reportIpcLogError(e) {
|
||
const nNow = Date.now();
|
||
if (nNow - this.nLastIpcErrorAt < NLOG_IPC_ERROR_REPORT_INTERVAL) return;
|
||
this.nLastIpcErrorAt = nNow;
|
||
console.log(SCONSOLE_LOG_COLOR_PATTERN_ERR, `${getNowString()} ${SLOG_PREFIX_PROTOCOL_ERR} (IPC): `, makeErrorText(e));
|
||
}
|
||
//Отправка строки лога родителю (воркер)
|
||
sendLogLineToParent(prms) {
|
||
if (typeof process.send !== "function" || !process.connected) return;
|
||
if (!prms || typeof prms.sMsg !== "string") return;
|
||
try {
|
||
process.send({ ...prms, bLogLine: true }, err => {
|
||
if (err) this.reportIpcLogError(err);
|
||
});
|
||
} catch (e) {
|
||
this.reportIpcLogError(e);
|
||
}
|
||
}
|
||
//Приём строки лога от воркера
|
||
acceptWorkerLogLine(prms) {
|
||
if (!prms || typeof prms.sNow !== "string" || typeof prms.sPrefix !== "string" || typeof prms.sMsg !== "string") return;
|
||
this.emitLogLine(prms.nLogState, prms.sMsg, prms.sNow, prms.sPrefix);
|
||
}
|
||
//Выдача строки протокола: воркер - IPC; main - файл, затем console
|
||
emitLogLine(nLogState, sMsg, sNow, sPrefix, bSync) {
|
||
const style = resolveLogStyle(nLogState);
|
||
const sPrefixUse = sPrefix || style.sPrefix;
|
||
//Воркер - только IPC родителю (файл и console пишет main)
|
||
if (isWorkerProcess()) {
|
||
this.sendLogLineToParent({ nLogState, sNow, sPrefix: sPrefixUse, sMsg });
|
||
return;
|
||
}
|
||
//Main: сначала файл, затем console
|
||
const sLine = formatLogLine(sNow, sPrefixUse, sMsg);
|
||
if (this.fileWriter) {
|
||
if (bSync) this.fileWriter.appendSync(sLine);
|
||
else this.fileWriter.enqueue(sLine);
|
||
}
|
||
console.log(style.sColorPattern, `${sNow} ${sPrefixUse}: `, sMsg);
|
||
}
|
||
//Синхронное протоколирование без БД (авария / финал)
|
||
logSync(nLogState, sMsg) {
|
||
this.emitLogLine(nLogState, sMsg, getNowString(), undefined, true);
|
||
}
|
||
//Синхронное протоколирование ошибки
|
||
errorSync(sMsg) {
|
||
this.logSync(NLOG_STATE_ERR, sMsg);
|
||
}
|
||
//Синхронное протоколирование предупреждения
|
||
warnSync(sMsg) {
|
||
this.logSync(NLOG_STATE_WRN, sMsg);
|
||
}
|
||
//Синхронное протоколирование информации
|
||
infoSync(sMsg) {
|
||
this.logSync(NLOG_STATE_INF, sMsg);
|
||
}
|
||
//Протоколирование
|
||
async log(prms) {
|
||
//Фиксируем время
|
||
const sNow = getNowString();
|
||
//Проверяем структуру переданного объекта для подключения
|
||
let sCheckResult = validateObject(prms, prmsLoggerSchema.log, "Параметры функции протоколирования");
|
||
//Если структура объекта в норме
|
||
if (!sCheckResult) {
|
||
this.emitLogLine(prms.nLogState, prms.sMsg, sNow);
|
||
//Протоколируем в БД, если это необходимо
|
||
if (this.bLogDB) {
|
||
try {
|
||
//Если есть чем протоколировать
|
||
if (this.dbConnector && this.dbConnector.bConnected) {
|
||
await this.dbConnector.putLog(prms);
|
||
}
|
||
} catch (e) {
|
||
this.emitLogLine(NLOG_STATE_ERR, e.sMessage, sNow, SLOG_PREFIX_PROTOCOL_ERR);
|
||
try {
|
||
if (this.dbConnector && this.dbConnector.bConnected)
|
||
await this.dbConnector.putLog({ nLogState: NLOG_STATE_ERR, sMsg: `${SLOG_PREFIX_PROTOCOL_ERR}: ${e.sMessage}` });
|
||
} catch {}
|
||
}
|
||
}
|
||
} else {
|
||
this.emitLogLine(NLOG_STATE_ERR, sCheckResult, sNow, SLOG_PREFIX_PROTOCOL_ERR);
|
||
console.log(prms);
|
||
}
|
||
}
|
||
//Протоколирование ошибки
|
||
async error(sMsg, prms) {
|
||
//Подготовим параметры для протоколирования
|
||
let logData = prms ? deepClone(prms) : {};
|
||
//Выставим сообщение и тип записи журнала
|
||
logData.nLogState = NLOG_STATE_ERR;
|
||
logData.sMsg = sMsg;
|
||
//Протоколируем
|
||
await this.log(logData);
|
||
}
|
||
//Протоколирование предупреждения
|
||
async warn(sMsg, prms) {
|
||
//Подготовим параметры для протоколирования
|
||
let logData = prms ? deepClone(prms) : {};
|
||
//Выставим сообщение и тип записи журнала
|
||
logData.nLogState = NLOG_STATE_WRN;
|
||
logData.sMsg = sMsg;
|
||
//Протоколируем
|
||
await this.log(logData);
|
||
}
|
||
//Протоколирование информации
|
||
async info(sMsg, prms) {
|
||
//Подготовим параметры для протоколирования
|
||
let logData = prms ? deepClone(prms) : {};
|
||
//Выставим сообщение и тип записи журнала
|
||
logData.nLogState = NLOG_STATE_INF;
|
||
logData.sMsg = sMsg;
|
||
//Протоколируем
|
||
await this.log(logData);
|
||
}
|
||
}
|
||
|
||
//-----------------
|
||
// Интерфейс модуля
|
||
//-----------------
|
||
|
||
exports.Logger = Logger;
|