Логгирование в Yii 2
Логгирование в Yii 2

Логирование

Yii предоставляет мощную, гибко настраиваемую и легко расширяемую систему логирования. Эта система логирования позволяет удобным способом сохранять сообщения разных типов и фильтровать их. Сообщения могут быть сохранены в файлы, базы данных или отправлены на email.

Использование Системы логирования Yii включает следующие шаги:



В данном разделе будем рассматривать первые два шага.

Сообщения лога

Запись сообщений лога осуществляется вызовом одного из следующих методов:

  • [[Yii::debug()]]: записывает сообщения для отслеживания выполнения кода приложения. Используется, в основном, при разработке.
  • [[Yii::info()]]: записывает сообщение, содержащее какую-либо полезную информацию.
  • [[Yii::warning()]]: записывает тревожное сообщение при возникновении неожиданного события.
  • [[Yii::error()]]: записывает критическую ошибку, на которую нужно, как можно скорее, обратить внимаение.

Эти методы позволяют записывать сообщения разных уровней важности и категорий. Они имеют одинаковое описание функции function ($message, $category = 'application'), где $message передает сообщение для записи, а $category - категорию сообщения. В следующем примере будет записано trace сообщение с категорией по умолчанию application:

Yii::debug('начало вычисления среднего дохода');

Note: Сообщение может быть как строкой, так и объектом или массивом. За корректную работу с содержимым сообщения отвечают цели лога. По умолчанию, если сообщение не является строкой, оно будет приведено к строковому типу при помощи [[yii\helpers\VarDumper::export()]].

Для упрощения работы с сообщениями лога и их фильтрации, рекомендуется явно указывать подходящую категорию для каждого сообщения. Возможно использование иерархической системы именования категорий, что значительно упростит целям лога фильтрацию сообщений по категориям. Простым и эффективным способом именования категорий является использование магической PHP-константы __METHOD__. Такой подход используется в ядре фреймворка Yii. Например,

Yii::debug('начало вычисления среднего дохода', __METHOD__);

Константа __METHOD__ вычисляется как имя метода (включая полное имя класса), в котором она использована. Например, её значение будет вычислено как 'app\controllers\RevenueController::calculate', если показанный выше код вызывается в соответствующем методе.

Info: методы логирования, описанные выше являются, на самом деле, ярлыками для метода [[yii\log\Logger::log()|log()]] [[yii\log\Logger|объекта логгера]], который доступен как синглтон Yii::getLogger(). При определенном количестве записанных сообщений или завершении приложения, объект логгера вызывает [[yii\log\Dispatcher|message dispatcher]] для отправки записанных сообщений зарегистрированным целям логов.

Цели логов

Цель логов - это экземпляр класса [[yii\log\Target]] или класса, унаследованного от него. Цель фильтрует сообщения логов по уровню важности и категории, а затем выгружает их в соответствующее хранилище. Например, [[yii\log\DbTarget|database target]] выгружает отфильтрованные сообщения логов в таблицу базы данных, а [[yii\log\EmailTarget|email target]] отправляет сообщения логов на заданные адреса email.

При помощи компонента приложения log возможна регистрация нескольких целей логов. Пример конфигурации приложения:

return [
    // Компонент "log" должен быть загружен на этапе предзагрузки
    'bootstrap' => ['log'],
    // Компонент "log" обрабатывает сообщения с меткой времени timestamp.
    // Задайте временную зону для создания корректных меток времени.
    'timeZone' => 'Europe/Moscow',
    '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' => 'Ошибки базы данных на сайте example.com',
                    ],
                ],
            ],
        ],
    ],
];

Note: Компонент log должен быть загружен в процессе предзагрузки, тогда он сможет оперативно передавать сообщения целям логов. Поэтому он указан в массиве bootstrap.

В приведенном выше коде в свойстве [[yii\log\Dispatcher::targets]] зарегистрированы две цели логов:

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

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

  • [[yii\log\DbTarget]]: сохраняет сообщения логов в таблицу базы данных.
  • [[yii\log\EmailTarget]]: шлет сообщения логов на заранее указанный email.
  • [[yii\log\FileTarget]]: сохраняет сообщения логов в файлы.
  • [[yii\log\SyslogTarget]]: сохраняет сообщения логов в системный лог используя функцию PHP syslog().

Дальше рассмотрим общие для этих четырех классов возможности.

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

Для каждой цели можно настроить свойства [[yii\log\Target::levels|levels]] и [[yii\log\Target::categories|categories]], которые указывают уровни важности и категории сообщений логов, которые цель должна обрабатывать.

Свойство [[yii\log\Target::levels|levels]] принимает массив, содержащий одно или несколько следующих значений:

  • error: соответствует сообщениям, сохраненным методом [[Yii::error()]].
  • warning: соответствует сообщениям, сохраненным методом [[Yii::warning()]].
  • info: соответствует сообщениям, сохраненным методом [[Yii::info()]].
  • trace: соответствует сообщениям, сохраненным методом [[Yii::debug()]].
  • profile: соответствует сообщениям, сохраненным методами [[Yii::beginProfile()]] и [[Yii::endProfile()]], подробнее о которых написано в подразделе Профилирование производительности.

