Логирование

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__ вычисляется как имя метода (с префиксом полного имени класса), где отображается константа. Например, она равна строке app\controllers\RevenueController::calculate, если указанная выше строка кода вызывается внутри этого метода.

Целевые лог-файлы

Цель журнала - это экземпляр класса yii\log\Target или его дочернего класса. Он фильтрует сообщения журнала по уровням и категориям серьезности, а затем экспортирует их на какой-либо носитель. Например, цель базы данных экспортирует отфильтрованные сообщения журнала в таблицу базы данных, в то время как цель электронной почты экспортирует сообщения журнала на указанные адреса электронной почты.

Вы можете зарегистрировать несколько целей журнала в приложении, настроив их через компонент приложения log в конфигурации приложения, например:

return [
    // the "log" component must be loaded during bootstrapping time
    'bootstrap' => ['log'],
    
    'components' => [
        'log' => [
            'targets' => [
                [
                    'class' => 'yii\log\DbTarget',
                    'levels' => ['error', 'warning'],
                ],
                [
                    'class' => 'yii\log\EmailTarget',
                    'levels' => ['error'],
                    'categories' => ['yii\db\*'],
                    'message' => [
                       'from' => ['log@example.com'],
                       'to' => ['admin@example.com', 'developer@example.com'],
                       'subject' => 'Database errors at example.com',
                    ],
                ],
            ],
        ],
    ],
];

В приведенном выше коде две записи журнала регистрируются в свойстве yii\log\Dispatcher::$targets:

  • Первая цель выбирает сообщения об ошибках и предупреждения и сохраняет их в таблице базы данных;
  • Вторая цель выбирает сообщения об ошибках в категориях, имена которых начинаются с yii\db\, и отправляет их по электронной почте на admin@example.com и developer@example.com.

Yii поставляется со следующими встроенными целями журнала. Обратитесь к документации по API об этих классах, чтобы узнать, как их настраивать и использовать.

  • yii\log\DbTarget: сохраняет сообщения журнала в таблице базы данных.
  • yii\log\EmailTarget: отправляет сообщения журнала на предварительно указанные адреса электронной почты.
  • yii\log\FileTarget: сохраняет сообщения журнала в файлах.
  • yii\log\SyslogTarget: сохраняет сообщения журнала в syslog, вызывая функцию PHP syslog().

Фильтрация сообщений

Для каждой цели журнала вы можете настроить его уровни и свойства категорий, чтобы указать, какие уровни важности и категории сообщений должны обрабатывать целевые объекты. Свойство levels принимает массив, состоящий из одного или нескольких следующих значений:

  • error: соответствует сообщениям, записанным Yii::error().
  • warning: соответствует сообщениям, записанным Yii::warning().
  • info: соответствует сообщениям, записанным Yii::info().
  • trace: соответствует сообщениям, записанным Yii::trace().
  • profile: соответствует сообщениям, записанным Yii::beginProfile() и Yii::endProfile().

Если вы не укажете свойство levels, это означает, что цель будет обрабатывать сообщения любого уровня серьезности.

Свойство categories принимает массив, состоящий из имен категорий или шаблонов сообщений. Цель будет обрабатывать только сообщения, категория которых может быть найдена или соответствовать одному из шаблонов в этом массиве. Шаблон категории - это префикс имени категории со звездочкой * в конце. Имя категории соответствует шаблону категории, если оно начинается с того же префикса шаблона. Например, yii\db\Command::execute и yii\db\Command::query используются как имена категорий для сообщений журнала, записанных в классе yii\db\Command. Оба они соответствуют шаблону yii\db\*.

Если вы не укажете свойство категорий, это означает, что цель будет обрабатывать сообщения любой категории.

Помимо добавления списков категорий по свойству категорий, вы можете также занести в черный список определенные категории за исключением свойства. Если категория сообщения найдена или совпадает с одним из шаблонов в этом свойстве, она НЕ будет обрабатываться целью.

Следующая целевая конфигурация указывает, что цель должна обрабатывать сообщения об ошибках и предупреждения только в категориях, чьи имена совпадают с именами yii\db\* или yii\web\HttpException:*, но не yii\web\HttpException:404.

