Журналювання
============

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