0 关注者

日志记录

Yii 提供了一个功能强大的日志记录框架,它高度可定制且可扩展。使用此框架,您可以轻松地记录各种类型的消息,过滤它们,并将它们收集到不同的目标,例如文件、数据库、电子邮件。

使用 Yii 日志记录框架涉及以下步骤

  • 在您的代码中不同位置记录 日志消息
  • 在应用程序配置中配置 日志目标,以过滤和导出日志消息;
  • 检查不同目标导出的已过滤日志消息(例如,Yii 调试器)。

在本节中,我们将主要介绍前两个步骤。

日志消息

记录日志消息就像调用以下日志记录方法之一一样简单

  • Yii::debug():记录一条消息以跟踪代码片段的运行方式。这主要用于开发目的。
  • Yii::info():记录一条消息,传达一些有用的信息。
  • Yii::warning():记录一条警告消息,表明发生了意外情况。
  • Yii::error():记录一个致命错误,应尽快调查。

这些日志记录方法以不同的严重性级别类别记录日志消息。它们共享相同的函数签名function ($message, $category = 'application'),其中$message代表要记录的日志消息,而$category是日志消息的类别。以下示例中的代码在默认类别application下记录跟踪消息

Yii::debug('start calculating average revenue');

信息:日志消息可以是字符串,也可以是复杂数据,例如数组或对象。 日志目标负责正确处理日志消息。默认情况下,如果日志消息不是字符串,它将通过调用 yii\helpers\VarDumper::export() 作为字符串导出。

为了更好地组织和过滤日志消息,建议您为每条日志消息指定一个合适的类别。您可以为类别选择分层命名方案,这将使日志目标更容易根据类别过滤消息。一个简单但有效的命名方案是使用 PHP 魔术常量__METHOD__作为类别名称。这也是 Yii 核心框架代码中使用的方案。例如,

Yii::debug('start calculating average revenue', __METHOD__);

__METHOD__常量评估为出现该常量的方法的名称(以完全限定的类名作为前缀)。例如,如果上面的代码行是在此方法内调用的,则它等于字符串'app\controllers\RevenueController::calculate'

信息:上面描述的日志方法实际上是log()方法的快捷方式,该方法是日志对象的单例,可以通过表达式Yii::getLogger()访问。当记录了足够的消息或应用程序结束时,日志对象将调用消息调度程序将记录的日志消息发送到注册的日志目标

日志目标

日志目标是yii\log\Target类或其子类的实例。它根据日志消息的严重程度级别和类别过滤日志消息,然后将它们导出到某些介质。例如,数据库目标将过滤后的日志消息导出到数据库表,而电子邮件目标将日志消息导出到指定的电子邮件地址。

您可以通过应用程序配置中的log 应用程序组件来配置多个日志目标,如下所示

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' => 'yii\log\DbTarget',
                    'levels' => ['error', 'warning'],
                ],
                [
                    'class' => 'yii\log\EmailTarget',
                    'levels' => ['error'],
                    'categories' => ['yii\db\*'],
                    'message' => [
                       'from' => ['[email protected]'],
                       'to' => ['[email protected]', '[email protected]'],
                       'subject' => 'Database errors at example.com',
                    ],
                ],
            ],
        ],
    ],
];

注意:log组件必须在启动时加载,以便它能够及时地将日志消息调度到目标。这就是为什么它在上面的bootstrap数组中列出的原因。

在上面的代码中,两个日志目标在yii\log\Dispatcher::$targets属性中注册

  • 第一个目标选择错误和警告消息,并将它们保存到数据库表中;
  • 第二个目标选择类别名称以yii\db\开头的错误消息,并将它们通过电子邮件发送到[email protected][email protected]

Yii 附带了以下内置日志目标。请参考这些类的 API 文档,了解如何配置和使用它们。

在下文中,我们将描述所有日志目标的共同功能。

消息过滤

对于每个日志目标,您可以配置其levelscategories属性,以指定目标应处理哪些严重程度级别和类别的消息。

levels属性接受一个数组,该数组包含以下一个或多个值

如果您没有指定levels属性,则意味着目标将处理任何严重程度级别的消息。

categories属性接受一个数组,该数组包含消息类别名称或模式。目标只会处理类别名称或模式与该数组中某一个相符的消息。类别模式是在其末尾带有星号*的类别名称前缀。如果类别名称以模式的相同前缀开头,则该类别名称与类别模式匹配。例如,yii\db\Command::executeyii\db\Command::query用作在yii\db\Command类中记录的日志消息的类别名称。它们都与模式yii\db\*匹配。

