Логирование ¶
Yii предоставляет мощную, гибко настраиваемую и легко расширяемую систему логирования. Эта система логирования позволяет удобным способом сохранять сообщения разных типов и фильтровать их. Сообщения могут быть сохранены в файлы, базы данных или отправлены на email.
Использование Системы логирования Yii включает следующие шаги:
- Запись сообщений лога в различных частях кода приложения;
- Настройка целей лога в конфигурации приложения;
- Изучение отфильтрованных сообщений лога, например, при помощи Отладчика Yii.
В данном разделе будем рассматривать первые два шага.
Сообщения лога ¶
Запись сообщений лога осуществляется вызовом одного из следующих методов:
- Yii::debug(): записывает сообщения для отслеживания выполнения кода приложения. Используется, в основном, при разработке.
- Yii::info(): записывает сообщение, содержащее какую-либо полезную информацию.
- Yii::warning(): записывает тревожное сообщение при возникновении неожиданного события.
- Yii::error(): записывает критическую ошибку, на которую нужно, как можно скорее, обратить внимание.
Эти методы позволяют записывать сообщения разных уровней важности и категорий. Они имеют одинаковое описание функции function ($message, $category = 'application'), где $message передает сообщение для записи, а $category — категорию сообщения. В следующем примере будет записано trace сообщение с категорией по умолчанию application:
Yii::debug('начало вычисления среднего дохода');
Примечание: Сообщение может быть как строкой, так и объектом или массивом. За корректную работу с содержимым сообщения отвечают цели лога. По умолчанию, если сообщение не является строкой, оно будет приведено к строковому типу при помощи yiihelpersVarDumper::export().
Для упрощения работы с сообщениями лога и их фильтрации, рекомендуется явно указывать подходящую категорию для каждого сообщения. Возможно использование иерархической системы именования категорий, что значительно упростит целям лога фильтрацию сообщений по категориям. Простым и эффективным способом именования категорий является использование магической PHP-константы __METHOD__. Такой подход используется в ядре фреймворка Yii. Например,
Yii::debug('начало вычисления среднего дохода', __METHOD__);
Константа __METHOD__ вычисляется как имя метода (включая полное имя класса), в котором она использована. Например, её значение будет вычислено как 'appcontrollersRevenueController::calculate', если показанный выше код вызывается в соответствующем методе.
Информация: методы логирования, описанные выше являются, на самом деле, ярлыками для метода log() объекта логгера, который доступен как синглтон
Yii::getLogger().
При определенном количестве записанных сообщений или завершении приложения, объект логгера вызывает message dispatcher для отправки записанных сообщений зарегистрированным целям логов.
Цели логов ¶
Цель логов — это экземпляр класса yiilogTarget или класса, унаследованного от него. Цель фильтрует сообщения логов по уровню важности и категории, а затем выгружает их в соответствующее хранилище. Например, database target выгружает отфильтрованные сообщения логов в таблицу базы данных, а email target отправляет сообщения логов на заданные адреса email.
При помощи компонента приложения log возможна регистрация нескольких целей логов. Пример конфигурации приложения:
return [
// Компонент "log" должен быть загружен на этапе предзагрузки
'bootstrap' => ['log'],
// Компонент "log" обрабатывает сообщения с меткой времени timestamp.
// Задайте временную зону для создания корректных меток времени.
'timeZone' => 'Europe/Moscow',
'components' => [
'log' => [
'targets' => [
[
'class' => 'yiilogDbTarget',
'levels' => ['error', 'warning'],
],
[
'class' => 'yiilogEmailTarget',
'levels' => ['error'],
'categories' => ['yiidb*'],
'message' => [
'from' => ['log@example.com'],
'to' => ['admin@example.com', 'developer@example.com'],
'subject' => 'Ошибки базы данных на сайте example.com',
],
],
],
],
],
];
Примечание: Компонент
logдолжен быть загружен в процессе предзагрузки, тогда он сможет оперативно передавать сообщения целям логов. Поэтому он указан в массивеbootstrap.
В приведенном выше коде в свойстве yiilogDispatcher::$targets зарегистрированы две цели логов:
- первая цель выбирает ошибки и предупреждения и сохраняет их в базу данных;
- вторая цель выбирает ошибки с категорией, имя которой начинается с
yiidbи шлет сразу на два адреса emailadmin@example.comиdeveloper@example.com.
На данный момент, Yii содержит следующие встроенные цели логов. В документации по API подробно описана настройка и использование этих классов.
- yiilogDbTarget: сохраняет сообщения логов в таблицу базы данных.
- yiilogEmailTarget: шлет сообщения логов на заранее указанный email.
- yiilogFileTarget: сохраняет сообщения логов в файлы.
- yiilogSyslogTarget: сохраняет сообщения логов в системный лог используя функцию PHP
syslog().
Дальше рассмотрим общие для этих четырех классов возможности.
Фильтрация сообщений ¶
Для каждой цели можно настроить свойства levels и categories, которые указывают уровни важности и категории сообщений логов, которые цель должна обрабатывать.
Свойство levels принимает массив, содержащий одно или несколько следующих значений:
error: соответствует сообщениям, сохраненным методом Yii::error().warning: соответствует сообщениям, сохраненным методом Yii::warning().info: соответствует сообщениям, сохраненным методом Yii::info().trace: соответствует сообщениям, сохраненным методом Yii::debug().profile: соответствует сообщениям, сохраненным методами Yii::beginProfile() и Yii::endProfile(), подробнее о которых написано в подразделе Профилирование производительности.
Если свойство levels не задано, цель логов будет обрабатывать сообщения с любым уровнем важности.
Свойство categories принимает массив, содержащий имена категорий или шаблоны.
Цель будет обрабатывать только те сообщения, категория которых совпадает с одним из значений или шаблонов этого массива. Шаблон категории должен состоять из префикса имени категории и звездочки * на конце. Имя категории совпадает с шаблоном, если оно начинается с префикса шаблона. Например, yiidbCommand::execute и yiidbCommand::query используются в качестве имен категорий сообщений, записанных в классе yiidbCommand. Оба они совпадают с шаблоном yiidb*.
Если свойство categories не задано, цель будет обрабатывать сообщения любой категории.
Кроме списка включаемый категорий, заданного свойством categories, при помощи свойства except возможно задать список исключаемых категорий. Если категория сообщения совпадает со значением или шаблоном из списка исключаемых категорий, такое сообщение не будет обработано.
В следующем примере показан вариант конфигурации цели логов, которая должна обрабатывать только сообщения об ошибках и предупреждениях в категориях yiidb* и yiiwebHttpException:*, за исключением yiiwebHttpException:404.
[
'class' => 'yiilogFileTarget',
'levels' => ['error', 'warning'],
'categories' => [
'yiidb*',
'yiiwebHttpException:*',
],
'except' => [
'yiiwebHttpException:404',
],
]
Примечание: При обработке HTTP-исключения обработчиком ошибок, сообщение будет сохранено с категорией вида
yiiwebHttpException:ErrorCode. Например, исключение yiiwebNotFoundHttpException вызовет сообщение об ошибке с категориейyiiwebHttpException:404.
Форматирование сообщений ¶
Цели логов выгружают отфильтрованные сообщения в определенном формате. Например, цель класса yiilogFileTarget сохранит сообщение следующего формата в файле runtime/log/app.log:
2014-10-04 18:10:15 [::1][][-][trace][yiibaseModule::getModule] Loading module: debug
По умолчанию сообщения логов форматируются методом yiilogTarget::formatMessage():
Временная метка [IP-адрес][ID пользователя][ID сессии][Уровень важности][Категория] Текст сообщения
Этот формат может быть изменен при помощи свойства yiilogTarget::$prefix, которое получает анонимную функцию, возвращающую нужный префикс сообщения. Например, следующий код позволяет настроить вывод идентификатор текущего пользователя в качестве префикса для всех сообщений.
[
'class' => 'yiilogFileTarget',
'prefix' => function ($message) {
$user = Yii::$app->has('user', true) ? Yii::$app->get('user') : null;
$userID = $user ? $user->getId(false) : '-';
return "[$userID]";
}
]
Кроме префиксов сообщений, также возможно добавление общей информации для каждого набора сообщений лога.
По умолчанию включаются значения следующих глобальных PHP-переменных: $_GET, $_POST, $_FILES, $_COOKIE,
$_SESSION и $_SERVER. Эта возможность настраивается при помощи свойства yiilogTarget::$logVars, содержащего массив имен переменных, которые необходимо включить в лог. Например, следующий код позволяет настроить цель логов так, чтобы к сообщениям присоединялось только содержимое переменной $_SERVER.
[
'class' => 'yiilogFileTarget',
'logVars' => ['_SERVER'],
]
При задании значением свойства logVars пустого массива, общая информация не будет выводиться.
Для определения собственного алгоритма подключения общей информации, следует переопределить метод yiilogTarget::getContextMessage().
Уровень отслеживания выполнения кода ¶
В процессе разработки, часто бывает необходимость видеть источники сообщений. Для этого нужно использовать свойство traceLevel компонента log. Например,
return [
'bootstrap' => ['log'],
'components' => [
'log' => [
'traceLevel' => YII_DEBUG ? 3 : 0,
'targets' => [...],
],
],
];
При такой настройке свойство traceLevel будет равно 3 при YII_DEBUG равном true и 0 при YII_DEBUG равном false. Это означает, что при включенном YII_DEBUG, каждое сообщение лога будет содержать до трех уровней стека вызовов, а при выключенном YII_DEBUG информация о стеке вызовов не будет включаться в лог.
Информация: Получение информации стека вызовов является не простым процессом. Поэтому такую возможность следует использовать только при разработке или отладке приложения.
Передача на обработку и выгрузка сообщений ¶
Как упоминалось выше, сообщения логов обрабатываются в массиве объектом логгера. Для ограничения объема памяти, занятого этим массивом, при накоплении определенного числа сообщений, логгер передает их на обработку целям логов. Максимальное количество сообщений определяется свойством flushInterval компонента log:
return [
'bootstrap' => ['log'],
'components' => [
'log' => [
'flushInterval' => 100, // по умолчанию 1000
'targets' => [...],
],
],
];
Информация: При завершении приложения, так же происходит передача сообщений на обработку.
После передачи сообщений объектом логгера в цели логов, сообщения не выгружаются немедленно. Вместо этого, выгрузка сообщений происходит когда цель логов накопит определенное количество фильтрованных сообщений. Максимальное количество сообщений определяется свойством exportInterval цели логов. Например,
[
'class' => 'yiilogFileTarget',
'exportInterval' => 100, // по умолчанию 1000
]
Из-за того, что значения максимального количества сообщений для передачи и выгрузки по умолчанию достаточно велико, при вызове метода Yii::debug() или любого другого метода логирования, сообщение не появится сразу в файле или таблице базы данных. Такое поведение может стать проблемой, например, в консольных приложениях с большим временем исполнения. Для того, чтобы все сообщения логов сразу попадали в лог, необходимо установить значения свойств flushInterval и exportInterval равными 1, например так:
return [
'bootstrap' => ['log'],
'components' => [
'log' => [
'flushInterval' => 1,
'targets' => [
[
'class' => 'yiilogFileTarget',
'exportInterval' => 1,
],
],
],
],
];
Примечание: Частая передача и выгрузка сообщений может сильно снизить производительность приложения.
Переключение целей логов ¶
Свойство enabled отвечает за включение или отключение цели логов. Возможно управлением этим свойством как в конфигурации приложения, так и при помощи следующего PHP-кода:
Yii::$app->log->targets['file']->enabled = false;
В данном примере используется цель логов file, которая может быть настроена в конфигурации приложения следующим образом:
return [
'bootstrap' => ['log'],
'components' => [
'log' => [
'targets' => [
'file' => [
'class' => 'yiilogFileTarget',
],
'db' => [
'class' => 'yiilogDbTarget',
],
],
],
],
];
Создание новых целей ¶
Создание новой цели логов не является сложной задачей. В общем случае нужно реализовать метод yiilogTarget::export(), выгружающий массив yiilogTarget::$messages в место хранения логов. Возможно использование метода yiilogTarget::formatMessage() для форматирования сообщения. Детали реализации можно подсмотреть в исходном коде любого из классов целей логов, включенных в состав Yii.
Профилирование производительности ¶
Профилирование производительности — это специальный тип сообщений логов, используемый для измерения времени выполнения определенных участков кода и определения проблем производительности. Например, класс yiidbCommand использует профилирование производительности для определения времени исполнения каждого запроса базы данных.
Для использования профилирования производительности нужно определить участок кода для измерения и обернуть его вызовами методов Yii::beginProfile() и Yii::endProfile(). Например,
Yii::beginProfile('myBenchmark');
// участок кода для профилирования...
Yii::endProfile('myBenchmark');
где myBenchmark является уникальным идентификатором данного измеряемого участка кода.
В дальнейшем, при изучении результатов профилирования, уникальный идентификатор поможет определить время выполнения соответствующего участка кода.
Очень важно соблюдать уровни вложенности пар beginProfile и endProfile. Например,
Yii::beginProfile('block1');
// код для профилирования
Yii::beginProfile('block2');
// другой код для профилирования
Yii::endProfile('block2');
Yii::endProfile('block1');
Если пропустить Yii::endProfile('block1') или поменять местами Yii::endProfile('block1') и
Yii::endProfile('block2'), профилирование производительности не будет работать.
Для каждого участка кода, будет записано сообщение лога с уровнем важности profile. Для сбора таких сообщений можно настроить цель логов или воспользоваться Отладчиком Yii, который имеет встроенную панель профилирования производительности, отображающую результаты измерений.
Yii provides a powerful logging framework that is highly customizable and extensible. Using this framework, you
can easily log various types of messages, filter them, and gather them at different targets, such as files, databases,
emails.
Using the Yii logging framework involves the following steps:
- Record log messages at various places in your code;
- Configure log targets in the application configuration to filter and export log messages;
- Examine the filtered logged messages exported by different targets (e.g. the Yii debugger).
In this section, we will mainly describe the first two steps.
Log Messages ¶
Recording log messages is as simple as calling one of the following logging methods:
- Yii::debug(): record a message to trace how a piece of code runs. This is mainly for development use.
- Yii::info(): record a message that conveys some useful information.
- Yii::warning(): record a warning message that indicates something unexpected has happened.
- Yii::error(): record a fatal error that should be investigated as soon as possible.
These logging methods record log messages at various severity levels and categories. They share
the same function signature function ($message, $category = 'application'), where $message stands for
the log message to be recorded, while $category is the category of the log message. The code in the following
example records a trace message under the default category application:
Yii::debug('start calculating average revenue');
Info: Log messages can be strings as well as complex data, such as arrays or objects. It is the responsibility
of log targets to properly deal with log messages. By default, if a log message is not a string,
it will be exported as a string by calling yiihelpersVarDumper::export().
To better organize and filter log messages, it is recommended that you specify an appropriate category for each
log message. You may choose a hierarchical naming scheme for categories, which will make it easier for
log targets to filter messages based on their categories. A simple yet effective naming scheme
is to use the PHP magic constant __METHOD__ for the category names. This is also the approach used in the core
Yii framework code. For example,
Yii::debug('start calculating average revenue', __METHOD__);
The __METHOD__ constant evaluates as the name of the method (prefixed with the fully qualified class name) where
the constant appears. For example, it is equal to the string 'appcontrollersRevenueController::calculate' if
the above line of code is called within this method.
Info: The logging methods described above are actually shortcuts to the log() method
of the logger object which is a singleton accessible through the expressionYii::getLogger(). When
enough messages are logged or when the application ends, the logger object will call a
message dispatcher to send recorded log messages to the registered log targets.
Log Targets ¶
A log target is an instance of the yiilogTarget class or its child class. It filters the log messages by their
severity levels and categories and then exports them to some medium. For example, a database target
exports the filtered log messages to a database table, while an email target exports
the log messages to specified email addresses.
You can register multiple log targets in an application by configuring them through the log application component
in the application configuration, like the following:
return [
// the "log" component must be loaded during bootstrapping time
'bootstrap' => ['log'],
// the "log" component process messages with timestamp. Set PHP timezone to create correct timestamp
'timeZone' => 'America/Los_Angeles',
'components' => [
'log' => [
'targets' => [
[
'class' => 'yiilogDbTarget',
'levels' => ['error', 'warning'],
],
[
'class' => 'yiilogEmailTarget',
'levels' => ['error'],
'categories' => ['yiidb*'],
'message' => [
'from' => ['log@example.com'],
'to' => ['admin@example.com', 'developer@example.com'],
'subject' => 'Database errors at example.com',
],
],
],
],
],
];
Note: The
logcomponent must be loaded during bootstrapping time so that
it can dispatch log messages to targets promptly. That is why it is listed in thebootstraparray as shown above.
In the above code, two log targets are registered in the yiilogDispatcher::$targets property:
- the first target selects error and warning messages and saves them in a database table;
- the second target selects error messages under the categories whose names start with
yiidb, and sends
them in an email to bothadmin@example.comanddeveloper@example.com.
Yii comes with the following built-in log targets. Please refer to the API documentation about these classes to
learn how to configure and use them.
- yiilogDbTarget: stores log messages in a database table.
- yiilogEmailTarget: sends log messages to pre-specified email addresses.
- yiilogFileTarget: saves log messages in files.
- yiilogSyslogTarget: saves log messages to syslog by calling the PHP function
syslog().
In the following, we will describe the features common to all log targets.
Message Filtering ¶
For each log target, you can configure its levels and
categories properties to specify which severity levels and categories of the messages the target should process.
The levels property takes an array consisting of one or several of the following values:
error: corresponding to messages logged by Yii::error().warning: corresponding to messages logged by Yii::warning().info: corresponding to messages logged by Yii::info().trace: corresponding to messages logged by Yii::debug().profile: corresponding to messages logged by Yii::beginProfile() and Yii::endProfile(), which will
be explained in more details in the Profiling subsection.
If you do not specify the levels property, it means the target will process messages
of any severity level.
The categories property takes an array consisting of message category names or patterns.
A target will only process messages whose category can be found or match one of the patterns in this array.
A category pattern is a category name prefix with an asterisk * at its end. A category name matches a category pattern
if it starts with the same prefix of the pattern. For example, yiidbCommand::execute and yiidbCommand::query
are used as category names for the log messages recorded in the yiidbCommand class. They both match
the pattern yiidb*.
If you do not specify the categories property, it means the target will process
messages of any category.
In addition to specifying allowed categories using the categories property, you may also
exclude certain categories by the except property. If the category of a message
is found or matches one of the patterns in this property, it will NOT be processed by the target.
The following target configuration specifies that the target should only process error and warning messages
under the categories whose names match either yiidb* or yiiwebHttpException:*, but not yiiwebHttpException:404.
[
'class' => 'yiilogFileTarget',
'levels' => ['error', 'warning'],
'categories' => [
'yiidb*',
'yiiwebHttpException:*',
],
'except' => [
'yiiwebHttpException:404',
],
]
Info: When an HTTP exception is caught by the error handler, an error message
will be logged with the category name in the format ofyiiwebHttpException:ErrorCode. For example,
the yiiwebNotFoundHttpException will cause an error message of categoryyiiwebHttpException:404.
Message Formatting ¶
Log targets export the filtered log messages in a certain format. For example, if you install
a log target of the class yiilogFileTarget, you may find a log message similar to the following in the
runtime/log/app.log file:
2014-10-04 18:10:15 [::1][][-][trace][yiibaseModule::getModule] Loading module: debug
By default, log messages will be formatted as follows by the yiilogTarget::formatMessage():
Timestamp [IP address][User ID][Session ID][Severity Level][Category] Message Text
You may customize this format by configuring the yiilogTarget::$prefix property which takes a PHP callable
returning a customized message prefix. For example, the following code configures a log target to prefix each
log message with the current user ID (IP address and Session ID are removed for privacy reasons).
[
'class' => 'yiilogFileTarget',
'prefix' => function ($message) {
$user = Yii::$app->has('user', true) ? Yii::$app->get('user') : null;
$userID = $user ? $user->getId(false) : '-';
return "[$userID]";
}
]
Besides message prefixes, log targets also append some context information to each batch of log messages.
By default, the values of these global PHP variables are included: $_GET, $_POST, $_FILES, $_COOKIE,
$_SESSION and $_SERVER. You may adjust this behavior by configuring the yiilogTarget::$logVars property
with the names of the global variables that you want to include by the log target. For example, the following
log target configuration specifies that only the value of the $_SERVER variable will be appended to the log messages.
[
'class' => 'yiilogFileTarget',
'logVars' => ['_SERVER'],
]
You may configure logVars to be an empty array to totally disable the inclusion of context information.
Or if you want to implement your own way of providing context information, you may override the
yiilogTarget::getContextMessage() method.
In case some of your request fields contain sensitive information you would not like to log (e.g. passwords, access tokens),
you may additionally configure maskVars property. By default, the following request parameters will be masked with ***:
$_SERVER[HTTP_AUTHORIZATION], $_SERVER[PHP_AUTH_USER], $_SERVER[PHP_AUTH_PW], but you can set your own:
[
'class' => 'yiilogFileTarget',
'logVars' => ['_SERVER'],
'maskVars' => ['_SERVER.HTTP_X_PASSWORD']
]
Message Trace Level ¶
During development, it is often desirable to see where each log message is coming from. This can be achieved by
configuring the traceLevel property of the log component like the following:
return [
'bootstrap' => ['log'],
'components' => [
'log' => [
'traceLevel' => YII_DEBUG ? 3 : 0,
'targets' => [...],
],
],
];
The above application configuration sets traceLevel to be 3 if YII_DEBUG is on
and 0 if YII_DEBUG is off. This means, if YII_DEBUG is on, each log message will be appended with at most 3
levels of the call stack at which the log message is recorded; and if YII_DEBUG is off, no call stack information
will be included.
Info: Getting call stack information is not trivial. Therefore, you should only use this feature during development
or when debugging an application.
Message Flushing and Exporting ¶
As aforementioned, log messages are maintained in an array by the logger object. To limit the
memory consumption by this array, the logger will flush the recorded messages to the log targets
each time the array accumulates a certain number of log messages. You can customize this number by configuring
the flushInterval property of the log component:
return [
'bootstrap' => ['log'],
'components' => [
'log' => [
'flushInterval' => 100, // default is 1000
'targets' => [...],
],
],
];
Info: Message flushing also occurs when the application ends, which ensures log targets can receive complete log messages.
When the logger object flushes log messages to log targets, they do not get exported
immediately. Instead, the message exporting only occurs when a log target accumulates certain number of the filtered
messages. You can customize this number by configuring the exportInterval
property of individual log targets, like the following,
[
'class' => 'yiilogFileTarget',
'exportInterval' => 100, // default is 1000
]
Because of the flushing and exporting level setting, by default when you call Yii::debug() or any other logging
method, you will NOT see the log message immediately in the log targets. This could be a problem for some long-running
console applications. To make each log message appear immediately in the log targets, you should set both
flushInterval and exportInterval to be 1,
as shown below:
return [
'bootstrap' => ['log'],
'components' => [
'log' => [
'flushInterval' => 1,
'targets' => [
[
'class' => 'yiilogFileTarget',
'exportInterval' => 1,
],
],
],
],
];
Note: Frequent message flushing and exporting will degrade the performance of your application.
Toggling Log Targets ¶
You can enable or disable a log target by configuring its enabled property.
You may do so via the log target configuration or by the following PHP statement in your code:
Yii::$app->log->targets['file']->enabled = false;
The above code requires you to name a target as file, as shown below by using string keys in the
targets array:
return [
'bootstrap' => ['log'],
'components' => [
'log' => [
'targets' => [
'file' => [
'class' => 'yiilogFileTarget',
],
'db' => [
'class' => 'yiilogDbTarget',
],
],
],
],
];
Since version 2.0.13, you may configure enabled with a callable to
define a dynamic condition for whether the log target should be enabled or not.
See the documentation of yiilogTarget::setEnabled() for an example.
Creating New Targets ¶
Creating a new log target class is very simple. You mainly need to implement the yiilogTarget::export() method
sending the content of the yiilogTarget::$messages array to a designated medium. You may call the
yiilogTarget::formatMessage() method to format each message. For more details, you may refer to any of the
log target classes included in the Yii release.
Tip: Instead of creating your own loggers you may try any PSR-3 compatible logger such
as Monolog by using
PSR log target extension.
Performance Profiling ¶
Performance profiling is a special type of message logging that is used to measure the time taken by certain
code blocks and find out what are the performance bottlenecks. For example, the yiidbCommand class uses
performance profiling to find out the time taken by each DB query.
To use performance profiling, first identify the code blocks that need to be profiled. Then enclose each
code block like the following:
Yii::beginProfile('myBenchmark');
...code block being profiled...
Yii::endProfile('myBenchmark');
where myBenchmark stands for a unique token identifying a code block. Later when you examine the profiling
result, you will use this token to locate the time spent by the corresponding code block.
It is important to make sure that the pairs of beginProfile and endProfile are properly nested.
For example,
Yii::beginProfile('block1');
// some code to be profiled
Yii::beginProfile('block2');
// some other code to be profiled
Yii::endProfile('block2');
Yii::endProfile('block1');
If you miss Yii::endProfile('block1') or switch the order of Yii::endProfile('block1') and
Yii::endProfile('block2'), the performance profiling will not work.
For each code block being profiled, a log message with the severity level profile is recorded. You can configure
a log target to collect such messages and export them. The Yii debugger has
a built-in performance profiling panel showing the profiling results.
Логирование
Yii предоставляет мощную, гибко настраиваемую и легко расширяемую систему логирования. Эта система логирования позволяет удобным способом сохранять сообщения разных типов и фильтровать их. Сообщения могут быть сохранены в файлы, базы данных или отправлены на email.
Использование Системы логирования Yii включает следующие шаги:
- Запись сообщений лога в различных частях кода приложения;
- Настройка целей лога в конфигурации приложения;
- Изучение отфильтрованных сообщений лога, например, при помощи Отладчика Yii.
В данном разделе будем рассматривать первые два шага.
Сообщения лога
Запись сообщений лога осуществляется вызовом одного из следующих методов:
- [[Yii::debug()]]: записывает сообщения для отслеживания выполнения кода приложения. Используется, в основном, при разработке.
- [[Yii::info()]]: записывает сообщение, содержащее какую-либо полезную информацию.
- [[Yii::warning()]]: записывает тревожное сообщение при возникновении неожиданного события.
- [[Yii::error()]]: записывает критическую ошибку, на которую нужно, как можно скорее, обратить внимание.
Эти методы позволяют записывать сообщения разных уровней важности и категорий. Они имеют одинаковое описание функции function ($message, $category = 'application'), где $message передает сообщение для записи, а $category — категорию сообщения. В следующем примере будет записано trace сообщение с категорией по умолчанию application:
Yii::debug('начало вычисления среднего дохода');
Note: Сообщение может быть как строкой, так и объектом или массивом. За корректную работу с содержимым сообщения отвечают цели лога. По умолчанию, если сообщение не является строкой, оно будет приведено к строковому типу при помощи [[yiihelpersVarDumper::export()]].
Для упрощения работы с сообщениями лога и их фильтрации, рекомендуется явно указывать подходящую категорию для каждого сообщения. Возможно использование иерархической системы именования категорий, что значительно упростит целям лога фильтрацию сообщений по категориям. Простым и эффективным способом именования категорий является использование магической PHP-константы __METHOD__. Такой подход используется в ядре фреймворка Yii. Например,
Yii::debug('начало вычисления среднего дохода', __METHOD__);
Константа __METHOD__ вычисляется как имя метода (включая полное имя класса), в котором она использована. Например, её значение будет вычислено как 'appcontrollersRevenueController::calculate', если показанный выше код вызывается в соответствующем методе.
Info: методы логирования, описанные выше являются, на самом деле, ярлыками для метода [[yiilogLogger::log()|log()]] [[yiilogLogger|объекта логгера]], который доступен как синглтон
Yii::getLogger().
При определенном количестве записанных сообщений или завершении приложения, объект логгера вызывает [[yiilogDispatcher|message dispatcher]] для отправки записанных сообщений зарегистрированным целям логов.
Цели логов
Цель логов — это экземпляр класса [[yiilogTarget]] или класса, унаследованного от него. Цель фильтрует сообщения логов по уровню важности и категории, а затем выгружает их в соответствующее хранилище. Например, [[yiilogDbTarget|database target]] выгружает отфильтрованные сообщения логов в таблицу базы данных, а [[yiilogEmailTarget|email target]] отправляет сообщения логов на заданные адреса email.
При помощи компонента приложения log возможна регистрация нескольких целей логов. Пример конфигурации приложения:
return [ // Компонент "log" должен быть загружен на этапе предзагрузки 'bootstrap' => ['log'], // Компонент "log" обрабатывает сообщения с меткой времени timestamp. // Задайте временную зону для создания корректных меток времени. 'timeZone' => 'Europe/Moscow', 'components' => [ 'log' => [ 'targets' => [ [ 'class' => 'yiilogDbTarget', 'levels' => ['error', 'warning'], ], [ 'class' => 'yiilogEmailTarget', 'levels' => ['error'], 'categories' => ['yiidb*'], 'message' => [ 'from' => ['log@example.com'], 'to' => ['admin@example.com', 'developer@example.com'], 'subject' => 'Ошибки базы данных на сайте example.com', ], ], ], ], ], ];
Note: Компонент
logдолжен быть загружен в процессе предзагрузки, тогда он сможет оперативно передавать сообщения целям логов. Поэтому он указан в массивеbootstrap.
В приведенном выше коде в свойстве [[yiilogDispatcher::targets]] зарегистрированы две цели логов:
- первая цель выбирает ошибки и предупреждения и сохраняет их в базу данных;
- вторая цель выбирает ошибки с категорией, имя которой начинается с
yiidbи шлет сразу на два адреса emailadmin@example.comиdeveloper@example.com.
На данный момент, Yii содержит следующие встроенные цели логов. В документации по API подробно описана настройка и использование этих классов.
- [[yiilogDbTarget]]: сохраняет сообщения логов в таблицу базы данных.
- [[yiilogEmailTarget]]: шлет сообщения логов на заранее указанный email.
- [[yiilogFileTarget]]: сохраняет сообщения логов в файлы.
- [[yiilogSyslogTarget]]: сохраняет сообщения логов в системный лог используя функцию PHP
syslog().
Дальше рассмотрим общие для этих четырех классов возможности.
Фильтрация сообщений
Для каждой цели можно настроить свойства [[yiilogTarget::levels|levels]] и [[yiilogTarget::categories|categories]], которые указывают уровни важности и категории сообщений логов, которые цель должна обрабатывать.
Свойство [[yiilogTarget::levels|levels]] принимает массив, содержащий одно или несколько следующих значений:
error: соответствует сообщениям, сохраненным методом [[Yii::error()]].warning: соответствует сообщениям, сохраненным методом [[Yii::warning()]].info: соответствует сообщениям, сохраненным методом [[Yii::info()]].trace: соответствует сообщениям, сохраненным методом [[Yii::debug()]].profile: соответствует сообщениям, сохраненным методами [[Yii::beginProfile()]] и [[Yii::endProfile()]], подробнее о которых написано в подразделе Профилирование производительности.
Если свойство [[yiilogTarget::levels|levels]] не задано, цель логов будет обрабатывать сообщения с любым уровнем важности.
Свойство [[yiilogTarget::categories|categories]] принимает массив, содержащий имена категорий или шаблоны.
Цель будет обрабатывать только те сообщения, категория которых совпадает с одним из значений или шаблонов этого массива. Шаблон категории должен состоять из префикса имени категории и звездочки * на конце. Имя категории совпадает с шаблоном, если оно начинается с префикса шаблона. Например, yiidbCommand::execute и yiidbCommand::query используются в качестве имен категорий сообщений, записанных в классе [[yiidbCommand]]. Оба они совпадают с шаблоном yiidb*.
Если свойство [[yiilogTarget::categories|categories]] не задано, цель будет обрабатывать сообщения любой категории.
Кроме списка включаемый категорий, заданного свойством [[yiilogTarget::categories|categories]], при помощи свойства [[yiilogTarget::except|except]] возможно задать список исключаемых категорий. Если категория сообщения совпадает со значением или шаблоном из списка исключаемых категорий, такое сообщение не будет обработано.
В следующем примере показан вариант конфигурации цели логов, которая должна обрабатывать только сообщения об ошибках и предупреждениях в категориях yiidb* и yiiwebHttpException:*, за исключением yiiwebHttpException:404.
[
'class' => 'yiilogFileTarget',
'levels' => ['error', 'warning'],
'categories' => [
'yiidb*',
'yiiwebHttpException:*',
],
'except' => [
'yiiwebHttpException:404',
],
]
Note: При обработке HTTP-исключения обработчиком ошибок, сообщение будет сохранено с категорией вида
yiiwebHttpException:ErrorCode. Например, исключение [[yiiwebNotFoundHttpException]] вызовет сообщение об ошибке с категориейyiiwebHttpException:404.
Форматирование сообщений
Цели логов выгружают отфильтрованные сообщения в определенном формате. Например, цель класса [[yiilogFileTarget]] сохранит сообщение следующего формата в файле runtime/log/app.log:
2014-10-04 18:10:15 [::1][][-][trace][yiibaseModule::getModule] Loading module: debug
По умолчанию сообщения логов форматируются методом [[yiilogTarget::formatMessage()]]:
Временная метка [IP-адрес][ID пользователя][ID сессии][Уровень важности][Категория] Текст сообщения
Этот формат может быть изменен при помощи свойства [[yiilogTarget::prefix]], которое получает анонимную функцию, возвращающую нужный префикс сообщения. Например, следующий код позволяет настроить вывод идентификатор текущего пользователя в качестве префикса для всех сообщений.
[
'class' => 'yiilogFileTarget',
'prefix' => function ($message) {
$user = Yii::$app->has('user', true) ? Yii::$app->get('user') : null;
$userID = $user ? $user->getId(false) : '-';
return "[$userID]";
}
]
Кроме префиксов сообщений, также возможно добавление общей информации для каждого набора сообщений лога.
По умолчанию включаются значения следующих глобальных PHP-переменных: $_GET, $_POST, $_FILES, $_COOKIE,
$_SESSION и $_SERVER. Эта возможность настраивается при помощи свойства [[yiilogTarget::logVars]], содержащего массив имен переменных, которые необходимо включить в лог. Например, следующий код позволяет настроить цель логов так, чтобы к сообщениям присоединялось только содержимое переменной $_SERVER.
[
'class' => 'yiilogFileTarget',
'logVars' => ['_SERVER'],
]
При задании значением свойства logVars пустого массива, общая информация не будет выводиться.
Для определения собственного алгоритма подключения общей информации, следует переопределить метод [[yiilogTarget::getContextMessage()]].
Уровень отслеживания выполнения кода
В процессе разработки, часто бывает необходимость видеть источники сообщений. Для этого нужно использовать свойство [[yiilogDispatcher::traceLevel|traceLevel]] компонента log. Например,
return [ 'bootstrap' => ['log'], 'components' => [ 'log' => [ 'traceLevel' => YII_DEBUG ? 3 : 0, 'targets' => [...], ], ], ];
При такой настройке свойство [[yiilogDispatcher::traceLevel|traceLevel]] будет равно 3 при YII_DEBUG равном true и 0 при YII_DEBUG равном false. Это означает, что при включенном YII_DEBUG, каждое сообщение лога будет содержать до трех уровней стека вызовов, а при выключенном YII_DEBUG информация о стеке вызовов не будет включаться в лог.
Info: Получение информации стека вызовов является не простым процессом. Поэтому такую возможность следует использовать только при разработке или отладке приложения.
Передача на обработку и выгрузка сообщений
Как упоминалось выше, сообщения логов обрабатываются в массиве [[yiilogLogger|объектом логгера]]. Для ограничения объема памяти, занятого этим массивом, при накоплении определенного числа сообщений, логгер передает их на обработку целям логов. Максимальное количество сообщений определяется свойством [[yiilogDispatcher::flushInterval|flushInterval]] компонента log:
return [ 'bootstrap' => ['log'], 'components' => [ 'log' => [ 'flushInterval' => 100, // по умолчанию 1000 'targets' => [...], ], ], ];
Info: При завершении приложения, так же происходит передача сообщений на обработку.
После передачи сообщений [[yiilogLogger|объектом логгера]] в цели логов, сообщения не выгружаются немедленно. Вместо этого, выгрузка сообщений происходит когда цель логов накопит определенное количество фильтрованных сообщений. Максимальное количество сообщений определяется свойством [[yiilogTarget::exportInterval|exportInterval]] цели логов. Например,
[
'class' => 'yiilogFileTarget',
'exportInterval' => 100, // по умолчанию 1000
]
Из-за того, что значения максимального количества сообщений для передачи и выгрузки по умолчанию достаточно велико, при вызове метода Yii::debug() или любого другого метода логирования, сообщение не появится сразу в файле или таблице базы данных. Такое поведение может стать проблемой, например, в консольных приложениях с большим временем исполнения. Для того, чтобы все сообщения логов сразу попадали в лог, необходимо установить значения свойств [[yiilogDispatcher::flushInterval|flushInterval]] и [[yiilogTarget::exportInterval|exportInterval]] равными 1, например так:
return [ 'bootstrap' => ['log'], 'components' => [ 'log' => [ 'flushInterval' => 1, 'targets' => [ [ 'class' => 'yiilogFileTarget', 'exportInterval' => 1, ], ], ], ], ];
Note: Частая передача и выгрузка сообщений может сильно снизить производительность приложения.
Переключение целей логов
Свойство [[yiilogTarget::enabled|enabled]] отвечает за включение или отключение цели логов. Возможно управлением этим свойством как в конфигурации приложения, так и при помощи следующего PHP-кода:
Yii::$app->log->targets['file']->enabled = false;
В данном примере используется цель логов file, которая может быть настроена в конфигурации приложения следующим образом:
return [ 'bootstrap' => ['log'], 'components' => [ 'log' => [ 'targets' => [ 'file' => [ 'class' => 'yiilogFileTarget', ], 'db' => [ 'class' => 'yiilogDbTarget', ], ], ], ], ];
Создание новых целей
Создание новой цели логов не является сложной задачей. В общем случае нужно реализовать метод [[yiilogTarget::export()]], выгружающий массив [[yiilogTarget::messages]] в место хранения логов. Возможно использование метода [[yiilogTarget::formatMessage()]] для форматирования сообщения. Детали реализации можно подсмотреть в исходном коде любого из классов целей логов, включенных в состав Yii.
Профилирование производительности
Профилирование производительности — это специальный тип сообщений логов, используемый для измерения времени выполнения определенных участков кода и определения проблем производительности. Например, класс [[yiidbCommand]] использует профилирование производительности для определения времени исполнения каждого запроса базы данных.
Для использования профилирования производительности нужно определить участок кода для измерения и обернуть его вызовами методов [[Yii::beginProfile()]] и [[Yii::endProfile()]]. Например,
Yii::beginProfile('myBenchmark'); // участок кода для профилирования... Yii::endProfile('myBenchmark');
где myBenchmark является уникальным идентификатором данного измеряемого участка кода.
В дальнейшем, при изучении результатов профилирования, уникальный идентификатор поможет определить время выполнения соответствующего участка кода.
Очень важно соблюдать уровни вложенности пар beginProfile и endProfile. Например,
Yii::beginProfile('block1'); // код для профилирования Yii::beginProfile('block2'); // другой код для профилирования Yii::endProfile('block2'); Yii::endProfile('block1');
Если пропустить Yii::endProfile('block1') или поменять местами Yii::endProfile('block1') и
Yii::endProfile('block2'), профилирование производительности не будет работать.
Для каждого участка кода, будет записано сообщение лога с уровнем важности profile. Для сбора таких сообщений можно настроить цель логов или воспользоваться Отладчиком Yii, который имеет встроенную панель профилирования производительности, отображающую результаты измерений.
Yii provides a powerful logging framework that is highly customizable and extensible. Using this framework, you
can easily log various types of messages, filter them, and gather them at different targets, such as files, databases,
emails.
Using the Yii logging framework involves the following steps:
- Record log messages at various places in your code;
- Configure log targets in the application configuration to filter and export log messages;
- Examine the filtered logged messages exported by different targets (e.g. the Yii debugger).
In this section, we will mainly describe the first two steps.
Log Messages
Recording log messages is as simple as calling one of the following logging methods:
- [[Yii::trace()]]: record a message to trace how a piece of code runs. This is mainly for development use.
- [[Yii::info()]]: record a message that conveys some useful information.
- [[Yii::warning()]]: record a warning message that indicates something unexpected has happened.
- [[Yii::error()]]: record a fatal error that should be investigated as soon as possible.
These logging methods record log messages at various severity levels and categories. They share
the same function signature function ($message, $category = 'application'), where $message stands for
the log message to be recorded, while $category is the category of the log message. The code in the following
example records a trace message under the default category application:
Yii::trace('start calculating average revenue');
Info: Log messages can be strings as well as complex data, such as arrays or objects. It is the responsibility
of log targets to properly deal with log messages. By default, if a log message is not a string,
it will be exported as a string by calling [[yiihelpersVarDumper::export()]].
To better organize and filter log messages, it is recommended that you specify an appropriate category for each
log message. You may choose a hierarchical naming scheme for categories, which will make it easier for
log targets to filter messages based on their categories. A simple yet effective naming scheme
is to use the PHP magic constant __METHOD__ for the category names. This is also the approach used in the core
Yii framework code. For example,
Yii::trace('start calculating average revenue', __METHOD__);
The __METHOD__ constant evaluates as the name of the method (prefixed with the fully qualified class name) where
the constant appears. For example, it is equal to the string 'appcontrollersRevenueController::calculate' if
the above line of code is called within this method.
Info: The logging methods described above are actually shortcuts to the [[yiilogLogger::log()|log()]] method
of the [[yiilogLogger|logger object]] which is a singleton accessible through the expressionYii::getLogger(). When
enough messages are logged or when the application ends, the logger object will call a
[[yiilogDispatcher|message dispatcher]] to send recorded log messages to the registered log targets.
Log Targets
A log target is an instance of the [[yiilogTarget]] class or its child class. It filters the log messages by their
severity levels and categories and then exports them to some medium. For example, a [[yiilogDbTarget|database target]]
exports the filtered log messages to a database table, while an [[yiilogEmailTarget|email target]] exports
the log messages to specified email addresses.
You can register multiple log targets in an application by configuring them through the log application component
in the application configuration, like the following:
return [
// the "log" component must be loaded during bootstrapping time
'bootstrap' => ['log'],
'components' => [
'log' => [
'targets' => [
[
'class' => 'yiilogDbTarget',
'levels' => ['error', 'warning'],
],
[
'class' => 'yiilogEmailTarget',
'levels' => ['error'],
'categories' => ['yiidb*'],
'message' => [
'from' => ['log@example.com'],
'to' => ['admin@example.com', 'developer@example.com'],
'subject' => 'Database errors at example.com',
],
],
],
],
],
];
Note: The
logcomponent must be loaded during bootstrapping time so that
it can dispatch log messages to targets promptly. That is why it is listed in thebootstraparray as shown above.
In the above code, two log targets are registered in the [[yiilogDispatcher::targets]] property:
- the first target selects error and warning messages and saves them in a database table;
- the second target selects error messages under the categories whose names start with
yiidb, and sends
them in an email to bothadmin@example.comanddeveloper@example.com.
Yii comes with the following built-in log targets. Please refer to the API documentation about these classes to
learn how to configure and use them.
- [[yiilogDbTarget]]: stores log messages in a database table.
- [[yiilogEmailTarget]]: sends log messages to pre-specified email addresses.
- [[yiilogFileTarget]]: saves log messages in files.
- [[yiilogSyslogTarget]]: saves log messages to syslog by calling the PHP function
syslog().
In the following, we will describe the features common to all log targets.
Message Filtering
For each log target, you can configure its [[yiilogTarget::levels|levels]] and
[[yiilogTarget::categories|categories]] properties to specify which severity levels and categories of the messages the target should process.
The [[yiilogTarget::levels|levels]] property takes an array consisting of one or several of the following values:
error: corresponding to messages logged by [[Yii::error()]].warning: corresponding to messages logged by [[Yii::warning()]].info: corresponding to messages logged by [[Yii::info()]].trace: corresponding to messages logged by [[Yii::trace()]].profile: corresponding to messages logged by [[Yii::beginProfile()]] and [[Yii::endProfile()]], which will
be explained in more details in the Profiling subsection.
If you do not specify the [[yiilogTarget::levels|levels]] property, it means the target will process messages
of any severity level.
The [[yiilogTarget::categories|categories]] property takes an array consisting of message category names or patterns.
A target will only process messages whose category can be found or match one of the patterns in this array.
A category pattern is a category name prefix with an asterisk * at its end. A category name matches a category pattern
if it starts with the same prefix of the pattern. For example, yiidbCommand::execute and yiidbCommand::query
are used as category names for the log messages recorded in the [[yiidbCommand]] class. They both match
the pattern yiidb*.
If you do not specify the [[yiilogTarget::categories|categories]] property, it means the target will process
messages of any category.
Besides whitelisting the categories by the [[yiilogTarget::categories|categories]] property, you may also
blacklist certain categories by the [[yiilogTarget::except|except]] property. If the category of a message
is found or matches one of the patterns in this property, it will NOT be processed by the target.
The following target configuration specifies that the target should only process error and warning messages
under the categories whose names match either yiidb* or yiiwebHttpException:*, but not yiiwebHttpException:404.
[
'class' => 'yiilogFileTarget',
'levels' => ['error', 'warning'],
'categories' => [
'yiidb*',
'yiiwebHttpException:*',
],
'except' => [
'yiiwebHttpException:404',
],
]
Info: When an HTTP exception is caught by the error handler, an error message
will be logged with the category name in the format ofyiiwebHttpException:ErrorCode. For example,
the [[yiiwebNotFoundHttpException]] will cause an error message of categoryyiiwebHttpException:404.
Message Formatting
Log targets export the filtered log messages in a certain format. For example, if you install
a log target of the class [[yiilogFileTarget]], you may find a log message similar to the following in the
runtime/log/app.log file:
2014-10-04 18:10:15 [::1][][-][trace][yiibaseModule::getModule] Loading module: debug
By default, log messages will be formatted as follows by the [[yiilogTarget::formatMessage()]]:
Timestamp [IP address][User ID][Session ID][Severity Level][Category] Message Text
You may customize this format by configuring the [[yiilogTarget::prefix]] property which takes a PHP callable
returning a customized message prefix. For example, the following code configures a log target to prefix each
log message with the current user ID (IP address and Session ID are removed for privacy reasons).
[
'class' => 'yiilogFileTarget',
'prefix' => function ($message) {
$user = Yii::$app->has('user', true) ? Yii::$app->get('user') : null;
$userID = $user ? $user->getId(false) : '-';
return "[$userID]";
}
]
Besides message prefixes, log targets also append some context information to each batch of log messages.
By default, the values of these global PHP variables are included: $_GET, $_POST, $_FILES, $_COOKIE,
$_SESSION and $_SERVER. You may adjust this behavior by configuring the [[yiilogTarget::logVars]] property
with the names of the global variables that you want to include by the log target. For example, the following
log target configuration specifies that only the value of the $_SERVER variable will be appended to the log messages.
[
'class' => 'yiilogFileTarget',
'logVars' => ['_SERVER'],
]
You may configure logVars to be an empty array to totally disable the inclusion of context information.
Or if you want to implement your own way of providing context information, you may override the
[[yiilogTarget::getContextMessage()]] method.
Message Trace Level
During development, it is often desirable to see where each log message is coming from. This can be achieved by
configuring the [[yiilogDispatcher::traceLevel|traceLevel]] property of the log component like the following:
return [
'bootstrap' => ['log'],
'components' => [
'log' => [
'traceLevel' => YII_DEBUG ? 3 : 0,
'targets' => [...],
],
],
];
The above application configuration sets [[yiilogDispatcher::traceLevel|traceLevel]] to be 3 if YII_DEBUG is on
and 0 if YII_DEBUG is off. This means, if YII_DEBUG is on, each log message will be appended with at most 3
levels of the call stack at which the log message is recorded; and if YII_DEBUG is off, no call stack information
will be included.
Info: Getting call stack information is not trivial. Therefore, you should only use this feature during development
or when debugging an application.
Message Flushing and Exporting
As aforementioned, log messages are maintained in an array by the [[yiilogLogger|logger object]]. To limit the
memory consumption by this array, the logger will flush the recorded messages to the log targets
each time the array accumulates a certain number of log messages. You can customize this number by configuring
the [[yiilogDispatcher::flushInterval|flushInterval]] property of the log component:
return [
'bootstrap' => ['log'],
'components' => [
'log' => [
'flushInterval' => 100, // default is 1000
'targets' => [...],
],
],
];
Info: Message flushing also occurs when the application ends, which ensures log targets can receive complete log messages.
When the [[yiilogLogger|logger object]] flushes log messages to log targets, they do not get exported
immediately. Instead, the message exporting only occurs when a log target accumulates certain number of the filtered
messages. You can customize this number by configuring the [[yiilogTarget::exportInterval|exportInterval]]
property of individual log targets, like the following,
[
'class' => 'yiilogFileTarget',
'exportInterval' => 100, // default is 1000
]
Because of the flushing and exporting level setting, by default when you call Yii::trace() or any other logging
method, you will NOT see the log message immediately in the log targets. This could be a problem for some long-running
console applications. To make each log message appear immediately in the log targets, you should set both
[[yiilogDispatcher::flushInterval|flushInterval]] and [[yiilogTarget::exportInterval|exportInterval]] to be 1,
as shown below:
return [
'bootstrap' => ['log'],
'components' => [
'log' => [
'flushInterval' => 1,
'targets' => [
[
'class' => 'yiilogFileTarget',
'exportInterval' => 1,
],
],
],
],
];
Note: Frequent message flushing and exporting will degrade the performance of your application.
Toggling Log Targets
You can enable or disable a log target by configuring its [[yiilogTarget::enabled|enabled]] property.
You may do so via the log target configuration or by the following PHP statement in your code:
Yii::$app->log->targets['file']->enabled = false;
The above code requires you to name a target as file, as shown below by using string keys in the
targets array:
return [
'bootstrap' => ['log'],
'components' => [
'log' => [
'targets' => [
'file' => [
'class' => 'yiilogFileTarget',
],
'db' => [
'class' => 'yiilogDbTarget',
],
],
],
],
];
Creating New Targets
Creating a new log target class is very simple. You mainly need to implement the [[yiilogTarget::export()]] method
sending the content of the [[yiilogTarget::messages]] array to a designated medium. You may call the
[[yiilogTarget::formatMessage()]] method to format each message. For more details, you may refer to any of the
log target classes included in the Yii release.
Performance Profiling
Performance profiling is a special type of message logging that is used to measure the time taken by certain
code blocks and find out what are the performance bottlenecks. For example, the [[yiidbCommand]] class uses
performance profiling to find out the time taken by each DB query.
To use performance profiling, first identify the code blocks that need to be profiled. Then enclose each
code block like the following:
Yii::beginProfile('myBenchmark');
...code block being profiled...
Yii::endProfile('myBenchmark');
where myBenchmark stands for a unique token identifying a code block. Later when you examine the profiling
result, you will use this token to locate the time spent by the corresponding code block.
It is important to make sure that the pairs of beginProfile and endProfile are properly nested.
For example,
Yii::beginProfile('block1');
// some code to be profiled
Yii::beginProfile('block2');
// some other code to be profiled
Yii::endProfile('block2');
Yii::endProfile('block1');
If you miss Yii::endProfile('block1') or switch the order of Yii::endProfile('block1') and
Yii::endProfile('block2'), the performance profiling will not work.
For each code block being profiled, a log message with the severity level profile is recorded. You can configure
a log target to collect such messages and export them. The Yii debugger has
a built-in performance profiling panel showing the profiling results.
В этой записи пойдет речь о логах в Yii2 basic, а точнее о том, как из записывать, почему они вам пригодятся и как их читать.
Эта запись относится к курсу Yii2 basic.
Для чего они нужны
- безопасность — если ваш сайт взломают или начнут происходить странные действия, которые вы не ожидали, то логи помогут восстановить хронологию событий чтобы разобраться из-за чего все началось и как это исправить
- неисправности — если у вас что-то перестало работать, то это должно быть записано в логи, чтобы это можно было исправить
Какие бывают логи
В Yii2 можно записывать логи четырьмя основными способами:
- В базу, используя — yiilogDbTarget
- Отправка по email, который заранее прописывается в конфиг, используя — yiilogEmailTarget
- В файл, используя класс — yiilogFileTarget (самый распространенный вариант)
- Сохранять логи в syslog, вызывая функцию
syslog(), используя — yiilogSyslogTarget
Чаще всего, я сохраняю логи в файл используя FileTarget и в этом посте, я буду описывать как им пользоваться.
Настройка логов
Чтобы начать пользоваться логами, в config/web.php, внутри components нужно указать следующее:
...
'log' => [
'traceLevel' => YII_DEBUG ? 3 : 0,
'targets' => [
[
'class' => 'yiilogFileTarget',
'levels' => ['error', 'warning'],
],
],
],
...
После levels добавьте следующую строку:
'logVars' => ['_GET', '_POST'],
Чтобы убрать лишнюю ненужную информацию из логов по типу $_SERVER, $_COOKIE и др.
В Yii2 basic, логгирование уже добавлено, поэтому никаких изменения делать не нужно. В случае, если будут какие-то не совпадения с тем, что я буду описывать ниже, то проверьте наличие компонента, как описано выше.
Уровни логов
Есть несколько уровней логов (levels из кода выше):
error— обычная ошибка, записывается сYii::error()warning— лог, уровня внимания, записывается сYii::warning()info— обычный лог, информативного уровня, записывается сYii::info()trace— лог, который может принимать в себя массивы, объекты и другие структуры, вызывается сYii:trace(). В логе, они будут выведены в читабельном формате, используя статический методexport()изyiihelpersVarDumper.profile— используется для расчета работы определенного участка кода. Вызывается сYii::beginProfile('название_профиля')иYii::endProfile('название_профиля'). То есть, когда код дойдет доYii::beginProfile(), в лог запишется, что профиль запущен, когда скрипт дойдет доendProfile(), в лог, также запишется о его завершении. В итоге, можно будет посмотреть сколько было потрачено времени между запуском профиля и его окончанием.название_профиля— это ID профиля, который должен быть уникальным.
Запись логов
Зайдите в controllers/CarController.php, найдите actionIndex() и замените его на это:
public function actionIndex()
{
$searchModel = new CarSearch();
$dataProvider = $searchModel->search(Yii::$app->request->queryParams);
Yii::info("Отображаем весь список машин.");
return $this->render('index', [
'searchModel' => $searchModel,
'dataProvider' => $dataProvider,
]);
}
Выделенная строка сверху — это место, где будет записывать сообщение «Отображаем весь список машин.» в лог на уровне info (информативное сообщение).
Не забудьте в самом конце этой записи удалить
Yii::info()из actionIndex().
Все логи, по умолчанию записываются в runtime/logs/, внутри файла app.log. Когда он становится слишком большой, у него появляются версии — app.log.1, app.log.2 и т.д. до пяти вариаций по умолчанию (вы можете сделать больше, если вам требуется).
Чтение логов
План действий (пока ничего не делайте, после этих двух этапов будет описано подробнее как и что нужно делать):
- Откроем
app.logи посмотрим на него (используя командуtailс флагом-f) в консоли - Зайдем на
http://yii.loc/carи вapp.logдолжны появиться новые изменения, включая сообщение «Отображаем весь список машин.»
Первое:
$ cd /var/www/yii-dev/runtime/logs/ $ tail -f app.log
У вас должна показаться маленькая часть лога, но это так и должно быть.
Второе:
Теперь зайдите на страницу http://yii.loc/car и следите за консолью, у вас должны начать появляться новые строчки. Должно быть похожее на следующее:
kcu.constraint_schema = rc.constraint_schema AND
kcu.constraint_name = rc.constraint_name
WHERE rc.constraint_schema = database() AND kcu.table_schema = database()
AND rc.table_name = 'cars' AND kcu.table_name = 'cars'
in /var/www/yii-dev/models/search/CarSearch.php:44
in /var/www/yii-dev/controllers/CarController.php:33
2017-09-02 22:11:09 [127.0.0.1][-][-][info][application] Отображаем весь список машин.
in /var/www/yii-dev/controllers/CarController.php:35
2017-09-02 22:11:09 [127.0.0.1][-][-][info][yiidbCommand::query] SELECT COUNT(*) FROM `cars`
in /var/www/yii-dev/views/car/index.php:40
in /var/www/yii-dev/controllers/CarController.php:40
2017-09-02 22:11:09 [127.0.0.1][-][-][info][yiidbCommand::query] SELECT * FROM `cars` LIMIT 20
in /var/www/yii-dev/views/car/index.php:40
in /var/www/yii-dev/controllers/CarController.php:40
2017-09-02 22:11:09 [127.0.0.1][-][-][info][yiiwebSession::open] Session started
in /var/www/yii-dev/views/layouts/main.php:43
in /var/www/yii-dev/controllers/CarController.php:40
Выделенная строка — это и есть то, что мы указали используя Yii::info(). Как видите, у сообщения следующий формат:
2017-09-02 22:11:09 [127.0.0.1][-][-][info][application]
Где вначале — это дата и время, потом IP — сейчас это localhost, два пустых значения, которые не указаны, и что уровень — это «info». В самом конце выводится текст лога.
Фильтр логов
Теперь представьте, что у вас большое приложение и куча логов. У вас наверняка нет возможности читать все что там написано и нужно отфильтровать только самое нужно.
Например, давайте покажем только сообщения с уровнем «[info]». Мы так же будет использовать tail, но с использованием grep:
$ tail -f app.log | grep "[info]"
«[» и «]» нужно экранизировать, используя «», так как иначе выведутся все логи, в которых есть буквы «i», «n», «f» и «o» — что нам НЕ нужно.
Теперь зайдите на /car и следите за консолью. У вас должно отображаться что-то похожее на это:
Если вы не видите изменений в логах при tail -f
Вероятнее всего, у вас НЕ стоят права на доступ к файлу у Apache — это решается просто:
$ sudo cd /var/www/yii/dev/ $ sudo chown -R www-data:www-data runtime/*
Теперь снова попробуйте описываемые инструкции выше, по поводу tail и в этот раз должно получиться.
Если логи до сих пор не записываются, тогда напишите комментарий ниже к этой записи, я постараюсь вам помочь.
Сделаем настоящие логи
Нам нужно подправить , actionUpdate() и actionDelete() в controllers/CarController.php, потому что в них происходят самые важные изменения данных в базе и если они будут происходить третьими лицами — это будет плохо, поэтому нужно всё логировать.
Вы можете добавить логи для
actionCreate(), но это уже на ваше личное усмотрение.
Откройте CarController и замените behaviors() на следующее:
public function behaviors()
{
return [
'access' => [
'class' => AccessControl::className(),
'only' => ['index', 'view', 'create', 'delete'],
'rules' => [
[
'actions' => ['index', 'view', 'create', 'delete'],
'allow' => true,
'roles' => ['@'],
],
],
],
'verbs' => [
'class' => VerbFilter::className(),
'actions' => [
'delete' => ['POST'],
],
],
];
}
Выделенные строки выше — это те, которые были добавлены. Мы добавили новое значение 'access', которое определяет доступ к action’ам. Мы указываем, что хотим использовать класс AccessControll, говорим, что хотим фильтровать только (only): index, view, create и delete методы. Разрешаем доступ (allow), только для авторизованных пользователей ('roles' => '@'). «@» означает, что доступ будет только у авторизованных пользователей. Альтернатива для гостей будет — «?».
Так же нам нужно создать приватный атрибут $_user перед behavious(), который будет держать текущую информацию о пользователе. Выглядит это следующим образом:
class CarController extends Controller
{
/**
* @var null|User
*/
private $_user = null;
public function behaviors()
{
...
Нам так же нужно заполнить $_user информацией о текущем пользователе. В Yii2, для получения данных о текущем пользователе используется команда Yii::$app->user. У нее есть множества вариантов использования, например узнать пользователь авторизован или нет (Yii::$app->user->isGuest) или же получить данные о пользователе (Yii::$app->user->identity). Нас интересует Yii::$app->user->identity.
После behaviours() добавьте новый метод beforeAction():
/**
* @inheritdoc
*/
public function beforeAction($action)
{
if ($this->_user === null)
$this->_user = Yii::$app->user->identity;
return parent::beforeAction($action);
}
Метод beforeAction() запускается до того как идет использование action’ов контроллера. Тем самым, каждый раз когда вы будите заходит на car/index и др, перед этим в $_user будут записаны данные от текущего, авторизованного пользователя. Это нам поможет понят кто именно изменяет или удаляет машину.
Теперь мы можем записывать логи.
actionUpdate()
public function actionUpdate($id)
{
$model = $this->findModel($id);
if ($model->load(Yii::$app->request->post())) {
if ($model->save()) {
Yii::info("Машина ID: {$model->id} была изменена {$this->_user->username}");
} else {
Yii::error("Машина ID: {$model->id} НЕ была изменена {$this->_user->username}. Возможные ошибки модели: " . json_encode($model->errors));
}
return $this->redirect(['view', 'id' => $model->id]);
} else {
return $this->render('update', [
'model' => $model,
]);
}
}
Из actionUpdate(), я вытащил метод save() из первоначального if и вставил внутри него, чтобы записывать нужные мне логи при save(), так как он возвращает булевое значение. Если машина, сохранена, тогда я записываю в логи, что все окей, используя статический метод Yii::info(), в случае, если не удалось сохранить, то в действие идет Yii::error(), который запишет ошибку.
actionDelete()
public function actionDelete($id)
{
$model = $this->findModel($id);
$modelId = $model->id;
if ($model->delete()) {
Yii::info("Машина ID: {$modelId} была удалена {$this->_user->username}");
} else {
Yii::error("Машина ID: {$modelId} НЕ была удалена {$this->_user->username}. Возможные ошибки модели: " . json_encode($model->errors));
}
return $this->redirect(['index']);
}
Логика этого action’а почти схожа с actionUpdate(), только в этом используется метод помощник — delete(), который удаляет запись из таблицы. Это достаточно серьезный action и мы должны понимать кто и что удалил, потому что если будет несколько пользователей и каждый «втихоря» будет удалять автомобили и вам нужно будет потом разобраться кто и что удалил, то логи вам в этом помогут.
Тестируем собственные логи
Вернемся немного назад и вспомним про tail -f и grep, которые мы использовали для чтения логов. Сейчас они там тоже пригодятся.
Чтобы понимать, что все работает как нужно:
- зайдите на страницу изменения любого автомобиля (если у вас нет, то добавьте один).
- откройте консоль, зайдите в директорию с логами:
/var/www/yii-dev/runtime/logs/и начните следить за данными фильтруя их:
$ tail -f app.log | grep "[info]"
Теперь введите другое название автомобиля и нажмите сохранить. В консоли, вы должны увидеть следующее:
2017-09-09 11:41:51 [127.0.0.1][1][-][info][yiidbConnection::open] Opening DB connection: mysql:host=localhost;dbname=yii_loc 2017-09-09 11:41:51 [127.0.0.1][1][-][info][yiidbCommand::query] SHOW FULL COLUMNS FROM `user` 2017-09-09 11:41:51 [127.0.0.1][1][-][info][yiidbCommand::query] SELECT 2017-09-09 11:41:51 [127.0.0.1][1][-][info][yiidbCommand::query] SHOW FULL COLUMNS FROM `cars` 2017-09-09 11:41:51 [127.0.0.1][1][-][info][yiidbCommand::query] SELECT 2017-09-09 11:41:51 [127.0.0.1][1][-][info][yiidbCommand::query] SELECT * FROM `cars` WHERE `id`='7' 2017-09-09 11:41:51 [127.0.0.1][1][-][info][application] Машина ID: 7 была изменена admin 2017-09-09 11:41:51 [127.0.0.1][1][-][info][application] $_GET = [ 2017-09-09 11:41:51 [127.0.0.1][1][-][info][yiiwebSession::open] Session started
Выделенная строка — это то, что вам интересует. Тоже самое должно произойти, когда вы попытаетесь удалить автомобиль. Но это уже проверите сами 
Yii2 debug модуль
Yii2 выпустил свой дебаг модуль (yiigiiModule), который очень удобный в использовании и 100% вам пригодится во время разработки для сравнивания скорости загрузки, SQL запросов и другим показателям.
Визуально этот модуль должен находится в правом нижнем углу:
Если нажать на стрелочку, то он откроется и выглядит это следующим образом:
Но это не все, вы можете открыть еще больше. Давайте возьмем скрин выше и попробуем его разобрать:
- нажав на вкладку «Log 10», где 10 — это кол-во логов записанное при открытии текущей страницы
- так же, тут показывается сколько времени ушло на открытие страницы (36 миллисекунд), и что для этого понадобилось всего 3.5 мб памяти
- сделано 6 SQL запросов и на это ушло всего 4 миллисекунды
- Так же, показывается информацию о текущем пользователе, если вы нажмете на «User 1»
- вы можете посмотреть все
js/cssи др. assets который были подгружены нажав на «Asset Bundles», всего 6
Читать далее — Yii2 basic: Запускаем проект, выходим в production
Логирование
Yii предоставляет мощную, гибко настраиваемую и легко расширяемую систему логирования. Эта система логирования позволяет удобным способом сохранять сообщения разных типов и фильтровать их. Сообщения могут быть сохранены в файлы, базы данных или отправлены на email.
Использование Системы логирования Yii включает следующие шаги:
- Запись сообщений лога в различных частях кода приложения;
- Настройка целей лога в конфигурации приложения;
- Изучение отфильтрованных сообщений лога, например, при помощи Отладчика Yii.
В данном разделе будем рассматривать первые два шага.
- Сообщения лога
- Цели логов
- Фильтрация сообщений
- Форматирование сообщений
- Уровень отслеживания выполнения кода
- Передача на обработку и выгрузка сообщений
- Переключение целей логов
- Создание новых целей
- Профилирование производительности
Сообщения лога
Запись сообщений лога осуществляется вызовом одного из следующих методов:
- [[Yii::debug()]]: записывает сообщения для отслеживания выполнения кода приложения. Используется, в основном, при разработке.
- [[Yii::info()]]: записывает сообщение, содержащее какую-либо полезную информацию.
- [[Yii::warning()]]: записывает тревожное сообщение при возникновении неожиданного события.
- [[Yii::error()]]: записывает критическую ошибку, на которую нужно, как можно скорее, обратить внимаение.
Эти методы позволяют записывать сообщения разных уровней важности и категорий. Они имеют одинаковое описание функции function ($message, $category = 'application'), где $message передает сообщение для записи, а $category — категорию сообщения. В следующем примере будет записано trace сообщение с категорией по умолчанию application:
Yii::debug('начало вычисления среднего дохода');
Note: Сообщение может быть как строкой, так и объектом или массивом. За корректную работу с содержимым сообщения отвечают цели лога. По умолчанию, если сообщение не является строкой, оно будет приведено к строковому типу при помощи [[yiihelpersVarDumper::export()]].
Для упрощения работы с сообщениями лога и их фильтрации, рекомендуется явно указывать подходящую категорию для каждого сообщения. Возможно использование иерархической системы именования категорий, что значительно упростит целям лога фильтрацию сообщений по категориям. Простым и эффективным способом именования категорий является использование магической PHP-константы __METHOD__. Такой подход используется в ядре фреймворка Yii. Например,
Yii::debug('начало вычисления среднего дохода', __METHOD__);
Константа __METHOD__ вычисляется как имя метода (включая полное имя класса), в котором она использована. Например, её значение будет вычислено как 'appcontrollersRevenueController::calculate', если показанный выше код вызывается в соответствующем методе.
Info: методы логирования, описанные выше являются, на самом деле, ярлыками для метода [[yiilogLogger::log()|log()]] [[yiilogLogger|объекта логгера]], который доступен как синглтон
Yii::getLogger(). При определенном количестве записанных сообщений или завершении приложения, объект логгера вызывает [[yiilogDispatcher|message dispatcher]] для отправки записанных сообщений зарегистрированным целям логов.
Цели логов
Цель логов — это экземпляр класса [[yiilogTarget]] или класса, унаследованного от него. Цель фильтрует сообщения логов по уровню важности и категории, а затем выгружает их в соответствующее хранилище. Например, [[yiilogDbTarget|database target]] выгружает отфильтрованные сообщения логов в таблицу базы данных, а [[yiilogEmailTarget|email target]] отправляет сообщения логов на заданные адреса email.
При помощи компонента приложения log возможна регистрация нескольких целей логов. Пример конфигурации приложения:
return [
// Компонент "log" должен быть загружен на этапе предзагрузки
'bootstrap' => ['log'],
// Компонент "log" обрабатывает сообщения с меткой времени timestamp.
// Задайте временную зону для создания корректных меток времени.
'timeZone' => 'Europe/Moscow',
'components' => [
'log' => [
'targets' => [
[
'class' => 'yiilogDbTarget',
'levels' => ['error', 'warning'],
],
[
'class' => 'yiilogEmailTarget',
'levels' => ['error'],
'categories' => ['yiidb*'],
'message' => [
'from' => ['[email protected]'],
'to' => ['[email protected]', '[email protected]'],
'subject' => 'Ошибки базы данных на сайте example.com',
],
],
],
],
],
];
Note: Компонент
logдолжен быть загружен в процессе предзагрузки, тогда он сможет оперативно передавать сообщения целям логов. Поэтому он указан в массивеbootstrap.
В приведенном выше коде в свойстве [[yiilogDispatcher::targets]] зарегистрированы две цели логов:
- первая цель выбирает ошибки и предупреждения и сохраняет их в базу данных;
- вторая цель выбирает ошибки с категорией, имя которой начинается с
yiidbи шлет сразу на два адреса email[email protected]и[email protected].
На данный момент, Yii содержит следующие встроенные цели логов. В документации по API подробно описана настройка и использование этих классов.
- [[yiilogDbTarget]]: сохраняет сообщения логов в таблицу базы данных.
- [[yiilogEmailTarget]]: шлет сообщения логов на заранее указанный email.
- [[yiilogFileTarget]]: сохраняет сообщения логов в файлы.
- [[yiilogSyslogTarget]]: сохраняет сообщения логов в системный лог используя функцию PHP
syslog().
Дальше рассмотрим общие для этих четырех классов возможности.
Фильтрация сообщений
Для каждой цели можно настроить свойства [[yiilogTarget::levels|levels]] и [[yiilogTarget::categories|categories]], которые указывают уровни важности и категории сообщений логов, которые цель должна обрабатывать.
Свойство [[yiilogTarget::levels|levels]] принимает массив, содержащий одно или несколько следующих значений:
error: соответствует сообщениям, сохраненным методом [[Yii::error()]].warning: соответствует сообщениям, сохраненным методом [[Yii::warning()]].info: соответствует сообщениям, сохраненным методом [[Yii::info()]].trace: соответствует сообщениям, сохраненным методом [[Yii::debug()]].profile: соответствует сообщениям, сохраненным методами [[Yii::beginProfile()]] и [[Yii::endProfile()]], подробнее о которых написано в подразделе Профилирование производительности.
Если свойство [[yiilogTarget::levels|levels]] не задано, цель логов будет обрабатывать сообщения с любым уровнем важности.
Свойство [[yiilogTarget::categories|categories]] принимает массив, содержащий имена категорий или шаблоны. Цель будет обрабатывать только те сообщения, категория которых совпадает с одним из значений или шаблонов этого массива. Шаблон категории должен состоять из префикса имени категории и звездочки * на конце. Имя категории совпадает с шаблоном, если оно начинается с префикса шаблона. Например, yiidbCommand::execute и yiidbCommand::query используются в качестве имен категорий сообщений, записанных в классе [[yiidbCommand]]. Оба они совпадают с шаблоном yiidb*.
Если свойство [[yiilogTarget::categories|categories]] не задано, цель будет обрабатывать сообщения любой категории.
Кроме списка включаемый категорий, заданного свойством [[yiilogTarget::categories|categories]], при помощи свойства [[yiilogTarget::except|except]] возможно задать список исключаемых категорий. Если категория сообщения совпадает со значением или шаблоном из списка исключаемых категорий, такое сообщение не будет обработано.
В следующем примере показан вариант конфигурации цели логов, которая должна обрабатывать только сообщения об ошибках и предупреждениях в категориях yiidb* и yiiwebHttpException:*, за исключением yiiwebHttpException:404.
[
'class' => 'yiilogFileTarget',
'levels' => ['error', 'warning'],
'categories' => [
'yiidb*',
'yiiwebHttpException:*',
],
'except' => [
'yiiwebHttpException:404',
],
]
Note: При обработке HTTP-исключения обработчиком ошибок, сообщение будет сохранено с категорией вида
yiiwebHttpException:ErrorCode. Например, исключение [[yiiwebNotFoundHttpException]] вызовет сообщение об ошибке с категориейyiiwebHttpException:404.
Форматирование сообщений
Цели логов выгружают отфильтрованные сообщения в определенном формате. Например, цель класса [[yiilogFileTarget]] сохранит сообщение следующего формата в файле runtime/log/app.log:
2014-10-04 18:10:15 [::1][][-][trace][yiibaseModule::getModule] Loading module: debug
По умолчанию сообщения логов форматируются методом [[yiilogTarget::formatMessage()]]:
Временная метка [IP-адрес][ID пользователя][ID сессии][Уровень важности][Категория] Текст сообщения
Этот формат может быть изменен при помощи свойства [[yiilogTarget::prefix]], которое получает анонимную функцию, возвращающую нужный префикс сообщения. Например, следующий код позволяет настроить вывод идентификатор текущего пользователя в качестве префикса для всех сообщений.
[
'class' => 'yiilogFileTarget',
'prefix' => function ($message) {
$user = Yii::$app->has('user', true) ? Yii::$app->get('user') : null;
$userID = $user ? $user->getId(false) : '-';
return "[$userID]";
}
]
Кроме префиксов сообщений, также возможно добавление общей информации для каждого набора сообщений лога. По умолчанию включаются значения следующих глобальных PHP-переменных: $_GET, $_POST, $_FILES, $_COOKIE, $_SESSION и $_SERVER. Эта возможность настраивается при помощи свойства [[yiilogTarget::logVars]], содержащего массив имен переменных, которые необходимо включить в лог. Например, следующий код позволяет настроить цель логов так, чтобы к сообщениям присоединялось только содержимое переменной $_SERVER.
[
'class' => 'yiilogFileTarget',
'logVars' => ['_SERVER'],
]
При задании значением свойства logVars пустого массива, общая информация не будет выводиться. Для определения собственного алгоритма подключения общей информации, следует переопределить метод [[yiilogTarget::getContextMessage()]].
Уровень отслеживания выполнения кода
В процессе разработки, часто бывает необходимость видеть источники сообщений. Для этого нужно использовать свойство [[yiilogDispatcher::traceLevel|traceLevel]] компонента log. Например,
return [
'bootstrap' => ['log'],
'components' => [
'log' => [
'traceLevel' => YII_DEBUG ? 3 : 0,
'targets' => [...],
],
],
];
При такой настройке свойство [[yiilogDispatcher::traceLevel|traceLevel]] будет равно 3 при YII_DEBUG равном true и 0 при YII_DEBUG равном false. Это означает, что при включенном YII_DEBUG, каждое сообщение лога будет содержать до трех уровней стека вызовов, а при выключенном YII_DEBUG информация о стеке вызовов не будет включаться в лог.
Info: Получение информации стека вызовов является не простым процессом. Поэтому такую возможность следует использовать только при разработке или отладке приложения.
Передача на обработку и выгрузка сообщений
Как упоминалось выше, сообщения логов обрабатываются в массиве [[yiilogLogger|объектом логгера]]. Для ограничения объема памяти, занятого этим массивом, при накоплении определенного числа сообщений, логгер передает их на обработку целям логов. Максимальное количество сообщений определяется свойством [[yiilogDispatcher::flushInterval|flushInterval]] компонента log:
return [
'bootstrap' => ['log'],
'components' => [
'log' => [
'flushInterval' => 100, // по умолчанию 1000
'targets' => [...],
],
],
];
Info: При завершении приложения, так же происходит передача сообщений на обработку.
После передачи сообщений [[yiilogLogger|объектом логгера]] в цели логов, сообщения не выгружаются немедленно. Вместо этого, выгрузка сообщений происходит когда цель логов накопит определенное количество фильтрованных сообщений. Максимальное количество сообщений определяется свойством [[yiilogTarget::exportInterval|exportInterval]] цели логов. Например,
[
'class' => 'yiilogFileTarget',
'exportInterval' => 100, // по умолчанию 1000
]
Из-за того, что значения максимального количества сообщений для передачи и выгрузки по умолчанию достаточно велико, при вызове метода Yii::debug() или любого другого метода логирования, сообщение не появится сразу в файле или таблице базы данных. Такое поведение может стать проблемой, например, в консольных приложениях с большим временем исполнения. Для того, чтобы все сообщения логов сразу же попадали в лог, необходимо установить значения свойств [[yiilogDispatcher::flushInterval|flushInterval]] и [[yiilogTarget::exportInterval|exportInterval]] равными 1, например так:
return [
'bootstrap' => ['log'],
'components' => [
'log' => [
'flushInterval' => 1,
'targets' => [
[
'class' => 'yiilogFileTarget',
'exportInterval' => 1,
],
],
],
],
];
Note: Частая передача и выгрузка сообщений может сильно снизить производительность приложения.
Переключение целей логов
Свойство [[yiilogTarget::enabled|enabled]] отвечает за включение или отключение цели логов. Возможно управлением этим свойством как в конфигурации приложения, так и при помощи следующего PHP-кода:
Yii::$app->log->targets['file']->enabled = false;
В данном примере используется цель логов file, которая может быть настроена в конфигурации приложения следующим образом:
return [
'bootstrap' => ['log'],
'components' => [
'log' => [
'targets' => [
'file' => [
'class' => 'yiilogFileTarget',
],
'db' => [
'class' => 'yiilogDbTarget',
],
],
],
],
];
Создание новых целей
Создание новой цели логов не является сложной задачей. В общем случае нужно реализовать метод [[yiilogTarget::export()]], выгружающий массив [[yiilogTarget::messages]] в место хранения логов. Возможно использование метода [[yiilogTarget::formatMessage()]] для форматирования сообщения. Детали реализации можно подсмотреть в исходном коде любого из классов целей логов, включенных в состав Yii.
Профилирование производительности
Профилирование производительности — это специальный тип сообщений логов, используемый для измерения времени выполнения определенных участков кода и определения проблем производительности. Например, класс [[yiidbCommand]] использует профилирование производительности для определения времени исполнения каждого запроса базы данных.
Для использования профилирования производительности нужно определить участок кода для измерения и обернуть его вызовами методов [[Yii::beginProfile()]] и [[Yii::endProfile()]]. Например,
Yii::beginProfile('myBenchmark');
// участок кода для профилирования...
Yii::endProfile('myBenchmark');
где myBenchmark является уникальным идентификатором данного измеряемого участка кода. В дальнейшем, при изучении результатов профилирования, уникальный идентификатор поможет определить время выполнения соответствующего участка кода.
Очень важно соблюдать уровни вложенности пар beginProfile и endProfile. Например,
Yii::beginProfile('block1');
// код для профилирования
Yii::beginProfile('block2');
// другой код для профилирования
Yii::endProfile('block2');
Yii::endProfile('block1');
Если пропустить Yii::endProfile('block1') или поменять местами Yii::endProfile('block1') и Yii::endProfile('block2'), профилирование производительности не будет работать.
Для каждого участка кода, будет записано сообщение лога с уровнем важности profile. Для сбора таких сообщений можно настроить цель логов или воспользоваться Отладчиком Yii, который имеет встроенную панель профилирования производительности, отображающую результаты измерений.
Заберите ссылку на статью к себе, чтобы потом легко её найти!
Выберите, то, чем пользуетесь чаще всего:
Yii предоставляет мощную среду ведения журнала, которая настраивается и расширяема. Используя эту инфраструктуру, вы можете легко регистрировать различные типы сообщений, фильтровать их и собирать по различным целям, таким как файлы, базы данных и электронные письма.
Использование структуры протоколирования Yii включает в себя следующие шаги:
- Запись сообщений журнала в различных местах вашего кода;
- Настраивать цели журнала в конфигурации приложения для фильтрации и экспорта сообщений журнала;
- Изучите отфильтрованные протоколируемые сообщения, экспортированные различными целевыми объектами (например, отладчик Yii).
Логирование сообщений
Запись сообщений журнала так же просто, как вызов одного из следующих методов ведения журнала:
Yii::trace(): записать сообщение для отслеживания того, как выполняется фрагмент кода. Это в основном для использования в целях развития.Yii::info(): записать сообщение, которое передает некоторую полезную информацию.Yii::warning(): записать предупреждающее сообщение, указывающее на то, что произошло что-то неожиданное.Yii::error(): записать фатальную ошибку, которая должна быть исследована как можно скорее.
Эти методы протоколирования записывают сообщения журнала на разных уровнях и категориях серьезности. Они используют одну и ту же функцию подписи function ($message, $category = 'application'), где $message означает запись журнала, а $category — это категория сообщения журнала. В следующем примере код записывает трассировочное сообщение в приложении по умолчанию:
Yii::trace('start calculating average revenue');
Для лучшей организации и фильтрации сообщений журнала рекомендуется указывать соответствующую категорию для каждого сообщения журнала. Вы можете выбрать иерархическую схему именования категорий, что упростит лог-объекты для фильтрации сообщений по их категориям. Простая, но эффективная схема именования заключается в использовании PHP-волшебной константы __METHOD__ для имен категорий. Это также подход, используемый в базовом коде Yii. Например:
Yii::trace('start calculating average revenue', __METHOD__);
Константа __METHOD__ вычисляется как имя метода (с префиксом полного имени класса), где отображается константа. Например, она равна строке appcontrollersRevenueController::calculate, если указанная выше строка кода вызывается внутри этого метода.
Целевые лог-файлы
Цель журнала — это экземпляр класса yiilogTarget или его дочернего класса. Он фильтрует сообщения журнала по уровням и категориям серьезности, а затем экспортирует их на какой-либо носитель. Например, цель базы данных экспортирует отфильтрованные сообщения журнала в таблицу базы данных, в то время как цель электронной почты экспортирует сообщения журнала на указанные адреса электронной почты.
Вы можете зарегистрировать несколько целей журнала в приложении, настроив их через компонент приложения log в конфигурации приложения, например:
return [
// the "log" component must be loaded during bootstrapping time
'bootstrap' => ['log'],
'components' => [
'log' => [
'targets' => [
[
'class' => 'yiilogDbTarget',
'levels' => ['error', 'warning'],
],
[
'class' => 'yiilogEmailTarget',
'levels' => ['error'],
'categories' => ['yiidb*'],
'message' => [
'from' => ['log@example.com'],
'to' => ['admin@example.com', 'developer@example.com'],
'subject' => 'Database errors at example.com',
],
],
],
],
],
];
В приведенном выше коде две записи журнала регистрируются в свойстве yiilogDispatcher::$targets:
- Первая цель выбирает сообщения об ошибках и предупреждения и сохраняет их в таблице базы данных;
- Вторая цель выбирает сообщения об ошибках в категориях, имена которых начинаются с yiidb, и отправляет их по электронной почте на admin@example.com и developer@example.com.
Yii поставляется со следующими встроенными целями журнала. Обратитесь к документации по API об этих классах, чтобы узнать, как их настраивать и использовать.
yiilogDbTarget: сохраняет сообщения журнала в таблице базы данных.yiilogEmailTarget: отправляет сообщения журнала на предварительно указанные адреса электронной почты.yiilogFileTarget: сохраняет сообщения журнала в файлах.yiilogSyslogTarget: сохраняет сообщения журнала в syslog, вызывая функцию PHPsyslog().
Фильтрация сообщений
Для каждой цели журнала вы можете настроить его уровни и свойства категорий, чтобы указать, какие уровни важности и категории сообщений должны обрабатывать целевые объекты. Свойство levels принимает массив, состоящий из одного или нескольких следующих значений:
error: соответствует сообщениям, записаннымYii::error().warning: соответствует сообщениям, записаннымYii::warning().info: соответствует сообщениям, записаннымYii::info().trace: соответствует сообщениям, записаннымYii::trace().profile: соответствует сообщениям, записаннымYii::beginProfile()иYii::endProfile().
Если вы не укажете свойство levels, это означает, что цель будет обрабатывать сообщения любого уровня серьезности.
Свойство categories принимает массив, состоящий из имен категорий или шаблонов сообщений. Цель будет обрабатывать только сообщения, категория которых может быть найдена или соответствовать одному из шаблонов в этом массиве. Шаблон категории — это префикс имени категории со звездочкой * в конце. Имя категории соответствует шаблону категории, если оно начинается с того же префикса шаблона. Например, yiidbCommand::execute и yiidbCommand::query используются как имена категорий для сообщений журнала, записанных в классе yiidbCommand. Оба они соответствуют шаблону yiidb*.
Если вы не укажете свойство категорий, это означает, что цель будет обрабатывать сообщения любой категории.
Помимо добавления списков категорий по свойству категорий, вы можете также занести в черный список определенные категории за исключением свойства. Если категория сообщения найдена или совпадает с одним из шаблонов в этом свойстве, она НЕ будет обрабатываться целью.
Следующая целевая конфигурация указывает, что цель должна обрабатывать сообщения об ошибках и предупреждения только в категориях, чьи имена совпадают с именами yiidb* или yiiwebHttpException:*, но не yiiwebHttpException:404.
[
'class' => 'yiilogFileTarget',
'levels' => ['error', 'warning'],
'categories' => [
'yiidb*',
'yiiwebHttpException:*',
],
'except' => [
'yiiwebHttpException:404',
],
]
Форматирование сообщений
Лог-объекты экспортируют отфильтрованные сообщения журнала в определенном формате. Например, если вы устанавливаете цель журнала для класса yiilogFileTarget, в файле runtime/log/app.log может найтись сообщение журнала, подобное приведенному ниже:
2014-10-04 18:10:15 [::1][][-][trace][yiibaseModule::getModule] Loading module: debug
По умолчанию сообщения журнала будут отформатированы следующим образом: yiilogTarget::formatMessage():
Timestamp [IP address][User ID][Session ID][Severity Level][Category] Message Text
Вы можете настроить этот формат, настроив свойство yiilogTarget::$prefix, которое принимает вызываемый PHP, возвращая настроенный префикс сообщения. Например, следующий код настраивает цель журнала для префикса каждого сообщения журнала текущим идентификатором пользователя (IP-адрес и идентификатор сеанса удаляются из соображений конфиденциальности).
[
'class' => 'yiilogFileTarget',
'prefix' => function ($message) {
$user = Yii::$app->has('user', true) ? Yii::$app->get('user') : null;
$userID = $user ? $user->getId(false) : '-';
return "[$userID]";
}
]
Помимо префиксов сообщений, цели журналов также добавляют некоторую контекстную информацию к каждому пакету сообщений журнала. По умолчанию в эти глобальные переменные PHP включены значения: $ _GET, $ _POST, $ _FILES, $ _COOKIE, $ _SESSION и $ _SERVER. Вы можете настроить это поведение, настроив свойство yiilogTarget::$logVars с именами глобальных переменных, которые вы хотите включить в цель журнала. Например, следующая целевая конфигурация журнала указывает, что к сообщениям журнала будет добавляться только значение переменной $ _SERVER.
[
'class' => 'yiilogFileTarget',
'logVars' => ['_SERVER'],
]
Вы можете настроить logVars как пустой массив, чтобы полностью отключить включение контекстной информации. Или, если вы хотите реализовать свой собственный способ предоставления контекстной информации, вы можете переопределить метод yiilogTarget::getContextMessage().
Уровень трассировки сообщения
Во время разработки часто желательно видеть, откуда приходит каждое сообщение журнала. Этого можно достичь, настроив свойство traceLevel компонента log следующим образом:
return [
'bootstrap' => ['log'],
'components' => [
'log' => [
'traceLevel' => YII_DEBUG ? 3 : 0,
'targets' => [...],
],
],
];
Вышеприведенная конфигурация приложения устанавливает traceLevel равным 3, если YII_DEBUG включен, и 0, если YII_DEBUG выключен. Это означает, что если YII_DEBUG включен, каждое сообщение журнала будет добавлено максимум на 3 уровнях стека вызовов, в котором записано лог-сообщение; И если YII_DEBUG выключен, информация стека вызовов не будет включена.
Смывание и экспорт сообщений
Как уже упоминалось, сообщения журнала сохраняются в массиве с помощью объекта журнала. Чтобы ограничить потребление памяти этим массивом, логгер будет сбрасывать записанные сообщения в лог-цели каждый раз, когда массив накапливает определенное количество сообщений журнала. Вы можете настроить этот номер, настроив свойство flushInterval компонента log:
return [
'bootstrap' => ['log'],
'components' => [
'log' => [
'flushInterval' => 100, // default is 1000
'targets' => [...],
],
],
];
Когда объект журнала сбрасывает сообщения журнала в целях ведения журнала, они не экспортируются немедленно. Вместо этого экспорт сообщений происходит только тогда, когда цель журнала накапливает определенное количество отфильтрованных сообщений. Вы можете настроить этот номер, настроив свойство exportInterval для отдельных целей журнала, например:
[
'class' => 'yiilogFileTarget',
'exportInterval' => 100, // default is 1000
]
Из-за настроек уровня очистки и экспорта по умолчанию при вызове метода Yii::trace() или любого другого метода ведения журналов вы НЕ увидите сообщение журнала сразу в целевых журналах. Это может быть проблемой для некоторых длительных консольных приложений. Чтобы каждое сообщение журнала сразу отображалось в целевых логах, вы должны установить как flushInterval, так и exportInterval равным 1, как показано ниже:
return [
'bootstrap' => ['log'],
'components' => [
'log' => [
'flushInterval' => 1,
'targets' => [
[
'class' => 'yiilogFileTarget',
'exportInterval' => 1,
],
],
],
],
];
Переключение целевых журналов
Вы можете включить или отключить цель журнала, настроив ее свойство enabled. Это можно сделать с помощью целевой конфигурации журнала или следующего выражения PHP в коде:
Yii::$app->log->targets['file']->enabled = false;
Вышеупомянутый код требует, чтобы вы назвали цель как file, как показано ниже, используя строковые ключи в массиве targets:
return [
'bootstrap' => ['log'],
'components' => [
'log' => [
'targets' => [
'file' => [
'class' => 'yiilogFileTarget',
],
'db' => [
'class' => 'yiilogDbTarget',
],
],
],
],
];
Создание новых целей
Создание нового целевого класса журнала очень просто. В основном вам нужно реализовать метод yiilogTarget::export(), отправляющий содержимое массива yiilogTarget::$messages на указанный носитель. Вы можете вызвать метод yiilogTarget::formatMessage() для форматирования каждого сообщения. Для получения дополнительной информации вы можете обратиться к любому целевому классу журнала, включенному в выпуск Yii.
Профилирование производительности
Профилирование производительности — особый тип ведения журнала сообщений, который используется для измерения времени, затрачиваемого определенными кодовыми блоками, и определения узких мест в производительности. Например, класс yiidbCommand использует профилирование производительности, чтобы узнать время, затраченное на каждый запрос БД.
Чтобы использовать профилирование производительности, сначала определите блоки кода, которые необходимо профилировать. Затем добавьте каждый кодовый блок следующим образом:
Yii::beginProfile('myBenchmark');
...code block being profiled...
Yii::endProfile('myBenchmark');
Где myBenchmark обозначает уникальный токен, идентифицирующий блок кода. Позже, когда вы изучите результат профилирования, вы будете использовать этот токен, чтобы найти время, затраченное на соответствующий блок кода. Важно, чтобы пары beginProfile и endProfile были правильно вложены. Например:
Yii::beginProfile('block1');
// some code to be profiled
Yii::beginProfile('block2');
// some other code to be profiled
Yii::endProfile('block2');
Yii::endProfile('block1');
Если вы пропустили Yii::endProfile('block1') или изменили порядок Yii::endProfile('block1') и Yii::endProfile('block2'), профилирование производительности не будет работать.
Для каждого профилируемого кодового блока записывается лог-сообщение с профилем уровня важности. Вы можете настроить цель журнала для сбора таких сообщений и их экспорта. В отладчике Yii имеется встроенная панель профилирования производительности, отображающая результаты профилирования.