Если свойство [[yii\log\Target::levels|levels]] не задано, цель логов будет обрабатывать сообщения с любым уровнем важности.

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

Если свойство [[yii\log\Target::categories|categories]] не задано, цель будет обрабатывать сообщения любой категории.

Кроме списка включаемый категорий, заданного свойством [[yii\log\Target::categories|categories]], при помощи свойства [[yii\log\Target::except|except]] возможно задать список исключаемых категорий. Если категория сообщения совпадает со значением или шаблоном из списка исключаемых категорий, такое сообщение не будет обработано.

В следующем примере показан вариант конфигурации цели логов, которая должна обрабатывать только сообщения об ошибках и предупреждениях в категориях 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',
    ],
]

Note: При обработке HTTP-исключения обработчиком ошибок, сообщение будет сохранено с категорией вида yii\web\HttpException:ErrorCode. Например, исключение [[yii\web\NotFoundHttpException]] вызовет сообщение об ошибке с категорией 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()]]:

Временная метка [IP-адрес][ID пользователя][ID сессии][Уровень важности][Категория] Текст сообщения

Этот формат может быть изменен при помощи свойства [[yii\log\Target::prefix]], которое получает анонимную функцию, возвращающую нужный префикс сообщения. Например, следующий код позволяет настроить вывод идентификатор текущего пользователя в качестве префикса для всех сообщений.

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

Уровень отслеживания выполнения кода

В процессе разработки, часто бывает необходимость видеть источники сообщений. Для этого нужно использовать свойство [[yii\log\Dispatcher::traceLevel|traceLevel]] компонента log. Например,

return [
    'bootstrap' => ['log'],
    'components' => [
        'log' => [
            'traceLevel' => YII_DEBUG ? 3 : 0,
            'targets' => [...],
        ],
    ],
];

При такой настройке свойство [[yii\log\Dispatcher::traceLevel|traceLevel]] будет равно 3 при YII_DEBUG равном true и 0 при YII_DEBUG равном false. Это означает, что при включенном YII_DEBUG, каждое сообщение лога будет содержать до трех уровней стека вызовов, а при выключенном YII_DEBUG информация о стеке вызовов не будет включаться в лог.

Info: Получение информации стека вызовов является не простым процессом. Поэтому такую возможность следует использовать только при разработке или отладке приложения.

Передача на обработку и выгрузка сообщений

Как упоминалось выше, сообщения логов обрабатываются в массиве [[yii\log\Logger|объектом логгера]]. Для ограничения объема памяти, занятого этим массивом, при накоплении определенного числа сообщений, логгер передает их на обработку целям логов. Максимальное количество сообщений определяется свойством [[yii\log\Dispatcher::flushInterval|flushInterval]] компонента log:

return [
    'bootstrap' => ['log'],
    'components' => [
        'log' => [
            'flushInterval' => 100,   // по умолчанию 1000
            'targets' => [...],
        ],
    ],
];

Info: При завершении приложения, так же происходит передача сообщений на обработку.

После передачи сообщений [[yii\log\Logger|объектом логгера]] в цели логов, сообщения не выгружаются немедленно. Вместо этого, выгрузка сообщений происходит когда цель логов накопит определенное количество фильтрованных сообщений. Максимальное количество сообщений определяется свойством [[yii\log\Target::exportInterval|exportInterval]] цели логов. Например,

[
    'class' => 'yii\log\FileTarget',
    'exportInterval' => 100,  // по умолчанию 1000
]

Из-за того, что значения максимального количества сообщений для передачи и выгрузки по умолчанию достаточно велико, при вызове метода Yii::debug() или любого другого метода логирования, сообщение не появится сразу в файле или таблице базы данных. Такое поведение может стать проблемой, например, в консольных приложениях с большим временем исполнения. Для того, чтобы все сообщения логов сразу же попадали в лог, необходимо установить значения свойств [[yii\log\Dispatcher::flushInterval|flushInterval]] и [[yii\log\Target::exportInterval|exportInterval]] равными 1, например так:

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

Note: Частая передача и выгрузка сообщений может сильно снизить производительность приложения.

Переключение целей логов

Свойство [[yii\log\Target::enabled|enabled]] отвечает за включение или отключение цели логов. Возможно управлением этим свойством как в конфигурации приложения, так и при помощи следующего PHP-кода:

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

В данном примере используется цель логов file, которая может быть настроена в конфигурации приложения следующим образом:

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

Заберите ссылку на статью к себе, чтобы потом легко её найти!
Раз уж досюда дочитали, то может может есть желание рассказать об этом месте своим друзьям, знакомым и просто мимо проходящим?
Не надо себя сдерживать! ;)

Старт! Горячий старт на просторы интернета
Старт! Горячий старт на просторы интернета
Старт! Меню