Skip to content

Описание и API ​

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

Состав мода ​

MPG_ModsLogger/
├── config.cpp                    # CfgPatches
└── Scripts/
    ├── 1_Core/enums.c            # enum MPG_ModsLoggerLevel (TRACE..FATAL) 
    ├── 3_Game/MPG_ModsLogger.c   # класс MPG_ModsLogger
    └── Common/defines.c          # #define MPG_MODS_LOGGER

Зачем 1_Core

enum MPG_ModsLoggerLevel объявлен в самом раннем скрипт-слое (engineScriptModule), чтобы компилироваться до всех остальных модулей. Без этого 3_Game-слой мода не подключается к компиляции и тип MPG_ModsLogger остаётся невидимым для потребителей.

Как это работает ​

Класс MPG_ModsLogger построен по фабричному паттерну: сам класс живёт в моде, а каждый мод-потребитель создаёт свой экземпляр с изолированными настройками и хранит его в собственном static ref. Несколько модов могут логировать параллельно - каждый в свой файл, со своим уровнем и директорией.

c
g_MY_Logger = new MPG_ModsLogger("MyMod", "$profile:MyMod/Logs", 3);

Конструктор:

  • открывает файл <logDir>/<modName>_Log.log;
  • создаёт директорию и архивную папку (рекурсивно, с поддержкой $profile:);
  • архивирует предыдущий лог (если файл уже существует);
  • запускает таймер CallLater(FlushBuffer, 500, true) - периодический сброс буфера.

Деструктор (~MPG_ModsLogger):

  • снимает таймер;
  • финальный flush буфера;
  • пишет Logger Closed: <timestamp>, закрывает файл.

Жизненный цикл экземпляра ​

Логгер создаётся в MissionServer.OnInit (после загрузки конфига потребителя - чтобы взять logLevel) и обнуляется в OnMissionFinish. Миссии перезапускаются без перезапуска процесса, поэтому обнуление обязательно - иначе статический ref переживёт миссию и укажет на «висячий» файловый дескриптор.

Возможности ​

Буферизация записи ​

Строки не пишутся на диск при каждом вызове. Накапливаются в m_Buffer и сбрасываются раз в 0.5с через CallLater (категория CALL_CATEGORY_SYSTEM). Это снижает нагрузку на диск при интенсивном логировании (например, TRACE при отладке спавна - сотни строк в секунду).

Исключение: сообщения уровней ERROR и FATAL сбрасывают буфер немедленно. Критичные ошибки не теряются при крахе процесса между плановыми flush'ами.

Шесть уровней логирования ​

УровеньЗначениеНазначениеFlush
TRACE1Детальная трассировка каждого шагаbuffered
DEBUG2Отладочная информацияbuffered
INFO3Информационные сообщения (по умолчанию)buffered
WARN4Предупрежденияbuffered
ERROR5Ошибкинемедленный
FATAL6Критические ошибкинемедленный

Фильтрация: сообщение пишется только если level >= m_MinLevel. m_MinLevel задаётся в конструкторе (обычно берётся из конфига потребителя). Это позволяет одной настройкой переключать детализацию: 1 - весь TRACE для глубокой отладки, 3 - рабочая информация, 5 - только ошибки.

Методы-предикаты IsTrace() … IsFatal() позволяют потребителю проверять, будет ли сообщение данного уровня записано, до формирования тяжёлой строки (экономия на форматировании).

Двойной вывод: файл + консоль ​

Каждое сообщение может дублироваться в серверную консоль (.RPT) через Print:

  • флаг consoleOutput (по умолчанию true) - включить/выключить дублирование;
  • consoleMinLevel (по умолчанию 4 = WARN) - начиная с какого уровня дублировать.

Типичный сценарий: файл хранит детальный лог (TRACE+), а в консоль падают только предупреждения и ошибки - чтобы не засорять .RPT.

Архивация и ротация ​

При старте логгера, если файл лога уже существует (от предыдущей сессии):

  • он копируется в LogArchives/ с именем <дата>_<время>.log;
  • оригинал удаляется, начинается новый лог.

Старые архивы автоматически чистятся: хранится не более maxArchives (по умолчанию 500). Лишние (самые старые по сортировке) удаляются. Параметр задаётся в конструкторе.

