View on GitHub

yii2-cookbook-chinese

Yii Application Development Cookbook(Third Edition)中文翻译

使用不同的日志路由

当你没有机会调试它的时候,打日志对于理解应用真正做了些什么非常关键。不管你是否相信,尽管你能100%确信你的应用将会按照你期望的执行,在生产环境中,它可以做很多你意识不到的事情。这没关系,因为没有人可以注意到任何事情。因此,如果我们期望不寻常的行为,我们需要立刻知道并有足够的信息来重现它。这就是日志派上用场的原因。

Yii允许一个开发者不止可以输出日志消息,也能根据消息的级别和种类进行不同的处理。例如,你可以将一条消息写入到数据库,发送一个电子邮件或者将它展示到浏览器中。

在本小节中,我们将会以更明智的方法处理日志消息:最重要的信息通过邮件发送,不太重要的信息会被保存到的文件A和B中,profiling将会被路由到Firebug中。此外,在开发模式下,所有的消息和profiling信息将会展示在屏幕上。

准备

按照官方指南http://www.yiiframework.com/doc-2.0/guide-start-installation.html的描述,使用Composer包管理器创建一个新的应用。

如何做…

执行如下步骤:

  1. 使用config/web.php配置日志:
'components' => [
    'log' => [
        'traceLevel' => 0,
        'targets' => [
            [
                'class' => 'yii\log\EmailTarget',
                'categories' => ['example'],
                'levels' => ['error'],
                'message' => [
                    'from' => ['log@example.com'],
                    'to' => ['developer1@example.com',
                        'developer2@example.com'],
                    'subject' => 'Log message',
                ],
            ],
            [
                'class' => 'yii\log\FileTarget',
                'levels' => ['error'],
                'logFile' => '@runtime/logs/error.log',
            ],
            [
                'class' => 'yii\log\FileTarget',
                'levels' => ['warning'],
                'logFile' => '@runtime/logs/warning.log',
            ],
            [
                'class' => 'yii\log\FileTarget',
                'levels' => ['info'],
                'logFile' => '@runtime/logs/info.log',
            ],
        ],
    ],
    'db' => require(__DIR__ . '/db.php'),
],
  1. 现在,我们会在protected/controllers/LogController.php中生成一些消息:
<?php
namespace app\controllers;
use yii\web\Controller;
use Yii;
class LogController extends Controller
{
    public function actionIndex()
    {
        Yii::trace('example trace message', 'example');
        Yii::info('info', 'example');
        Yii::error('error', 'example');
        Yii::trace('trace', 'example');
        Yii::warning('warning','example');
        Yii::beginProfile('preg_replace', 'example');
        for($i=0;$i<10000;$i++){
            preg_replace('~^[ a-z]+~', '', 'test it');
        }
        Yii::endProfile('preg_replace', 'example');
        return $this->render('index');
    }
}

以及视图views/log/index.php

<div class="log-index">
    <h1>Log</h1>
</div>
  1. 现在多次运行先前的动作。在屏幕上,你应该看到Log头和一个有日志消息数字的调试面板:

  1. 如果你点击17,你将会看到一个web日志,如下图所示:

  1. 一条日志包含我们打的所有信息,堆栈踪迹、时间戳、级别和分类。
  2. 现在打开Profiling页面。你应该能看到profiling消息,如下截图所示:

profiling信息展示了我们代码块的所有执行时长。

  1. 因为我们刚刚修改了日志文件的名称,而不是路径,你应该能在runtime/logs中找到日志文件error.logwarning.loginfo.log
  2. 打开文件你将会看到如下消息:
2016-03-06 07:28:35 [127.0.0.1][-][-][error][example] error
...
2016-03-06 07:28:35 [127.0.0.1][-][-][warning][example] warning
...
2016-03-06 07:28:35 [127.0.0.1][-][-][info][example] info

工作原理…

当使用Yii::erorrYii::warningYii::info或者Yii::trace打日志时,Yii将它传递给了日志路由。

依赖于如何配置,它会将消息发送给一个或多个目标,例如,通过电子邮件发送错误信息、将调试信息写入到文件A中、将警告信息写入到文件B中。

yii\log\Dispatcher类的对象通常被附加在一个名叫log的应用组件上。因此,为了配置它,我们应该在配置文件组件部分设置它的属性。这里唯一可配的属性是targets,它包含了一组日志路由和他们的配置。

我们已经定义了四个日志路由。这里回顾一下:

[
    'class' => 'yii\log\EmailTarget',
    'categories' => ['example'],
    'levels' => ['error'],
// 'mailer' => 'mailer',
    'message' => [
        'from' => ['log@example.com'],
        'to' => ['developer1@example.com', 'developer2@example.com'],
        'subject' => 'Log error',
    ],
],

EmailTarget默认通过Yii::$app->mailer组件发送一封电子邮件来发送日志消息。我们将类别限制为example,并将级别限制为error。电子邮件将会从log@example.com发送给两个开发者,且主题是Log error

[
    'class' => 'yii\log\FileTarget',
    'levels' => [warning],
    'logFile' => '@runtime/logs/warning.log',
],

FileTarget将错误消息发送到一个指定的文件。我们将消息级别限制为warning,并使用一个名叫warning.log的文件。同样我们将info级别的消息存放在Info.log文件中。

此外,我们可以使用yii\log\SyslogTarget将消息写到Unix /var/log/syslog系统文件中,或者使用yii\log\DbTarget将消息写入到数据库中。对于第二种情况,你必须应用他们的migration:

./yii migrate --migrationPath=@yii/log/migrations/

更多…

关于Yii打日志有很多有趣的东西,在接下来的部分中进行讨论。

Yii::trace和Yii::getLogger()->log

Yii::trace是对Yii::log的封装:

public static function trace($message, $category = 'application')
{
    if (YII_DEBUG) {
        static::getLogger()->log($message, Logger::LEVEL_TRACE, $category);
    }
}

因此,如果Yii在debug模式下,Yii::trace使用trace级别来打日志。

Yii::beginProfile和Yii::endProfile

这些方法被用于测量应用中部分代码的执行时间。在我们的LogController中,我们测量了preg_replace执行10000次所用的时间:

Yii::beginProfile('preg_replace', 'example');
for($i=0;$i<10000;$i++){
    preg_replace('~^[ a-z]+~', '', 'test it');
}
Yii::endProfile('preg_replace', 'example');

Yii::beginProfile标记用于profiling的代码块开头。我们必须为每一个代码块设置一个唯一的token,以及指定一个可选的分类:

public static function beginProfile($token, $category = 'application') {  }

Yii::endProfile可以匹配到先前调用有相同的分类名的beginProfile

public static function endProfile($token, $category = 'application') {  }

begin-end-调用也必须被正确的嵌套。

立即打日志消息

默认情况下,Yii会将所有的日志消息存放在内存中,知道应用终止。这是为了性能考虑,并且一般都能运行良好。

但是,如果一个控制台应用需要长时间运行,日志消息将不会被立刻写出。为了确保你的消息能在任何时候都被打印出来,你可以使用Yii::$app->getLogger()>flush(true)显式刷新,或者为你的控制台应用配置修改flushIntervalexportInterval

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

参考