Вход

Просмотр полной версии : [Вопрос] Система логов



PawnoNoob
13.05.2018, 06:24
Приветствую! Спрошу кратко: как лучше реализовать систему логов? На MySQL или на файлах?
P.S.: планируется вести лог почти каждого действия.

ziggi
13.05.2018, 10:31
Я бы сделал на MySQL, потому что можно реализовать логи с категориями, сделать удобный поиск и просмотр через веб-интерфейс с различными фильтрами.

PawnoNoob
13.05.2018, 16:00
Я бы сделал на MySQL, потому что можно реализовать логи с категориями, сделать удобный поиск и просмотр через веб-интерфейс с различными фильтрами.

Хм, я тоже хотел бы сделать именно в базе данных, но не будет ли никаких "лагов" при большом количестве игроков?
И ещё вопрос: как бы Вы порекомендовали сделать саму структуру базы данных для логирования действий администрации и игроков в целом?

Daniel_Cortez
13.05.2018, 17:34
Хм, я тоже хотел бы сделать именно в базе данных, но не будет ли никаких "лагов" при большом количестве игроков?
Если запрос асинхронный, основной поток сервера не блокируется.


И ещё вопрос: как бы Вы порекомендовали сделать саму структуру базы данных для логирования действий администрации и игроков в целом?
В общих чертах всё должно выглядеть примерно так:


CREATE TABLE `adm_actions`(
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
`datetime` DATETIME NOT NULL,
`accid` INT UNSIGNED NOT NULL,
`action` INT UNSIGNED NOT NULL,
`extra` TEXT NOT NULL,
PRIMARY KEY (`id`)
);



enum eAdminAction
{
ADMIN_ACTION_LOGIN,
ADMIN_ACTION_LOGOUT,
ADMIN_ACTION_KICK,
ADMIN_ACTION_BAN
};

LogAdminAction(playerid, eAdminAction:action, const extra[] = "")
{
new str[256];
static const fmt[] =
"INSERT INTO `adm_actions` (`datetime`,`accid`,`action`,`extra`) VALUES (NOW(),'%d','%d','%e')";
mysql_format(mysql_connection, str, sizeof(str), fmt, player_info[playerid][pAccoundID], _:action, extra);
return mysql_tquery(mysql_connection, str);
}

CMD:kick(playerid, params[])
{
new targetname[MAX_PLAYER_NAME + 1];
// ...
LogAdminAction(playerid, ADMIN_ACTION_KICK, targetname);
return SendClientMessage(/* ... */);
}


Или можно просто дампить строки, но это слишком "топорный" метод: мало того, что строки будут занимать больше пространства в БД, так ещё и не получится сделать вывод всех действий от конкретного админа или за конкретную дату (разве что юзать строковые функции, но это будет уже не так просто, да и не факт, что надёжно).

StevenH
13.05.2018, 17:34
Хм, я тоже хотел бы сделать именно в базе данных, но не будет ли никаких "лагов" при большом количестве игроков?
И ещё вопрос: как бы Вы порекомендовали сделать саму структуру базы данных для логирования действий администрации и игроков в целом?

Лагов не будет, на всех крупных проектах именно так и сделано (на адвансе например это отчеты, на аризоне почти каждое действие логируется в БД)

PawnoNoob
13.05.2018, 18:05
Если запрос асинхронный, основной поток сервера не блокируется.


В общих чертах всё должно выглядеть примерно так:


CREATE TABLE `adm_actions`(
`id` INT UNSIGNED NOT NULL,
`datetime` DATETIME NOT NULL,
`accid` INT UNSIGNED NOT NULL,
`action` INT UNSIGNED NOT NULL,
`extra` TEXT NOT NULL,
INDEX `id_index` (`id`)
);



enum eAdminAction
{
ADMIN_ACTION_LOGIN,
ADMIN_ACTION_LOGOUT,
ADMIN_ACTION_KICK,
ADMIN_ACTION_BAN
};

LogAdminAction(playerid, eAdminAction:action, const extra[] = "")
{
new str[256];
static const fmt[] =
"INSERT INTO `adm_actions` (`datetime`,`accid`,`action`,`extra`) VALUES (NOW(),'%d','%d','%e')";
mysql_format(mysql_connection, str, sizeof(str), fmt, player_info[playerid][pAccoundID], _:action, extra);
return mysql_tquery(mysql_connection, str);
}

