Журналирование
==============

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()].