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" для логирования координат

См. также