Журналирование ============== Yii предоставляет гибкий и расширяемый функционал протоколирования. Сообщения можно классифицировать в соответствии с уровнем протоколирования и типом сообщений. Используя фильтры по уровню и категории, можно направить поток сообщений в файлы, электронную почту, окно браузера и т.д. Протоколирование сообщений -------------------------- Сообщение может быть запротоколировано путем вызова [Yii::log] или [Yii::trace]. Разница между ними заключается в том, что последний пишет в лог только тогда, когда приложение работает в [режиме отладки](/doc/guide/basics.entry#debug-mode). ~~~ [php] Yii::log($message, $level, $category); Yii::trace($message, $category); ~~~ Для внесения сообщения в лог, необходимо указать категорию и уровень сообщения. Категория представляет собой строку формата `xxx.yyy.zzz`, что очень схоже с форматом представления [псевдонима пути](/doc/guide/basics.namespace). Например, если сообщение добавляется в лог в [CController], мы можем использовать категорию `system.web.CController`. Уровень сообщения может иметь одно из следующих значений: - `trace`: этот уровень используется методом [Yii::trace]. Он предназначен для отслеживания процесса выполнения приложения в ходе разработки; - `info`: этот уровень предназначен для протоколирования информации общего характера; - `profile`: данный уровень используется для профилирования (измерения) производительности; - `warning`: этот уровень предназначен для сообщений-предупреждений; - `error`: этот уровень используется для сообщений о критических ошибках. Маршрутизация сообщений ----------------------- Сообщения, протоколируемые с использованием [Yii::log] или [Yii::trace], хранятся в памяти. Как правило, нам требуется либо отобразить их в окне браузера, либо сохранить в файле, отправить электронным письмом и пр. Направление сообщений в различные места назначения называется *маршрутизацией сообщений*. В Yii за маршрутизацию сообщений отвечает компонент приложения [CLogRouter]. Этот компонент управляет множеством так называемых *маршрутов сообщений*. Каждый маршрут представляет одно место назначения потока сообщений. Сообщения, направляемые по тому или иному маршруту, можно отфильтровать в зависимости от их уровня и типа. Для того, чтобы воспользоваться маршрутизацией сообщений, нам необходимо установить и подгрузить заранее компонент приложения [CLogRouter]. Кроме того, необходимо настроить свойство [routes|CLogRouter::routes] этого компонента, указав маршруты сообщений, которые предполагается использовать. Ниже приведен пример необходимой [конфигурации приложения](/doc/guide/basics.application#application-configuration): ~~~ [php] array( … 'preload'=>array('log'), 'components'=>array( … 'log'=>array( 'class'=>'CLogRouter', 'routes'=>array( array( 'class'=>'CFileLogRoute', 'levels'=>'trace, info', 'categories'=>'system.*', ), array( 'class'=>'CEmailLogRoute', 'levels'=>'error, warning', 'emails'=>'admin@example.com', ), ), ), ), ) ~~~ В примере выше, у нас есть два маршрута сообщений. Первый - [CFileLogRoute] - сохраняет сообщения в папке приложения для временных файлов `runtime`. Сохраняются только сообщения с уровнем `trace` или `info` и чья категория начинается с `system.`. Второй маршрут - [CEmailLogRoute] - отправляет сообщения на указанный электронный адрес. Отправляются только сообщения уровня `error` или `warning`. Начиная с Yii версии 1.1.13 можно исключать определённые категории: ~~~ [php] 'routes'=>array( array( 'class'=>'CEmailLogRoute', 'levels'=>'error, warning', 'except'=>'system.CModule.*' // отсылаем почтой всё кроме сообщений от CModule 'emails'=>'admin@example.com', ), array( 'class'=>'CWebLogRoute', 'categories'=>'system.db.*', 'except'=>'system.db.ar.*', // показываем всё, что касается базы данных, но не касается AR ), ~~~ В Yii доступны для использования следующие маршруты сообщений: - [CDbLogRoute]: сохраняет сообщения в таблицу базы данных; - [CEmailLogRoute]: отправляет сообщения на указанный адрес электронной почты; - [CFileLogRoute]: сохраняет сообщения во временной папке приложения; - [CWebLogRoute]: отображает сообщения в конце текущей страницы; - [CProfileLogRoute]: отображает сообщения о производительности в конце текущей страницы. ~~~ [php] array( ...... 'preload'=>array('log'), 'components'=>array( ...... 'log'=>array( 'class'=>'CLogRouter', 'routes'=>array( array( 'class'=>'CProfileLogRoute', 'report'=>'summary', // Показывает время выполнения каждого отмеченного блока кода. // Значение "report" также можно указать как "callstack". ), ...остальные маршруты... ), ), ), ) ~~~ > Info|Информация: Маршрутизация сообщения происходит в конце каждого текущего цикла обработки запроса, в момент, когда вызывается событие [onEndRequest|CApplication::onEndRequest]. Для прерывания процесса обработки текущего запроса, используйте метод [CApplication::end()] вместо `die()` или `exit()`. [CApplication::end()] вызывает событие [onEndRequest|CApplication::onEndRequest], что позволяет корректно запротоколировать сообщения. Фильтрация сообщений -------------------- Как уже упоминалось выше, сообщения можно отфильтровать по их уровню и типу до того, как они будут направлены тем или иным маршрутом. Это осуществляется путем настройки свойств [levels|CLogRoute::levels] и [categories|CLogRoute::categories] соответствующего маршрута. Если необходимо указать несколько уровней или типов, значения должны быть разделены запятыми. Поскольку типы сообщений указываются в формате `xxx.yyy.zzz`, мы можем воспринимать их как иерархию типов. В частности, мы говорим, что `xxx` является родителем `xxx.yyy`, а последний в свою очередь является родителем для `xxx.yyy.zzz`. Поэтому для указания типа `xxx`, а также всех его типов-потомков можно использовать выражение `xxx.*`. Сохранение контекста сообщений ------------------------------ Мы можем сохранять дополнительную информацию, такую как предопределённые переменные PHP (`$_GET`, `$_SERVER`), ID сессии, имя пользователя и т.д. Для этого необходимо задать необходимый фильтр в свойстве [CLogRoute::filter]. В состав фреймворка входит удобный класс [CLogFilter], который может быть использован в качестве фильтра в большинстве случаев. По умолчанию, [CLogFilter] будет записывать сообщение вместе с такими переменными, как `$_GET` и `$_SERVER`, которые обычно содержат ценную системную информацию. Можно настроить [CLogFilter] таким образом, чтобы перед каждым сообщением записывать ID сессии, имя пользователя и другие данные, которые могут облегчить поиск по большому количеству сообщений. Следующие настройки включают запись контекста сообщений. У каждого журнального маршрута может быть задан свой фильтр. По умолчанию никакого фильтра не задано. ~~~ [php] array( … 'preload'=>array('log'), 'components'=>array( … 'log'=>array( 'class'=>'CLogRouter', 'routes'=>array( array( 'class'=>'CFileLogRoute', 'levels'=>'error', 'filter'=>'CLogFilter', ), …other log routes… ), ), ), ) ~~~ Yii поддерживает журналирование информации стека вызова в сообщениях, протоколируемых путем вызова [Yii::trace]. По умолчанию, данная особенность отключена, т.к. снижает производительность. Для ее использования, необходимо просто определить константу `YII_TRACE_LEVEL` в начале входного скрипта (до включения файла `yii.php`) целым числом большим нуля. Тогда Yii будет добавлять в каждое трассирующее сообщение имя файла и номер строки стека вызова, в которых был сделан вызов кода. Число `YII_TRACE_LEVEL` определяет количество слоев каждого стека вызова, которое должно быть записано. Эта информация особенно полезна на стадии разработки, так как может помочь нам определить места, в которых вызываются трассирующие сообщения. Профилирование производительности --------------------------------- Для целей измерения производительности используется специальный тип сообщений. Его можно использовать для измерения времени исполнения некоторого блока кода и определения узких мест в производительности. Для того, чтобы измерить производительность, необходимо указать ту часть кода, выполнение которой будет отслеживаться. Используя следующие методы, мы отмечаем начало и конец каждого измеряемого блока кода: ~~~ [php] Yii::beginProfile('blockID'); …блок профилируемого кода… Yii::endProfile('blockID'); ~~~ где `blockID` — это уникальный идентификатор блока кода. Обратите внимание, что блоки кода должны иметь корректную вложенность, т.е. они не могут пересекаться друг с другом. Они либо идут параллельно, либо один блок полностью включает другой. Для того, чтобы увидеть результат профилирования, нам потребуется установить компонент приложения [CProfileLogRoute], отвечающий за соответствующий маршрут протоколирования. Здесь все аналогично работе с простыми маршрутами сообщений. Маршрут [CProfileLogRoute] отобразит результаты измерения производительности внизу текущей страницы. Профилирование SQL-запросов --------------------------- Профилирование особенно полезно при работе с базой данных, так как SQL-запросы часто являются самым узким местом производительности приложения. Несмотря на то, что мы можем вставить в нужные места `beginProfile` и `endProfile` для того, чтобы замерить время, затраченное на каждый SQL-запрос, Yii предоставляет более удобное решение данной проблемы. Выставив в настройках приложения [CDbConnection::enableProfiling] в true, мы получим профилирование всех выполняемых SQL-запросов. Полученные результаты можно вывести при помощи вышеупомянутого [CProfileLogRoute], показывающего, какой SQL-запрос сколько времени занял. Для вывода общего количества запросов и общего времени выполнения можно использовать [CDbConnection::getStats()].