Журнал событий. Принципы работы логирования в Cotonti

Каждое действие, которое Cotonti считает важным, автоматически записывается в отдельную таблицу в базе данных. Как это работает работает и как этим пользоваться?

0 Опубликована Опубликовано в: Cotonti Siena CMF > Cotonti - чтиво. Документация.
Журнал событий. Принципы работы логирования в Cotonti

Журнал событий (Логи) в админ-панели

Где найти: Админ-панель → Other (Прочее) → Log (Журнал).

Что это такое

Это история всех значимых действий на сайте. Каждое действие, которое Cotonti считает важным, автоматически записывается в отдельную таблицу в базе данных. Админ-страница просто показывает эту таблицу удобным списком.

Представьте себе бортовой журнал корабля или камеру наблюдения в магазине: кто, когда, что сделал, с какого адреса. Здесь — то же самое для сайта.

Что попадает в журнал

  • Вход и выход пользователей. Кто зашёл, кто вышел, кто пытался зайти, но не смог (неверный пароль).
  • Восстановление пароля. Успешные и неудачные попытки сброса.
  • Регистрация. Неверные ссылки подтверждения, попытки взлома.
  • Действия с пользователями. Редактирование, удаление профиля.
  • Действия со статьями и страницами. Создание, редактирование, удаление, отправка на модерацию.
  • Действия с форумом. Создание, редактирование, удаление тем и постов. Закрытие, закрепление, перенос.
  • Действия с категориями. Создание, изменение, удаление.
  • Действия с товарами — если модуль магазина включён и в его коде есть логирование.
  • Действия с расширениями. Установка, настройка, удаление плагинов и модулей.
  • Действия с правами доступа. Кому что разрешили, кому запретили.
  • Действия с доп. полями. Создание, изменение, удаление пользовательских полей.
  • Ошибки и подозрительные действия. Попытки нарушить правила, неверные попытки загрузить файлы, подозрительная активность.
  • Действия плагинов. Например, «чистильщик» (cleaner) отчитывается об удалённых записях, IPsearch — о поиске по IP.

Какие данные хранятся по каждому событию

  • Номер записи — идентификатор.
  • Дата и время (в GMT).
  • IP-адрес — с какого компьютера пришло действие. Есть поиск по IP, если включён плагин.
  • Пользователь — кто именно (логин или имя).
  • Группа события — категория: администрация, форум, пользователи, безопасность, плагины и т. д.
  • Текст события — что именно произошло, простыми словами.

Как этим пользоваться

Фильтр по группе. Вверху страницы выпадающий список. Можно оставить «Все», а можно смотреть только форум, только действия администраторов, только события безопасности, только пользователей, только плагины.

Просмотр. Список отсортирован сверху вниз — самые новые записи вверху.

Переход по IP. Если включён плагин поиска по IP, можно кликнуть на IP-адрес и увидеть все действия с этого адреса.

Переход по группе. Клик по названию группы отфильтрует журнал по этой группе.

Постраничная навигация. Внизу счётчик: сколько всего записей, сколько на странице. Всё разбито по страницам.

Кнопка «Очистить всё»

Кнопка Очистить (Purge) — удаляет все записи журнала. Действие необратимое. Только для администратора.

Кому это нужно

  • Администратору сайта — чтобы понять, кто и что делал, найти виновника беспорядка, разобраться с инцидентом.
  • При подозрении на взлом — когда непонятно, откуда взялись изменения, журнал покажет: с какого IP, под каким пользователем, что именно было сделано.
  • При разбирательстве между пользователями — кто прав, кто виноват, кто что удалил, кто кого редактировал.
  • Для аудита действий модераторов — контролировать, что модераторы не превышают полномочия.

Что важно понимать

Журнал можно отключить. В настройках есть уровень логирования. Если стоит none — ничего не пишется. Если all — пишется всё. Можно также включать логирование только для отдельных групп или только для отдельных типов событий внутри группы.

Записи хранятся не вечно. Встроенный плагин «чистильщик» может автоматически удалять старые записи журнала — например, старше 90 дней. Настраивается отдельно.

Записи нельзя редактировать. Можно только читать или полностью очистить. Это сделано специально: журнал — это доказательство, а доказательство нельзя править.