CMD:kick(playerid, params[])
{
new targetname[MAX_PLAYER_NAME + 1];
// ...
LogAdminAction(playerid, ADMIN_ACTION_KICK, targetname);
return SendClientMessage(/* ... */);
}


Или можно просто дампить строки, но это слишком "топорный" метод: мало того, что строки будут занимать больше пространства в БД, так ещё и не получится сделать вывод всех действий от конкретного админа или за конкретную дату (разве что юзать строковые функции, но это будет уже не так просто, да и не факт, что надёжно).

Хм, появилось ещё несколько вопросов:
1. Почему именно "_:action"? (я про нижнее подчёркивание и двоеточние :wizard:) Что это даёт?
2. Параметр "eAdminAction:action" возвращает порядковый номер из enum?
3. В каких случаях используется mysql_format и mysql_tquery? Просто читал в одной из статей, что mysql_format защищает от инъекций и т.д.
4. Что за "асинхронные" запросы?

Daniel_Cortez
13.05.2018, 18:56
Почему именно "_:action"? (я про нижнее подчёркивание и двоеточние :wizard:) Что это даёт?
Потому что функция mysql_format ожидает только аргументы, имеющие теги "_" или "Float" (см. теги перед "..." ниже).

native mysql_format(MySQL:handle, output[], max_len, const format[], {Float,_}:...);

Если не понимаете о чём я - просто удалите "_:" из того кода и попробуйте скомпилировать - словите варнинг о несоответствии тегов.


Параметр "eAdminAction:action" возвращает порядковый номер из enum?
Это не функция, чтобы что-то возвращать. Если вы про тег в аргументе "action", то да, он означает, что его значением должна быть одна из констант из enum eAdminAction.


В каких случаях используется mysql_format и mysql_tquery? Просто читал в одной из статей, что mysql_format защищает от инъекций и т.д.
mysql_tquery() - отправляет асинхронные запросы к БД.
mysql_format(), как можно понять из примера выше, отвечает за форматирование строки с запросом. Основная причина её использования вместо format() - защита от инъекций. И хотя польза такой защиты есть только тогда, когда в форматной строке есть спецификатор "%s" (или "%e" в случае с mysql_format()), ИМХО, проще по привычке всегда использовать для запросов именно эту функцию. Кроме того, в последних версиях SA-MP в format() тоже появился аналогичный спецификатор "%q", но он больше специфичен для SQLite и по идее может неправильно работать с запросами для MySQL.


Что за "асинхронные" запросы?
Запросы, выполняемые в отдельном потоке и не блокирующие главный поток, в котором выполняются скрипты и обрабатываются серверные данные.
Если запрос не асинхронный, он выполняется в главном потоке, в котором всё зависает, пока не выполнится запрос.


И да, я немного обновил создание БД в предыдущем посте, в первоначальном варианте не работало автозаполнение поля "id".

PawnoNoob
13.05.2018, 23:51
Потому что функция mysql_format ожидает только аргументы, имеющие теги "_" или "Float" (см. теги перед "..." ниже).

native mysql_format(MySQL:handle, output[], max_len, const format[], {Float,_}:...);

Если не понимаете о чём я - просто удалите "_:" из того кода и попробуйте скомпилировать - словите варнинг о несоответствии тегов.


Это не функция, чтобы что-то возвращать. Если вы про тег в аргументе "action", то да, он означает, что его значением должна быть одна из констант из enum eAdminAction.


mysql_tquery() - отправляет асинхронные запросы к БД.
mysql_format(), как можно понять из примера выше, отвечает за форматирование строки с запросом. Основная причина её использования вместо format() - защита от инъекций. И хотя польза такой защиты есть только тогда, когда в форматной строке есть спецификатор "%s" (или "%e" в случае с mysql_format()), ИМХО, проще по привычке всегда использовать для запросов именно эту функцию. Кроме того, в последних версиях SA-MP в format() тоже появился аналогичный спецификатор "%q", но он больше специфичен для SQLite и по идее может неправильно работать с запросами для MySQL.


Запросы, выполняемые в отдельном потоке и не блокирующие главный поток, в котором выполняются скрипты и обрабатываются серверные данные.
Если запрос не асинхронный, он выполняется в главном потоке, в котором всё зависает, пока не выполнится запрос.


И да, я немного обновил создание БД в предыдущем посте, в первоначальном варианте не работало автозаполнение поля "id".

О как. Вот теперь всё встало на свои места. Огромное спасибо за понятное объяснение :clapping: