Как вести логирование в yii2?
Ответ
Возможность вести log-файлы полезна и на этапе знакомства с фреймворком, и в реальном проекте. В обоих случаях - для уточнения, что именно содержит переменная, или проверки, выполняется ли некоторый участок кода. Во втором случае ведение логов часто является требованием заказчика (например, при осуществлении платежей).
Логирование (по-русски: протоколирование) можно вести:
а) в файл (FileTarget.php);
б) в системный файл посредством php-функции syslog (SyslogTarget.php);
в) на email (EmailTarget.php);
г) в базу данных (DbTarget.php);
д) любой другой ваш вариант.
Есть еще возможность профилирования.
В этом посте рассмотрим лог в файл. Конкретнее, реализуем возможность добавлять логи в файл itmathrepetitor.txt, находящийся в корне сайта (то есть около папок web, views, vendor и т.д.).
Открываем файл config/web.php и в массив $config добавляем строку 'bootstrap' => ['log'], (если она еще не добавлена, конечно).
1 2 3 4 5 6 7 |
... 'basePath' => dirname(__DIR__), 'bootstrap' => ['log'], 'components' => [ 'urlManager' => [ ... |
Тем самым мы добавили log в предзагрузку.
Далее в подмассиве 'components' изменяем/добавляем элемент 'log':
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
'log' => [ 'flushInterval' => 1, 'traceLevel' => YII_DEBUG ? 3 : 0, 'targets' => [ [ 'class' => 'yii\log\FileTarget', 'exportInterval' => 1, 'categories' => ['my_category'], 'levels' => ['info'], 'logFile' => '@app/itmathrepetitor.txt', 'logVars' => [] ], ], ], |
С помощью 'logVars' можно добавлять данные из переменных $_GET, $_POST, $_FILES, $_COOKIE, $_SESSION и $_SERVER. Например, 'logVars' => ['_GET', '_SERVER']. В нашем же случае никакие из этих переменных не используются (неудобно выискивать свои сообщения среди массы других).
Чтобы сообщения сразу добавлялись в файл, 'flushInterval' и 'exportInterval' установлены в 1. По умолчанию эти значения равны 1000, то есть сообщения накапливаются в памяти затем выгружаются в файл. Здесь могут быть проблемы с производительностью из-за частой выгрузки.
Уровень логирования выбран 'info'. Все варианты уровня из vendor/yiisoft/yii2/log/Target.php:
1 2 3 4 5 6 7 8 |
static $levelMap = [ 'error' => Logger::LEVEL_ERROR, 'warning' => Logger::LEVEL_WARNING, 'info' => Logger::LEVEL_INFO, 'trace' => Logger::LEVEL_TRACE, 'profile' => Logger::LEVEL_PROFILE, ]; |
Если не указывать 'categories', то по умолчанию значение будет равно 'application' и к нашим сообщения будут добавлены многие другие. Например, подключение и запросы к базе данных.
Проверим работоспособность нашего кода. Для этого в каком-нибудь контроллере добавим строку Yii::info('test message','my_category'); и перейдем на страницу сайта, обрабатываемую этим контроллером.
В результате в itmathrepetitor.txt появятся строки:
1 2 3 4 |
2016-01-07 21:31:38 [127.0.0.1][-][-][info][my_category] test message in D:\OpenServer\domains\localhost\yii2tasks\controllers\TaskController.php:102 |
Похоже, что задача выполнена. Но хотелось бы видеть результат лаконичнее:
1 2 3 4 |
[07-01 21:55:46] test message [TaskController.php:102] |
Но тогда логирование получается настолько простым, что выгоднее создать обычное расширение для записи в файл без использования стандартного Logger-а (задача для самостоятельного решения). Кстати, при реализации пригодится константа php __METHOD__.
Мы же создадим в папке vendor/yiisoft/yii2/log копию файла FileTarget.php c именем MyFileTarget.php, изменим название класса в файле на MyFileTarget, добавим функцию formatMessage из файла Target.php и частично изменим ее код:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
public function formatMessage($message) { list($text, $level, $category, $timestamp) = $message; $level = Logger::getLevelName($level); if (!is_string($text)) { // exceptions may not be serializable if in the call stack somewhere is a Closure if ($text instanceof \Exception) { $text = (string) $text; } else { $text = VarDumper::export($text); } } $traces = []; if (isset($message[4])) { foreach ($message[4] as $trace) { $traces[] = " " . basename($trace['file']) . ":{$trace['line']}"; } } return "[" . date('d-m H:i:s', $timestamp) . "] $text" . (empty($traces) ? '' : "\n [" . trim(implode(" ", $traces))) ."]"; } |
Теперь сообщения имеют нужный формат. Кстати, из кода следует, что передавать в log-файл можно не только строки, но и объекты ($text=VarDumper::export($text)).
Последнее замечание: хотелось бы вместо Yii::info('test message','my_category'); писать Yii::show('test_message'), то есть не указывать категорию (лишние символы ведь). Напомню, что тогда по умолчанию категория равна 'application', что добавляет в log-файл дополнительные сообщения. Вторая задача для самостоятельного решения: реализовать такую возможность без изменения файла BaseYii.php. Мы же поступим, видимо, непрофессионально. Откроем файл vendor/yiisoft/yii2/BaseYii.php и добавим функцию show:
1 2 3 4 5 |
public static function show($message, $category = 'my_category') { static::getLogger()->log($message, Logger::LEVEL_INFO, $category); } |
Остается проверить код Yii::show('test_message'); Для тренировки:
а) добавьте ограничение количества сообщений в файле - хранятся только последние 10;
б) каждое новое сообщение добавляется в начало файла.
Дополнительно о логировании смотрите https://github.com/yiisoft/yii2/blob/master/docs/guide-ru/runtime-logging.md и http://www.yiiframework.com/doc-2.0/yii-log-logger.html
Не гоже что то менять в папке vendor, правильнее будет отнаследоваться и переопределить.
namespace app\components\log
class MyFileTarget extends \yii\log\FileTarget
{
// Переопределяем, добавляем, изменяем
public function formatMessage($message)
{
...
}
}
В конфиге указываем свой переопределённый класс MyFileTarget вместо FileTarget
'log' => [
...
'targets' => [
[
'class' => 'app\components\log\MyFileTarget',
...
],
],
...
],