февраля 2017
Категория:
MySQL
Настройка логирования и отладка MySQL запросов.
Логирование MySQL запросов настраивается в 2 этапа:
- Включаем само логирование;
- Указываем куда логировать запросы.
Почему удобно включать логирование с помощью MySQL команд:
- Не нужно дергать файл конфига MySQL /etc/mysql/my.cnf;
- После рестарта сервера — лог выключается.
Посмотреть включено ли логирование:
SELECT @@GLOBAL.general_log
SHOW GLOBAL VARIABLES LIKE '%log%';
SHOW GLOBAL VARIABLES WHERE Variable_name in ('version', 'log', 'general_log', 'general_log_file', 'log_output');
Включить/выключить логирование (1 — включить, 0 — выключить):
SET GLOBAL general_log = 1;
Логировать запросы в таблицу mysql.general_log:
SET GLOBAL general_log = 1;
SET GLOBAL log_output = 'TABLE';
Посмотреть логи запросов в таблице:
SELECT * FROM mysql.general_log;
Логировать запросы в файл /var/log/mysql.log (этот тип логирования включён по умолчанию):
SET GLOBAL log_output = 'FILE';
SET GLOBAL general_log_file = '/var/log/mysql.log';
Примечание
Файл не нужно создавать предварительно, но нужно дать права на запись в этот каталог!
По умолчанию лог-файл запросов: /var/lib/mysql/%USERNAME%log.
Включаем логирование SQL запросов в файл:
SET GLOBAL log_output = 'FILE';
SET GLOBAL general_log_file='dev-sql.log';
Внимание!
Глобальная системная переменная general_log_file содержит имя файла, а не полный путь к нему. Этот файл будет создан в каталоге /var/lib/mysql/ и по умолчанию именуется как <hostname>.log.
Примечание
Для просмотра значения глобальной системной переменной general_log_file — выполните запрос:
SHOW VARIABLES LIKE 'general_log_file';
Можно включить логирование и в таблицу, и в файл:
SET GLOBAL log_output = 'TABLE,FILE';
Для отключения лога:
SET GLOBAL log_output = 'NONE';
Пример включения логирования из PHP-скрипта и просмотр логов:
mysql_query("SET GLOBAL log_output = 'FILE'");
mysql_query("SET GLOBAL general_log_file = '/home/stas/tmp/mysql.project.log'");
mysql_query("SET GLOBAL general_log = 1");
// здесь выполняем SQL запрос
mysql_query("SET GLOBAL general_log = 0");
mysql_query("SET GLOBAL log_output = 'TABLE'");
Включить постоянное логирование (требует рестарт сервера):
[mysqld]
log = /var/log/mysql/mysql.log
Подробнее о других системных переменных логирования: http://dev.mysql.com/doc/refman/5.1/en/server-system-variables.html#sysvar_log_output
#mysql query log
Если выбирать из этих двух, то второй, разумеется. Он на порядок лучше первого:
— в отличие от первого, он будет выдавать ошибки туда же, куда и весь остальной РНР. На машине разработчика это может быть экран, на боевом сайте — лог. Первый плюёт ошибки в браузер ВСЕГДА, чего на боевом сайте не должно быть никогда
— в отличие от первого, он сообщит номер строки и имя файла, где произошла ошибка, что является критически важным для того самого отлова ошибки. Рекомендую попробовать поискать ошибочный запрос в коде на пару тысяч строк по сообщению от первого варианта. Подробнее про то, как правильно обрабатывать ошибки, можно почитать здесь: Обработка ошибок, часть 1
Примечание: на самом деле ни тот, ни другой коды работать не будут, поскольку mysqli_error() тоже требует $link в обязательном порядке.
Дальше уже идут более продвинутые варианты.
Для начала, mysqli умеет кидать исключения из коробки:
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
и после этого любая ошибка mysqli будет порождать исключение.
Но у этого подхода есть два минуса:
1. Такой вариант может понадобиться только в случае, если обращения к mysqli_query разбросаны по всему коду, чего делать нельзя ни в коем случае.
2. В брошенном исключении будет отсутствовать сам запрос, который может быть очень полезен при отладке.
Поэтому идеальным вариантом будет такой:
Во-первых, все обращения к mysqli API в обязательном порядке надо завернуть в какую-либо библиотеку, которая возьмёт на себя выполнение всей грязной и повторяющейся работы. Пример такой библиотеки — SafeMysql
Во-вторых, в этой библиотеке оформить код обращения к mysqli_query такм образом:
$res = $link->query($query);
if (!$res) throw new Exception($link->error() ." [$query]");
В результате мы получим идеальную обработку ошибок:
— этот код уже из коробки будет так же следовать настройкам РНР, и не будет выдавать ошибки на экран на боевом сервере, но при этом программист всегда будет о ней проинформирован.
— этот код будет выдавать трассировку вызовов — бесценную информацию, без которой найти место, где произошла ошибка, будет очень сложно.
— брошенное исключение можно будет поймать в хендлере или блоке try..catch (однако если нет опыта работы с этими двумя вещами, то на первое время лучше оставить исключение как есть. В обработке ошибок есть много нюансов, неизвестных среднему кодеру, и поэтому лучше оставить эту задачу для РНР).
What are your thoughts of using the following function to log mysql errors?
<?php
function sql_query($query)
{
$q = mysql_query($query);
if(!$q)
{
mysql_query('INSERT INTO mysql_errors (error_query, error_about) VALUES ('.
$query.', '.mysql_error().' )');
}
return $q;
}
?>
Do you find it useful? How can it be improved?
ajreal
46.6k10 gold badges88 silver badges119 bronze badges
asked Dec 1, 2010 at 16:14
Should be:
mysql_query("INSERT INTO mysql_errors (error_query, error_about, error_date) VALUES ('".
mysql_real_escape_string($query)."', '".mysql_real_escape_string(mysql_error())."',NOW())");
I added a date field and sanitised input. There needs to be a way to differentiate new and old errors and a date field does that.
Taryn East
27.4k9 gold badges86 silver badges108 bronze badges
answered Nov 19, 2012 at 22:14
I could use this, if I found a way to induce the right error, to do an SQL injection.
Why not just use MySQL’s native error log? Is there some reason to stuff them back into the DB?
answered Dec 1, 2010 at 16:17
DampeS8NDampeS8N
3,61117 silver badges20 bronze badges
It looks like you’ve not sanitized input, what if $query contains ‘? It would cause another SQL error.
answered Dec 1, 2010 at 16:18
DmiDmi
6351 gold badge11 silver badges22 bronze badges
Provided, of course, you already have your database connections open and do not need to open them here the function would work as expected as long as you have single quotes around the values in the second query. On a related note, is the mysql_error log not sufficient?
answered Dec 1, 2010 at 16:18
drewsterdrewster
531 silver badge4 bronze badges
2
-
#1
Передо мной стоит задача добавить в самописный движок механизм логирования запросов MySQL
вариант с изменением настроек в my.cnf или изменение глобальных переменных средствами SQL мне не подходит. Недостаточно прав доступа.
Механизм логирования должен быть реализован в самом движке.
В рамках модулей движка осуществляется доступ к базе данных с помощью sql запросов. Как правило таким образом:
$query = ‘…’;
$query = mysql_make_qw($query , …,…);
$result = mysql_query($query) or die(‘Ошибка …..’ . $query . ‘ ‘ . mysql_error());
В данном случае информация об ошибке выводится на экран. А мне нужно, чтобы она записывалась в файл. В принципе проблем с этим нет, функция записи информации файл написать нетрудно.
Моя проблема заключается в том, что мне придется пересмотреть весь код всего движка и заменить строку $result = mysql_query($query) or die(‘Ошибка …..’ . $query . ‘ ‘ . mysql_error());
на , например
if (!$result = mysql_query($query) )
{
//вызов метода, записывающего сообщение об ошибке в файл
}
Вопрос, есть ли у кого-нибудь идеи, как подключить механизм записи ошибок в файл без необходимости пересматривать весь код.
P.S. Есть идея реализовать механизм с помощью исключений, но это не решит главный вопрос, мне все равно придется пересмотреть весь код.
С.
Продвинутый новичок
-
#2
Поскольку представленный код очень чайнический, то стоит воспользоваться поводом и пересмотреть весь код. Тем более, что там пересматривать? Поправить в 3-5 местах.
-
#3
>> Поскольку представленный код очень чайнический, то стоит воспользоваться поводом и пересмотреть весь код. Тем более, что там пересматривать? >> Поправить в 3-5 местах.
не стоит торопиться с выводами о коде. Это лишь общая схема, достаточная для того, чтобы предложить помощь или нет., а запросов в движке много. Объем кода большой.
Вы знаете другой способ послать запрос на выполнение средствами mysql кроме как использование функций mysql_query . А выкладывать классы в тему форума для того чтобы получить совет не вижу смысла
-
#4
Добавь функцию my_query и вызывай её, а уж она пущай вызывает mysqli_query и логирует.
придется пересмотреть весь код всего движка и заменить строку $result = mysql_query($query) or…
-
#5
Добавь функцию my_query и вызывай её, а уж она пущай вызывает mysqli_query и логирует.
спасибо, если других вариантов я не найду, то поступлю именно так. Но и в этом случае, мне придется пересмотреть весь код.
С.
Продвинутый новичок
-
#6
Да что за пересмотрофобия? Будто код на железобетонной плите выбит или поиск по файлам еще не изобрели. Работы на 10 минут, охов и ахов — полтонны.
-
#7
С.,
Да что за пересмотрофобия?
Страшно заглядывать в старые, тёмные уголки кода. Ладно если просто заменить, а если вспоминать придётся как оно там работало?…
-
#8
С.,
Страшно заглядывать в старые, тёмные уголки кода. Ладно если просто заменить, а если вспоминать придётся как оно там работало?…
В моем случае придется просто заменить. У меня была идея переопределить функцию mysql_query . Но для этого потребуется установка новых модулей php . Не все хостинг — провайдеры это поддерживают …
Я думала, что можно перехватить вызов функции php … той же mysql_query()…
Были также попытки реализовать такой алгоритм … Мы ведь можем с помощью функции mysql_error() получить строку с сообщением об ошибке. Можно было пустить в бесконечном цикле обращение к этой функции — как бы ее слушатель. И как-то распараллелить процессы. Но средств для реализации такого алгоритма я не вижу в php … pcntl не подходит …
-
#9
Обертку использовать над модулей php вы не догадались?
-
#10
У меня была идея переопределить функцию mysql_query . Но для этого потребуется установка новых модулей php . Не все хостинг — провайдеры это поддерживают …
Можно было пустить в бесконечном цикле обращение к этой функции — как бы ее слушатель. И как-то распараллелить процессы. Но средств для реализации такого алгоритма я не вижу в php … pcntl не подходит …
После такого я чувствую себя чайником, и мне стыдно за свой примитивный ответ.
-
#11
Обертку использовать над модулей php вы не догадались?
поясните, пожалуйста, с этой темой не сталкивалась…
-
#12
То есть вы представляете себе как «слушать обращения», «распараллелить» и «переопределить», но тем не менее, до сих пор не додумались написать свой класс-обертку для работы с mysql? То есть написать класс/набор методов, где вся работа была бы вынесена в свои методы, и mysql_query, от которого давно надо отказаться, дергался бы только внутри класса в паре мест.
Стоит поискать по этому форуму, например есть http://phpclub.ru/talk/threads/Пятница-Говнокод-Парсинг-плейсхолдеров.76371/ такая тема, где в первом посте есть ссылка на библиотеку. Есть куча оберток, в том числе старая от Котерова и куча других.
-
#13
То есть вы представляете себе как «слушать обращения», «распараллелить» и «переопределить», но тем не менее, до сих пор не додумались написать свой класс-обертку для работы с mysql? То есть написать класс/набор методов, где вся работа была бы вынесена в свои методы, и mysql_query, от которого давно надо отказаться, дергался бы только внутри класса в паре мест.
Стоит поискать по этому форуму, например есть http://phpclub.ru/talk/threads/Пятница-Говнокод-Парсинг-плейсхолдеров.76371/ такая тема, где в первом посте есть ссылка на библиотеку. Есть куча оберток, в том числе старая от Котерова и куча других.
все ясно, спасибо. Естественно я в курсе того, как написать класс обертку. … сглупила немного (такое бывает). В моем движке я использую такую обертку. В принципе и в движке для которого, мне нужно написать логирование подобный класс используется. В нем правда только одна функция , позволяющая защитить код от sql — инъекций.
Мне хотелось попытаться решить задачу с минимальным вмешательством в код. Написать некий универсальный механизм, который не потребует «чистить код», что-то типа надстройки … добавил функцию в код в одном месте и логирование запустилось.
-
#14
Так если у тебя есть обертка, тогда не вижу вообще проблем…
С.
Продвинутый новичок
-
#15
Так если у тебя есть обертка, тогда не вижу вообще проблем…
VVV
подобный класс используется. В нем правда только одна функция , позволяющая защитить код от sql — инъекций.
Вот он вызывается во второй строке:
$query = ‘…’;
$query = mysql_make_qw($query , …,…);
$result = mysql_query($query) or die(‘Ошибка …..’ . $query . ‘ ‘ . mysql_error());
-
#16
С., Я так понял, что это простая функция, которая делает что-то с запросом, а обертки что-то я не вижу…
С.
Продвинутый новичок
-
#17
Просто все, что утверждает эта дама, надо делить на 3.14. Терминами она жонглирует хорошо.
-
#18
С., Я так понял, что это простая функция, которая делает что-то с запросом, а обертки что-то я не вижу…
Ранее я уже писала, что обертка реализована в моем движке.
Мне нужно написать код для другого — стороннего движка.
Вы говорите о том, что «Так если у тебя есть обертка, тогда не вижу вообще проблем» . Обертка то есть, только в другом движке?
Перенести его на мой движок — хорошо перенесу.
Но в этом случае, мне придется как бы то ни было заменить все фрагменты типа
$query = ‘…’;
$query = mysql_make_qw($query , …,…);
$result = mysql_query($query) or die(‘Ошибка …..’ . $query . ‘ ‘ . mysql_error());
на новый код, использующий конструкции моего класса «обертки»
я же обратилась на этот форум, чтобы спросить совет — возможно кто-то знает методы или механизмы, которые позволят не переделывать проект, не лезть в код, а анализировать, например, значение mysql_error() параллельно с работой проекта, не мешая ему.
Вопросы типа, зачем тебе это нужно….
В движке не так много функций … и т.д. и т.п. не конструктивны.
Вы либо можете помочь и что-то подсказать, либо отвечаете вопросами на вопрос — это все равно что не можете помочь в данном случае
-
#19
Ок, теперь понятно, что у вас и где, то есть обертки просто нет на месте. Но вот при чем тут mysql_error() я до сих пор не понимаю.
PS: За это время можно было успеть переписать половину проекта имхо
-
#20
А еще можно взять абсолютно любой современный редактор, найти кнопку «Replace in project/files» и везде заменить mysql_query на все что вам нужно
Потом найти кнопку поиска по регулярному выражению и выпилить вот это
Код:
or die('Ошибка .....' . $query . ' ' . mysql_error());
Журналы событий — первый и самый простой инструмент для определения статуса системы и выявления ошибок. Основных логов в MySQL четыре:
- Error Log — стандартный лог ошибок, которые собираются во время работы сервера (в том числе start и stop);
- Binary Log — лог всех команд изменения БД, нужен для репликации и бэкапов;
- General Query Log — основной лог запросов;
- Slow Query Log — лог медленных запросов.
Лог ошибок
Этот журнал содержит все ошибки, которые произошли во время работы сервера, включая критические ошибки, а также остановки, включения сервера и предупреждения (warnings). С него нужно начать в случае сбоя системы. По умолчанию все ошибки выводятся в консоль (stderr), также можно записывать ошибки в syslog (по умолчанию в Debian) или отдельный лог-файл:
log_error=/var/log/mysql/mysql_error.log
# Ошибки будут писаться в mysql_error.log
Рекомендуем держать этот журнал включенным для быстрого определения ошибок. А для понимания, что значит та или иная ошибка, в MySQL присутствует утилита perror:
shell> perror 13 64
OS error code 13: Permission denied
OS error code 64: Machine is not on the network
# Объясняет значения кодов ошибок
Бинарный (он же двоичный) лог
В бинарный лог записываются все команды изменения базы данных, пригодится для репликации и восстановления.
Включается так:
log_bin = /var/log/mysql/mysql-bin.log
expire_logs_days = 5
max_binlog_size = 500M
# Указывает расположение, срок жизни и максимальный размер файла
Учтите, что если вы не собираетесь масштабировать систему и реализовывать отказоустойчивость, то бинарный лог лучше не включать. Он требователен к ресурсам и снижает производительность системы.
Лог запросов
В этом журнале содержатся все полученные SQL-запросы, информация о подключениях клиентов. Может пригодиться для анализа индексов и оптимизации, а также выявления ошибочных запросов:
general_log_file = /var/log/mysql/mysql.log
<b>general_log = 1</b>
# Включает лог и указывает расположение файла
Также его можно включить/отключить во время работы сервера MySQL:
SET GLOBAL general_log = 'ON';
SET GLOBAL general_log = 'OFF';
# Для применения не нужно перезагружать сервер
Лог медленных запросов
Журнал пригодится для определения медленных, то есть неэффективных запросов. Подробнее читайте в этой статье.
Просмотр логов
Для просмотра логов на Debian (Ubuntu) нужно выполнить:
# Лог ошибок
tail -f /var/log/syslog
<span class="comment"> #Лог запросов
</span>tail -f /var/log/mysql/mysql.log
<span class="comment"> # Лог медленных запросов
</span>tail -f /var/log/mysql/mysql-slow.log
# Если логи не указаны отдельно, то находятся в /var/lib/mysql
Ротация логов
Не забывайте сжимать (архивировать, ротировать) файлы логов, чтобы они занимали меньше места на сервере. Для этого используйте утилиту logrotate, отредактировав файл конфигурации /etc/logrotate.d/mysql-server:
# - I put everything in one block and added sharedscripts, so that mysql gets
<span class="comment">
# flush-logs'd only once.
</span># Else the binary logs would automatically increase by n times every day.
<span class="comment">
# - The error log is obsolete, messages go to syslog now.
</span><b>/var/log/mysql.log /var/log/mysql/mysql.log /var/log/mysql/mysql-slow.log</b> {
daily
rotate 7
missingok
create 640 mysql adm
compress
sharedscripts
postrotate
test -x /usr/bin/mysqladmin || exit 0
<span class="comment"> # If this fails, check debian.conf!
</span> MYADMIN="/usr/bin/mysqladmin --defaults-file=/etc/mysql/debian.cnf"
if [ -z "`$MYADMIN ping 2>/dev/null`" ]; then
<span class="comment"> # Really no mysqld or rather a missing debian-sys-maint user?
</span> <span class="comment"> # If this occurs and is not an error please report a bug.
</span> <span class="comment"> #if ps cax | grep -q mysqld; then
</span> if killall -q -s0 -umysql mysqld; then
exit 1
fi
else
$MYADMIN flush-logs
fi
endscript
}
# Сжимает и архивирует нужные логи, очищает файлы
DDL Log
MySQL также ведет лог языка описания данных. В него собираются данные операций типа DROP_TABLE and ALTER_TABLE. Лог используется для восстановления после сбоев, которые произошли во время выполнения таких операций. DDL Log — бинарный файл, не предназначенный для чтения пользователем, поэтому не модифицируйте и не удаляйте его.
Самое главное
Всегда включайте лог ошибок, используйте лог запросов для проверки соединения приложения с базой данных, проверки запросов и работы memcached. Лог медленных запросов пригодится для оптимизации работы MySQL.