Тексты на английском. Сами тексты событий («Logged in», «Deleted post #123») хранятся в том виде, в котором их записал код. Перевод не предусмотрен.

Короткое резюме

Страница «Логи» в админке — это памятка о прошлом сайта: кто что делал, когда, откуда, чем закончилось. Инструмент для разбора инцидентов, контроля модераторов и понимания, что вообще происходит на проекте. Если что-то «само сломалось» или «само появилось» — журнал расскажет, кто и как это сделал, но разумеется, если эти данные приходят в инструмент логирования, через вызов функции cot_log().

Технические аспекты и принципы работы логирования в Cotonti

Материал построен на анализе исходного кода Cotonti: функции cot_log(), cot_log_import(), файла system/admin/admin.log.php, шаблона admin.log.tpl, схемы таблицы cot_logger из setup/install.sql, патча patch_0.9.22.sql, а также всех 83 вызовов cot_log в 35 файлах ядра, модулей и плагинов.


1. Где живёт логирование в коде

Точка входа — единственная функция. Всё логирование в Cotonti идёт через одну функцию:

function cot_log($text, $group = 'adm', $type = '', $status = '', $extra_data = [])

Она объявлена в system/functions.php (строка ~3801). Никаких других публичных API для записи в журнал в ядре нет. Любой модуль или плагин пишет событие только вызовом cot_log().

Обёртка для ошибок импорта. Есть вспомогательная функция cot_log_import() (строка ~3906):

function cot_log_import($s, $e, $v, $o)
{
    $text = ...
    cot_log($text, 'sec', 'input', 'error');
}

Она используется самим ядром для записи ошибок при импорте входящих данных (форматы $s, $e, $v, $o — source, expected, value, output). Хардкод группы sec (безопасность), типа input, статуса error.


2. Параметры cot_log()

ПараметрТипПо умолчаниюНазначение
$textstringобязательныйОписание события. Обрезается до 255 символов через cot_cutstring() перед записью.
$groupstring'adm'Группа события. Ключ для фильтрации в админке. Может быть кодом расширения (forums, page, users) или служебной группой (adm, sec).
$typestring''Тип события внутри группы: add, edit, delete, login, logout, passrecover и т. д. Участвует в логике отбора по уровню логирования.
$statusstring''Статус. По соглашению — 'done' (успех) или 'error' (ошибка). Жёстко не валидируется.
$extra_dataarray[]Массив «поле → значение» для дополнительных полей таблицы cot_logger, если они созданы как extrafields.

Возвращает: bool. true — запись вставлена, false — запись не сделана (например, логирование отключено).


3. Логика принятия решения о записи (главное)

Прежде чем что-либо записать, функция проверяет, надо ли это делать. Это самая нетривиальная часть.

Шаг 1. Глобальная проверка возможности. Если не инициализированы Cot::$cfg, Cot::$db, или не задан Cot::$cfg['loggerlevel'], или он равен 'none' — выход, возврат false.

Шаг 2. Разбор значения loggerlevel.

  • Если loggerlevel === 'all' — логируется всё.
  • Если loggerlevel содержит '+' — строка разбивается по + в массив $loggerlevel.
  • Иначе — $loggerlevel превращается в массив с единственным элементом.

Шаг 3. Учёт настройки уровня для конкретного расширения. Если в $loggerlevel присутствует литерал 'ext' — включается режим «доверять настройкам расширений»:

  • Обходятся все активные плагины и модули (из $cot_plugins_enabled и $cot_modules).
  • Ищется то расширение, чей код совпадает с $group.
  • Если найдено — берётся его параметр $cfg[$ext]['loggerlevel']:
    • 'none' — не логируется;
    • 'all' — логируется;
    • содержит '+' — разбивается, и если $type входит в список — логируется;
    • совпадает ровно с $type — логируется;
    • если параметр у расширения отсутствует вообще — логируется по умолчанию.
  • После первого совпадения цикл прерывается.

Шаг 4. Прямое совпадение группы. Если после шага 3 запись всё ещё не разрешена, дополнительно проверяется: входит ли $group напрямую в массив $loggerlevel. Если да — логируется.

Шаг 5. Если разрешено — сбор и вставка данных.

Практический вывод: уровень логирования — это совокупность глобального loggerlevel и per-extension настройки. Глобально можно поставить adm+sec+users (логировать только административные, безопасности и пользовательские события) или all (всё), или ext (доверить решение каждому расширению). Расширения, в свою очередь, могут указать none, all или конкретный список типов через +.


4. Что именно записывается в БД

Массив $loger_data формируется перед вставкой:

ПолеИсточникОграничение
log_date(int) Cot::$sys['now']UNIX timestamp
log_ipCot::$usr['ip'], либо пустая строкаДо 64 символов (после патча 0.9.22 — под IPv6)
log_uidCot::$usr['id'], либо 0INT
log_nameCot::$usr['name'], либо пустая строкаVARCHAR
log_uricot_cutstring(Cot::$sys['uri_curr'], 255)До 255 символов
log_group$group, либо '0' как валидное значениеVARCHAR
log_type$typeVARCHAR
log_status$statusVARCHAR
log_textcot_cutstring($text, 255)До 255 символов

Значение log_group === '0' приравнено к валидному (не пустому). Всё остальное пустое заменяется пустыми строками.


5. Extrafields для таблицы cot_logger

Таблица журнала поддерживает дополнительные поля. Логика:

if (!empty($extra_data)) {
    if (!empty(Cot::$extrafields[Cot::$db->logger])) {
        foreach (Cot::$extrafields[Cot::$db->logger] as $exfld) {
            if (isset($extra_data[$exfld['field_name']])) {
                $loger_data['log_' . $exfld['field_name']] = $extra_data[$exfld['field_name']];
            }
        }
    }
}

Что это значит на практике:

  • В $extra_data можно передать произвольный массив.
  • Ядро отфильтрует его — оставит только те ключи, которые объявлены как extrafields для таблицы cot_logger.
  • Ключ в БД получит префикс log_: extra_data['foo'] → колонка log_foo.
  • Если extrafields не определены — массив молча игнорируется.

То есть без предварительного создания extrafields через админку передача $extra_data не даст эффекта.


6. Хук loger.event

Перед вставкой вызывается хук:

foreach (cot_getextplugins('loger.event') as $pl) {
    include $pl;
}

Имя хука — loger.event, не logger.event. Это историческая опечатка в ядре, менять её нельзя — сломает совместимость с уже написанными плагинами.

Через этот хук плагин может изменить массив $loger_data перед вставкой в БД (добавить/заменить поля). Вставка происходит после хука — то есть все правки будут записаны.

Пример плагина логирования с хукингом в loger.event

Вот пример реальной бизнес-логики, которую можно подключить через хук loger.event. Этот хук вызывается внутри функции cot_log() перед вставкой записи в базу данных и позволяет плагину вмешаться в процесс логирования: изменить данные, добавить свои поля или выполнить побочные действия (например, уведомления).

Где находится файл-обработчик

В Cotonti обработчики хуков — это PHP-файлы в папке плагина, имя которых соответствует коду хука. По соглашению, для хука loger.event файл называется так:

plugins/nameplug/nameplug.loger.event.php

Плагин должен быть активирован и зарегистрировать этот хук в своём файле nameplug.loger.event.php (Hooks=loger.event).

Пример бизнес-логики: «Уведомление о критических событиях»

Задача: при логировании любого события с группой sec и статусом error отправлять уведомление в Telegram-чат администратора. Это позволит мгновенно узнавать о попытках взлома, ошибках загрузки файлов и других инцидентах безопасности, не дожидаясь, пока администратор зайдёт в админку.

Файл: plugins/securityalert/securityalert.loger.event.php

<?php
/**
 * Обработчик хука loger.event.
 * Отправляет уведомление в Telegram при логировании
 * событий безопасности с статусом "error".
 *
 * Доступные переменные из cot_log():
 *   $loger_data  — массив, который будет вставлен в БД
 *   $text        — текст события
 *   $group       — группа события (adm, sec, users, forums, ...)
 *   $type        — тип события (login, passrecover, file_upload, ...)
 *   $status      — статус (done, error)
 *   $extra_data  — дополнительные данные (массив)
 */

defined('COT_CODE') or die('Wrong URL.');

// 1. Проверяем, что нас интересует именно событие безопасности с ошибкой
if ($group !== 'sec' || $status !== 'error') {
    return; // ничего не делаем — хук просто завершается
}

// 2. Формируем текст уведомления
$alertText = "🚨 COTONTI SECURITY ALERT 🚨\n"
           . "Тип: {$type}\n"
           . "Пользователь: " . ($loger_data['log_name'] ?: 'Гость') . " (ID: {$loger_data['log_uid']})\n"
           . "IP: {$loger_data['log_ip']}\n"
           . "URI: {$loger_data['log_uri']}\n"
           . "Текст: {$loger_data['log_text']}\n"
           . "Дата: " . date('Y-m-d H:i:s', $loger_data['log_date']);

// 3. Отправляем в Telegram (пример через file_get_contents — без внешних библиотек)
$botToken = '123456789:ABCdefGhIJKlmNoPQRsTUVwxyZ'; // токен бота
$chatId   = '-1001234567890';                        // ID чата администратора

$url = "https://api.telegram.org/bot{$botToken}/sendMessage";
$postData = [
    'chat_id' => $chatId,
    'text'    => $alertText,
    'parse_mode' => 'HTML'
];

$options = [
    'http' => [
        'method'  => 'POST',
        'header'  => "Content-Type: application/x-www-form-urlencoded\r\n",
        'content' => http_build_query($postData),
        'timeout' => 3,
    ],
];

$context = stream_context_create($options);
@file_get_contents($url, false, $context); // @ — чтобы ошибка сети не сломала логирование

Что делает этот код

  1. Фильтрует события. Плагин реагирует только на группу sec со статусом error. Все остальные записи журнала (успешный вход, создание темы и т. д.) игнорируются, чтобы не заваливать чат лишним.
  2. Собирает контекст. Использует $loger_data, который уже сформирован ядром: дата, IP, UID, имя, URI, текст. Это те же данные, что попадут в базу.
  3. Отправляет уведомление. Через Telegram Bot API. Администратор получает сообщение в реальном времени.

Другие сценарии для loger.event

  • Обогащение журнала. Если в $extra_data переданы поля, которых нет в extrafields, но нужны для аналитики, можно добавить их в $loger_data вручную, даже если они не описаны как extrafields (но тогда колонка в БД должна существовать).
  • Блокировка спама. Если за последние 10 секунд уже было 5 записей с одинаковым $group и $type от одного IP, можно не отправлять уведомление повторно (антифлуд). Реализуется через чтение последних записей из cot_logger внутри хука.
  • Маршрутизация в файл. Вместо БД (или вместе с ней) писать событие в отдельный файл logs/security.log для быстрого доступа без захода в админку.
  • Расширенная фильтрация. Если стандартный loggerlevel не позволяет гибко отсекать события (например, нужно логировать только определённые комбинации group + type + status), хук даёт полный контроль.

Важные нюансы

  • Хук не может отменить запись. cot_log() вызывает loger.event перед Cot::$db->insert, но не проверяет возвращаемое значение. Плагин может изменить данные, но не предотвратить вставку.
  • Переменные доступны благодаря include. Файл подключается внутри функции cot_log(), поэтому все её локальные переменные ($loger_data, $text, $group, $type, $status, $extra_data) видны в обработчике.
  • Порядок вызова. Плагины подключаются в порядке, определённом их настройками. Если несколько плагинов слушают loger.event, они выполнятся последовательно, каждый может изменить $loger_data, и следующий увидит уже изменённые данные.
  • Ошибки в хуке не логируются автоматически. Если ваш обработчик бросит исключение или вызовет фатальную ошибку, это может прервать выполнение cot_log() и запись в журнал не произойдёт. Обязательно оборачивайте потенциально опасные операции (сетевые запросы, файловые операции) в try/catch или подавляйте ошибки через @.

Итог

Хук loger.event — это точка расширения, которая позволяет плагину вмешаться в момент формирования записи журнала. Типовые задачи: уведомления, дополнительная фильтрация, обогащение данных, маршрутизация в другие хранилища. Пример выше показывает, как из Cotonti-журнала сделать систему оперативного оповещения об инцидентах безопасности.


7. cot_log_import() — обёртка для ошибок импорта

function cot_log_import($s, $e, $v, $o)

Вызывается из system/functions.php (строка 537) при неудачном импорте входящих данных. Жёстко пишет в группу sec с типом input и статусом error. Само событие — «что ожидалось, что пришло, какое значение, откуда».

Это фактически отдельный «канал» — все ошибки импорта независимо от своего источника попадают в раздел «Безопасность → Input».


8. Схема таблицы cot_logger

Создаётся в setup/install.sql:

DROP TABLE IF EXISTS `cot_logger`;
CREATE TABLE `cot_logger` (
    ...
);

Изменения в патче 0.9.22:

ALTER TABLE `cot_logger` MODIFY `log_ip` varchar(64) DEFAULT '';

Поле log_ip расширено до 64 символов — под IPv6. До этого было короче и не вмещало адреса нового формата.

Первичный ключ: log_id (автоинкремент). Используется в ORDER BY log_id DESC для сортировки «сначала новые».

Индексы: в исходной схеме на log_group отдельный индекс не создаётся, фильтрация идёт обычным сравнением.


9. Админ-страница журнала

Файл: system/admin/admin.log.php (в исходниках Cotonti это system/admin/ или аналогичный путь).

Проверка прав. Страница требует:

list(Cot::$usr['auth_read'], Cot::$usr['auth_write'], Cot::$usr['isadmin']) = cot_auth('admin', 'a');
cot_block(Cot::$usr['auth_read']);

То есть доступ к разделу admin / a в группе прав, и в первую очередь — право на чтение. Кнопка очистки доступна только при $usr['isadmin'].

Хлебные крошки: Other → Log.

Заголовок и справка: adm_log_desc, Log.

Пагинация. Размер страницы определяется конфигом:

$maxrowsperpage = (is_int(Cot::$cfg['maxrowsperpage']) && Cot::$cfg['maxrowsperpage'] > 0 || ctype_digit(Cot::$cfg['maxrowsperpage'])) ?
    Cot::$cfg['maxrowsperpage'] : 15;

Если в конфиге maxrowsperpage валидное целое > 0 — используется оно, иначе 15.

Фильтр по группам. В шаблоне выводится выпадающий список с вариантами:

$log_groups = [
    'all' => Cot::$L['All'],
    'def' => Cot::$L['Default'],
    'adm' => Cot::$L['Administration'],
    'for' => Cot::$L['Forums'],
    'sec' => Cot::$L['Security'],
    'usr' => Cot::$L['Users'],
    'plg' => Cot::$L['Plugins']
];

Важный факт. Список вариантов не совпадает с реальными значениями log_group в БД. В коде ядра и расширений записываются полные имена групп: users, forums, page, i18n, trashcan, sec, adm. А в фильтре используются сокращения: for, usr, plg. Значит:

  • Фильтр «Administration» (adm) — работает.
  • Фильтр «Security» (sec) — работает.
  • Фильтр «Forums» (for) — не найдёт ничего, потому что реальные записи имеют группу forums, а не for.
  • Фильтр «Users» (usr) — не найдёт ничего, потому что реальные записи имеют группу users, а не usr.
  • Фильтр «Plugins» (plg) — не найдёт ничего, потому что плагины пишут свои коды групп (i18n, trashcan, referers, pm).
  • Фильтр «Default» (def) — тоже не совпадает ни с одной реальной группой.

Это фактическая особенность стокового Cotonti: работают только «Все» и «Администрация» и «Безопасность». Остальные фильтры из коробки бесполезны. Разработчику темы или плагина это полезно знать, если он собирается улучшать страницу.

Запрос данных.

  • При $n === 'all':

    SELECT * FROM cot_logger WHERE 1 ORDER by log_id DESC LIMIT $d, $maxrowsperpage
    
  • Иначе:

    SELECT * FROM cot_logger WHERE log_group='$n' ORDER by log_id DESC LIMIT $d, $maxrowsperpage
    

Подстановка $n не параметризована — фильтр берётся из GET/POST и напрямую вставляется в SQL. Потенциальный источник SQL-инъекции при отсутствии дополнительной санитизации в cot_import_pagenav и импорте $n. Стоит проверять версию ядра перед использованием на публичном сайте.

Теги строки (MAIN.LOG_ROW).

ТегИсточник
ADMIN_LOG_ROW_LOG_IDlog_id
ADMIN_LOG_ROW_DATEcot_date('datetime_medium', log_date)
ADMIN_LOG_ROW_DATE_STAMPсырой timestamp
ADMIN_LOG_ROW_URL_IP_SEARCHесли активен плагин ipsearch — ссылка на поиск по IP
ADMIN_LOG_ROW_LOG_IPIP
ADMIN_LOG_ROW_LOG_NAMEимя пользователя
ADMIN_LOG_ROW_URL_LOG_GROUPссылка на фильтр по этой группе
ADMIN_LOG_ROW_LOG_GROUPчеловекочитаемое имя группы, если найдено в $log_groups, иначе — как есть
ADMIN_LOG_ROW_LOG_TEXThtmlspecialchars(log_text) — XSS-защита применена

Хуки страницы:

  • admin.log.first — в начале скрипта, до purge.
  • admin.log.purge — внутри блока очистки, до TRUNCATE.
  • admin.log.loop — внутри обхода строк, для добавления своих тегов.
  • admin.log.tags — в конце, после назначения общих тегов.

10. Очистка журнала

Кнопка «Очистить всё» (ADMIN_LOG_URL_PRUNE) видна только $usr['isadmin'] (в стоковом шаблоне) и вызывает:

$db->query("TRUNCATE $db_logger") ? cot_message('adm_ref_prune') : cot_message('Error');

TRUNCATE, а не DELETE. Это значит:

  • Счётчик автоинкремента log_idсбрасывается в 1.
  • Записи удаляются безвозвратно и без прохождения через loger.event.
  • Операция требует наличия cot_check_xg() (токен безопасности формы).

Автоудаление старых записей. Встроенный плагин cleaner при каждом запуске (по cron или вручную) удаляет записи старше Cot::$cfg['plugin']['cleaner']['logprune'] дней. При этом он сам пишет в журнал запись об этом:

cot_log('Cleaner plugin deleted ' . Cot::$db->affectedRows . ' log entries older than ...', 'adm', 'log', 'delete');

То есть факт автоочистки фиксируется отдельно — не стирается вместе со старыми записями (если, конечно, сам не попадёт под следующую чистку).


11. Полный список групп и типов в реальном коде

Собрано из 83 вызовов cot_log() в ядре и расширениях.

Группа adm (административные действия):

  • тип update / статус error — ошибка получения обновления с cotonti.com.
  • тип user_groups_rights / статусы add, update — управление правами групп.
  • тип extrafield / статусы add, update, delete — управление extrafields.
  • тип structure / статусы add, update — управление структурой (категориями).
  • тип log / статус delete — запись от cleaner о чистке журнала.

Группа sec (безопасность):

  • тип input / статус error — ошибки импорта (cot_log_import).
  • тип hammer / статус error — IP-бан за флуд.
  • тип file_upload / статус error — несоответствие MIME, ошибка проверки загрузки.
  • тип user / статус error — неверная ссылка валидации при регистрации.
  • тип page / статус error — прямой доступ к непубличной или невалидированной странице.
  • тип forums / статус error — попытка редактирования поста без прав.

Группа users:

  • тип login / статус error — неудачный вход.
  • тип logout / статус done — выход.
  • тип passrecover / статус error — неудачное восстановление пароля.
  • тип edit / статус done — редактирование пользователя.
  • тип delete / статус done — удаление пользователя.
  • тип clear / статус done — очистка неактивных пользователей плагином cleaner.

Группа forums:

  • тип lock topic / статус done — закрытие/открытие темы (одно событие с разным текстом).
  • тип pin topic / статус done — закрепление.
  • тип announce topic / статус done — объявление.
  • тип bump topic / статус done — поднятие.
  • тип made topic / статус done — сделано приватным.
  • тип reset topic / статус done — сброс настроек темы.
  • тип delete post / статус done — удаление поста.
  • тип delete topic / статус done — удаление темы.

Группа page (модуль страниц):

  • тип add / статус done.
  • тип edit / статус done.
  • тип delete / статус done.
  • тип unvalidated / статус done.

Группа pm — плагин cleaner: типы delete / статус done (удаление непрочитанных, неархивированных, старых сообщений).

Группа referers — плагин cleaner: тип delete / статус done.

Группа i18n (плагин мультиязычности):

  • тип page / статусы add, edit, delete.
  • тип structure / статусы add, edit, delete.

Группа trashcan:

  • тип restore / статусы done, error.
  • тип prune / статус done.

Группа ext (для плагинов, которые не имеют собственного кода как модуля):

  • тип search / статус error — ошибки поиска.

Итого: групп не 7, как в выпадающем фильтре админки, а как минимум 11: adm, sec, users, forums, page, i18n, trashcan, referers, pm, ext, и плюс свои группы добавляют сторонние плагины (в анализе видны marketreviews, payordersmarket и другие — по типовым конвенциям).


12. Что важно знать разработчику

  1. cot_log() не бросает исключений и не логирует собственные ошибки. Если запись не прошла из-за настроек — вернётся false, всё. Если упала БД — увидите ошибку уровня БД от Cot::$db->insert, но cot_log её не перехватывает.
  2. Группа должна соответствовать коду расширения, если вы хотите, чтобы per-extension настройка loggerlevel работала. Например, плагин mychat должен писать cot_log($msg, 'mychat', ...), а не cot_log($msg, 'plugins', ...).
  3. Тип и статус — условные соглашения. Ядро использует 'done' и 'error', но жёсткой валидации нет. Используйте эти же строки — фильтры и сторонние плагины могут на них ориентироваться.
  4. $text обрезается до 255 символов — длинные описания теряют хвост. Если нужно больше — используйте extrafields.
  5. Хук называется loger.event, не logger.event. Не опечатайтесь.
  6. Параметр $extra_data бесполезен без заранее объявленных extrafields для таблицы cot_logger.
  7. Очистка (purge) стирает всё, включая счётчик log_id. Если у вас есть внешние ссылки на конкретные ID записей журнала — они сломаются.
  8. Фильтры по группам в стоковой админке неполные. Для реального просмотра истории проекта пользуйтесь фильтром «All» и глазами, либо перепишите список $log_groups в своём admin.log.php под фактические группы, используемые на вашем сайте.

13. Связь с архитектурой Cotonti

  • Логирование инициализируется после поднятия ядра — до этого cot_log молча выходит.
  • Вставка идёт через абстрактный слой Cot::$db (Cot::$db->insert), а не через прямой SQL. Значит, соблюдается единая точка аудита SQL-операций и работает буферизация, если она включена.
  • Хук loger.event встроен в общий механизм хуков Cotonti — порядок подключения плагинов к этому хуку регулируется штатной сортировкой по весам.
  • cleanerкак штатный плагин показывает правильный подход: расширение само решает, что писать в журнал, и само же сообщает, когда чистит чужие записи.

Итог

Логирование в Cotonti — это одна функция с одной таблицей и одним хуком. Вся гибкость достигается за счёт трёх параметров: group, type, status, плюс глобальный и порасширенческий loggerlevel. Реализация простая и предсказуемая, но с известными ограничениями: фиксированная длина текста, неполный список фильтров в стоковой админке, отсутствие параметризации в фильтре SQL. Для администратора это готовый аудит действий, для разработчика — единая точка отчётности, которую не нужно изобретать заново.

Комментарии отсутствуют
Добавление комментариев доступно только зарегистрированным пользователям
webitproff
webitproff

Оффлайн

Последняя авторизация: 12.10.2026 18:21

Обо мне кратко
Поддержка и разработка веб-проектов на CMF Cotonti: приватные мессенджеры через сайт, открытые и закрытые небольшие социальные сети, торговые площадки и маркетплейсы, портал биржи фриланса и услуг, каталоги товаров оптовых поставщиков, дропшиппинг-платформы, интернет-магазины и многое другое.
Смотреть разработки и скачать
Публичное портфолио моих работ и разработок
Телеграм для сообщений
@webitproff
Телеграм-канал
@s/aBuyFILE
Публичный профиль
  • Дата размещения 11.10.2026 13:52
  • Обновлено 11.10.2026 13:52

Похожие страницы

Изучаем принцип работы хуков в Cotonti CMF
1 Хуки (hooks) в Cotonti CMF — это механизм расширений, который позволяет подключать код в различные точки жизненного
Описание работы метода text() в Cotonti CMF
2 Описание работы метода text() в Cotonti CMF Cotonti — это мощная система управления контентом (CMS), используемая для
Именование хуков, объявление и чтение. Принципы и правила. Обзорное руководство
3 ВведениеДанное руководство является результатом анализа исходного кода Cotonti Siena, в частности, функций
AJAX-части плагинов в Cotonti Siena. Принципы и примеры
4 AJAX-части плагинов в Cotonti Siena техническое руководство (фактическое описание, основано по-большей части из старой
Cotonti Siena CMF • 10.03.2026 08:45 webitproff