[
    'class' => 'yii\log\FileTarget',
    'levels' => ['error', 'warning'],
    'categories' => [
        'yii\db\*',
        'yii\web\HttpException:*',
    ],
    'except' => [
        'yii\web\HttpException:404',
    ],
]

Форматирование сообщений

Лог-объекты экспортируют отфильтрованные сообщения журнала в определенном формате. Например, если вы устанавливаете цель журнала для класса yii\log\FileTarget, в файле runtime/log/app.log может найтись сообщение журнала, подобное приведенному ниже:

2014-10-04 18:10:15 [::1][][-][trace][yii\base\Module::getModule] Loading module: debug

По умолчанию сообщения журнала будут отформатированы следующим образом: yii\log\Target::formatMessage():

Timestamp [IP address][User ID][Session ID][Severity Level][Category] Message Text

Вы можете настроить этот формат, настроив свойство yii\log\Target::$prefix, которое принимает вызываемый PHP, возвращая настроенный префикс сообщения. Например, следующий код настраивает цель журнала для префикса каждого сообщения журнала текущим идентификатором пользователя (IP-адрес и идентификатор сеанса удаляются из соображений конфиденциальности).

[
    'class' => 'yii\log\FileTarget',
    '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. Вы можете настроить это поведение, настроив свойство yii\log\Target::$logVars с именами глобальных переменных, которые вы хотите включить в цель журнала. Например, следующая целевая конфигурация журнала указывает, что к сообщениям журнала будет добавляться только значение переменной $ _SERVER.

[
    'class' => 'yii\log\FileTarget',
    'logVars' => ['_SERVER'],
]

Вы можете настроить logVars как пустой массив, чтобы полностью отключить включение контекстной информации. Или, если вы хотите реализовать свой собственный способ предоставления контекстной информации, вы можете переопределить метод yii\log\Target::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' => 'yii\log\FileTarget',
    'exportInterval' => 100,  // default is 1000
]

Из-за настроек уровня очистки и экспорта по умолчанию при вызове метода Yii::trace() или любого другого метода ведения журналов вы НЕ увидите сообщение журнала сразу в целевых журналах. Это может быть проблемой для некоторых длительных консольных приложений. Чтобы каждое сообщение журнала сразу отображалось в целевых логах, вы должны установить как flushInterval, так и exportInterval равным 1, как показано ниже:

return [
    'bootstrap' => ['log'],
    'components' => [
        'log' => [
            'flushInterval' => 1,
            'targets' => [
                [
                    'class' => 'yii\log\FileTarget',
                    'exportInterval' => 1,
                ],
            ],
        ],
    ],
];

Переключение целевых журналов

Вы можете включить или отключить цель журнала, настроив ее свойство enabled. Это можно сделать с помощью целевой конфигурации журнала или следующего выражения PHP в коде:

Yii::$app->log->targets['file']->enabled = false;

Вышеупомянутый код требует, чтобы вы назвали цель как file, как показано ниже, используя строковые ключи в массиве targets:

return [
    'bootstrap' => ['log'],
    'components' => [
        'log' => [
            'targets' => [
                'file' => [
                    'class' => 'yii\log\FileTarget',
                ],
                'db' => [
                    'class' => 'yii\log\DbTarget',
                ],
            ],
        ],
    ],
];

Создание новых целей

Создание нового целевого класса журнала очень просто. В основном вам нужно реализовать метод yii\log\Target::export(), отправляющий содержимое массива yii\log\Target::$messages на указанный носитель. Вы можете вызвать метод yii\log\Target::formatMessage() для форматирования каждого сообщения. Для получения дополнительной информации вы можете обратиться к любому целевому классу журнала, включенному в выпуск Yii.

Профилирование производительности

Профилирование производительности - особый тип ведения журнала сообщений, который используется для измерения времени, затрачиваемого определенными кодовыми блоками, и определения узких мест в производительности. Например, класс yii\db\Command использует профилирование производительности, чтобы узнать время, затраченное на каждый запрос БД.

Чтобы использовать профилирование производительности, сначала определите блоки кода, которые необходимо профилировать. Затем добавьте каждый кодовый блок следующим образом:

\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 имеется встроенная панель профилирования производительности, отображающая результаты профилирования.