Формат строки лога ​

<часы:минуты:секунды> <ticktime> [<УРОВЕНЬ>] [<ModName>] <сообщение>

Пример:

14:23:07 00:15:42 [INFO ] [MPG_Spawner] Server started
14:23:09 00:15:44 [DEBUG] [MPG_Spawner] PointId: 5; spawn triggered
14:23:10 00:15:45 [WARN ] [MPG_Spawner] Low ammo: 3

Два временных штампа:

  • часы:минуты:секунды - реальное время (для сопоставления с другими логами);
  • ticktime (g_Game.GetTickTime()) - время с старта сервера (для измерения интервалов между событиями, не зависит от смены реального времени).

Временной префикс кэшируется по секунде: пересчёт строки происходит только когда секунда меняется, а не при каждой записи. При интенсивном логировании это экономит сотни вызовов GetHourMinuteSecond в секунду.

Хелпер FormatPosition ​

Статический метод MPG_ModsLogger.FormatPosition(vector) → "x, y, z". Удобно логировать координаты сущностей. Возвращает строку через запятую (читаемый формат), не через пробел - для лога, не для парсинга.

Преимущества ​

  • Изоляция модов. Каждый мод пишет в свой файл (MPG_Spawner_Log.log, MPG_BossHorde_Log.log), со своим уровнем и директорией. Логи не смешиваются в общем .RPT, искать проблемы конкретного мода проще.
  • Нет дублирования кода. До выделения в мод класс MPG_ModsLogger копипастился в каждый мод. При установке двух модов с копией возникал конфликт определений класса - сервер не компилировался. Мод решает это: класс определён ровно один раз.
  • Опциональность. Потребитель компилируется и работает без мода - через #ifdef MPG_ModsLogger. Если админ не поставил @MPG_ModsLogger, логирование деградируется до Print в .RPT. Нет жёсткой зависимости.
  • Производительность. Буферизация (0.5с) снижает I/O при высокочастотном логировании. Кэширование временного префикса по секунде убирает избыточные вызовы. Немедленный flush для ERROR/FATAL гарантирует сохранность критичных данных.
  • Управление объёмом. Архивация при каждом рестарте + ротация по maxArchives - логи не разрастаются бесконечно. На серверах с автоперезапуском это критично: без ротации файл мог бы вырасти до гигабайтов за неделю.
  • Двойной контекст времени. Реальное время + ticktime в одной строке позволяют одновременно сопоставлять события с внешними логами и измерять внутренние интервалы.

Особенности и ограничения ​

  • Только сервер. Мод - type = "mod", но логирование серверное (потребитель создаёт логгер в MissionServer.OnInit под IsServer()). Клиенту логгер не нужен.
  • Зависимость от g_Game. Таймер flush и GetTickTime требуют загруженного движка. Логгер нельзя создавать в статических инициализаторах или до старта миссии - только в MissionServer.OnInit.
  • Один файл на экземпляр. Перезапись (FileMode.WRITE), не дозапись. При пересоздании логгера старый контент переносится в архив - не теряется, но в активном файле всегда текущая сессия.
  • Порядок загрузки. @MPG_ModsLogger должен идти в списке модов до потребителей - чтобы символ MPG_ModsLogger и тип были доступны при компиляции.
  • Конфиг загружается до логгера. Конструктор конфига потребителя использует Print, а не логгер (логгера ещё нет в момент загрузки конфига). Логгер создаётся после - и берёт logLevel из уже загруженного конфига.

API ​

ЭлементСигнатураОписание
Конструкторnew MPG_ModsLogger(modName, logDir, minLevel = 3, consoleOutput = true, maxArchives = 500, consoleMinLevel = 4)Создаёт экземпляр, открывает файл, запускает таймер flush
Trace/Debug/Info/Warning/Error/Fatalvoid X(string message)Логирование с заданным уровнем
IsTrace/IsDebug/IsInfo/IsWarn/IsError/IsFatalbool X()Проверка, будет ли уровень записан (для отсечения тяжёлого форматирования)
FlushBuffervoid FlushBuffer()Принудительный сброс буфера (вызывается таймером, можно вручную)
FormatPositionstatic string FormatPosition(vector pos)"x, y, z" для логирования координат

См. также ​