如果您没有指定categories属性,则意味着目标将处理任何类别的消息。

除了使用categories属性指定允许的类别外,您还可以通过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',
    ],
]

信息:当 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()按以下格式格式化

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

您可以通过配置yii\log\Target::$prefix属性来定制此格式,该属性接受一个返回定制消息前缀的 PHP 可调用对象。例如,以下代码配置了一个日志目标,为每个日志消息添加当前用户 ID 的前缀(出于隐私原因,IP 地址和会话 ID 已被删除)。

[
    '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()方法。

如果您的一些请求字段包含您不想记录的敏感信息(例如密码、访问令牌),您还可以配置maskVars属性。默认情况下,以下请求参数将被屏蔽为***$_SERVER[HTTP_AUTHORIZATION]$_SERVER[PHP_AUTH_USER]$_SERVER[PHP_AUTH_PW],但您可以设置您自己的

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

消息跟踪级别

在开发过程中,通常希望看到每条日志消息来自哪里。这可以通过配置log组件的traceLevel属性来实现,如下所示

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

上面的应用程序配置将traceLevel设置为,如果YII_DEBUG为开,则为 3;如果YII_DEBUG为关,则为 0。这意味着,如果YII_DEBUG为开,则每条日志消息将附加最多 3 级调用堆栈,在该调用堆栈中记录日志消息;如果YII_DEBUG为关,则不会包含任何调用堆栈信息。

信息:获取调用堆栈信息并不简单。因此,您应该只在开发或调试应用程序时使用此功能。

消息刷新和导出

如前所述,日志消息由日志对象保存在一个数组中。为了限制此数组的内存消耗,日志对象将在每次数组累积一定数量的日志消息时将记录的消息刷新到日志目标。您可以通过配置log组件的flushInterval属性来定制此数字

return [
    'bootstrap' => ['log'],
    'components' => [
        'log' => [
            'flushInterval' => 100,   // default is 1000
            'targets' => [...],
        ],
    ],
];

信息:当应用程序结束时,也会发生消息刷新,这确保日志目标可以接收完整的日志消息。

日志对象将日志消息刷新到日志目标时,它们不会立即被导出。相反,消息导出只会在日志目标累积一定数量的过滤后的消息时发生。您可以通过配置单个日志目标exportInterval属性来定制此数字,如下所示,

[
    'class' => 'yii\log\FileTarget',
    'exportInterval' => 100,  // default is 1000
]

由于刷新和导出级别设置,默认情况下,当您调用Yii::debug()或任何其他日志方法时,您将不会立即在日志目标中看到日志消息。这对于一些长时间运行的控制台应用程序来说可能是个问题。为了使每个日志消息立即出现在日志目标中,您应该将flushIntervalexportInterval都设置为 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',
                ],
            ],
        ],
    ],
];

从 2.0.13 版本开始,您可以使用可调用对象配置enabled,以定义日志目标是否应该启用的动态条件。有关示例,请参阅yii\log\Target::setEnabled()的文档。

创建新的目标

创建新的日志目标类非常简单。您主要需要实现 yii\log\Target::export() 方法,将 yii\log\Target::$messages 数组的内容发送到指定的介质。您可以调用 yii\log\Target::formatMessage() 方法来格式化每个消息。有关更多详细信息,您可以参考 Yii 发布版中包含的任何日志目标类。

提示: 您可以尝试使用任何与 PSR-3 兼容的日志记录器,例如 Monolog,通过 PSR 日志目标扩展,而不是创建自己的日志记录器。

性能分析

性能分析是一种特殊的日志记录类型,用于测量某些代码块的执行时间,并找出性能瓶颈。例如,yii\db\Command 类使用性能分析来找出每个数据库查询的执行时间。

要使用性能分析,首先要确定需要分析的代码块。然后将每个代码块包含在以下代码中:

\Yii::beginProfile('myBenchmark');

...code block being profiled...

\Yii::endProfile('myBenchmark');

其中 myBenchmark 代表一个唯一标识代码块的标记。稍后,当您检查分析结果时,您将使用此标记来定位对应代码块所花费的时间。

确保 beginProfileendProfile 对正确嵌套非常重要。例如:

\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') 的顺序,性能分析将无法正常工作。

对于每个被分析的代码块,都会记录一个严重级别为 profile 的日志消息。您可以配置一个 日志目标 来收集这些消息并导出它们。 Yii 调试器 有一个内置的性能分析面板,显示分析结果。

发现错别字或您认为此页面需要改进?
在 github 上编辑它 !