ZetCode

PHP Monolog

最后修改于 2025 年 2 月 16 日

PHP Monolog 教程演示了如何使用 Monolog 在 PHP 中进行日志记录。

日志记录

日志记录是将信息写入日志文件的过程。日志文件包含有关操作系统、软件或通信中发生的各种事件的信息。

日志记录的目的

日志记录的目的如下:

日志记录不仅限于识别软件开发中的错误。它还用于检测安全事件、监控策略违规、在出现问题时提供信息、查找应用程序瓶颈或生成使用数据。

要记录的事件

应记录的事件包括输入验证失败、身份验证和授权失败、应用程序错误、配置更改以及应用程序启动和关闭。

不要记录的事件

不应记录的事件包括应用程序源代码、会话标识值、访问令牌、敏感个人数据、密码、数据库连接字符串、加密密钥、银行账户和持卡人数据。

日志记录最佳实践

以下是日志记录的一些最佳实践

PHP Monolog

Monolog是一个流行的 PHP 日志记录库。它允许将日志发送到文件、套接字、收件箱、数据库和各种 Web 服务。它实现了 PSR-3 接口。

$ composer req monolog/monolog

我们使用 composer 安装 Monolog。

Monolog 结构

一个 Monolog 记录器实例有一个 *channel* (名称) 和一堆 *handlers*。handlers 负责将消息保存到文件、数据库或将其发送到邮件。

已记录的消息通过 *handler 堆栈* 传递。最后一个 handler 首先执行。消息的进一步传播由 `bubble` 变量控制,该变量默认设置为 `true`。

一个 *message record* 是一条将要写入日志的信息。消息记录包含以下部分

类型 描述
消息 字符串 日志消息。
级别 int 日志消息的严重性。
level_name 字符串 日志级别的字符串表示。
上下文 数组 随消息构造传递的任意数据。
channel 字符串 该消息被记录到的 channel。
datetime Monolog\DateTimeImmutable 记录消息的日期和时间。
extra 数组 一个占位符数组,处理器可以在其中放置其他数据。

一个 *处理器* 是任何可用于处理日志消息的 PHP 可调用对象。它可以向记录添加一些额外的数据。

`context` 是一个数组数据,其中包含不适合主字符串的附加信息。 context 是日志记录方法(例如 `info` 或 `warn`)的参数。

*格式化器* 用于格式化消息记录。

Monolog 日志级别

日志级别用于按紧急程度对日志消息进行分类。 Monolog 具有以下日志级别

不太严重的日志不会被具有更严重日志级别的 handlers 处理。通过将日志级别设置为 ERROR,我们可以获得 ERROR 级别及以上的消息。

每个 handler 都指定一个日志级别;默认值为 `DEBUG`。要生成具有特定日志级别的消息,我们有包括 `info`、`warn`、`error` 和 `critical` 在内的方法。由于 Monolog 早于 PSR-3,因此它包含重复的方法(例如 `addInfo` 或 `addWarning`)。

Monolog 简单示例

在第一个示例中,我们使用 `Streamhandler` 将消息记录到文件。

simple.php
<?php

require __DIR__ . '/vendor/autoload.php';

use Monolog\Handler\StreamHandler;
use Monolog\Logger;

$logger = new Logger('main');
$logger->pushHandler(new StreamHandler(__DIR__ . '/logs/app.log', Logger::DEBUG));

$logger->info('First message');

该示例将 info 消息写入 `logs/app.log` 文件。

$logger = new Logger('main');

创建一个名为 `main` 的新记录器。

$logger->pushHandler(new StreamHandler(__DIR__ . '/logs/app.log', Logger::DEBUG));

我们使用 `pushHandler` 将一个 `StreamHandler` 添加到记录器。 handler 将消息写入指定的文件,并带有 DEBUG 严重性。

$logger->info('First message');

我们使用 `info` 方法记录一条 info 消息。

$ cat logs\app.log
[2019-05-15 15:49:48] main.INFO: First message [] []

这是写入的消息。日志消息以当前日期和时间开头。它后面是日志 channel 名称和级别。然后是消息记录。两个方括号是我们可以指定上下文和额外数据的地方。我们可以使用 Monolog 格式化程序自定义此输出。

Monolog 控制台日志记录

我们可以将日志消息写入终端。

console_log.php
<?php

require __DIR__ . '/vendor/autoload.php';

use Monolog\Logger;
use Monolog\Handler\StreamHandler;

$logger = new Logger('stderr');
$logger->pushHandler(new StreamHandler('php://stderr', Logger::WARNING));

$logger->warn('A warning message');

我们通过将 `php://stderr` 指定给 `StreamHandler` 来写入控制台。

Monolog context 数组

Monolog context 数组允许在用户端级别向消息记录添加信息。

context_array.php
<?php

require __DIR__ . '/vendor/autoload.php';

use Monolog\Handler\StreamHandler;
use Monolog\Logger;

$logger = new Logger('main');
$logger->pushHandler(new StreamHandler('php://stderr'));

$logger->info('Information message', ['user' => get_current_user()]);

`info` 方法的第二个参数是 context 数组。我们将当前用户添加到消息中。

$ php context_array.php
[2019-05-15 15:37:56] main.INFO: Information message {"user":"Jano"} []

Monolog 记录器 handler 堆栈

我们可以将多个 handlers 添加到堆栈。最后一个添加的 handler 首先执行。

handler_stack.php
<?php

require __DIR__ . '/vendor/autoload.php';

use Monolog\Handler\StreamHandler;
use Monolog\Logger;

$main = new Logger('main');
$main->pushHandler(new StreamHandler(__DIR__ . '/logs/app.log'));

$main->pushHandler(new StreamHandler('php://stdout', $level = Logger::DEBUG,
        $bubble = true));

$main->info('Information message');

在示例中,我们有两个记录器 handlers:一个文件和一个控制台 handler。如果我们将 `$bubble` 更改为 `false`,则消息将不会写入 `logs/app.log` 文件。

Monolog 自定义处理器

可以使用 `pushProcessor` 添加自定义处理器。

custom_processor.php
<?php

require __DIR__ . '/vendor/autoload.php';

use Monolog\Handler\StreamHandler;
use Monolog\Logger;

$logger = new Logger('main');
$logger->pushHandler(new StreamHandler('php://stderr'));

$logger->pushProcessor(function ($record) {
    $record['extra']['user'] = get_current_user();

    return $record;
});

$logger->info('Information message');

在示例中,我们在处理器中向消息记录添加了一些额外的信息。根据文档,context 和 extra 数据之间的区别在于,context 在用户端提供,而 extra 仅是内部的,可以由处理器填充。

$ php custom_processor.php
[2019-05-15 17:24:48] main.INFO: Information message [] {"user":"Jano"}

附加信息将添加到输出的末尾。

Monolog JsonFormatter

`JsonFormatter` 以 JSON 格式写入记录。

json_formatter.php
<?php

require __DIR__ . '/vendor/autoload.php';

use Monolog\Formatter\JsonFormatter;
use Monolog\Handler\StreamHandler;
use Monolog\Logger;

$logger = new Logger('main');

$formatter = new JsonFormatter();

$stream = new StreamHandler(__DIR__ . '/logs/app-json.log');
$stream->setFormatter($formatter);

$logger->pushHandler($stream);
$logger->info('Information message', ['user' => get_current_user()]);

该示例使用 `JsonFormatter` 以 JSON 格式将 info 消息写入文件。

$ cat logs\app-json.log
{"message":"Information message","context":{"user":"Jano"},"level":200,"level_name":"INFO",
"channel":"main","datetime":{"date":"2019-05-15 17:37:40.433222","timezone_type":3,
"timezone":"Europe/Berlin"},"extra":[]}

这是已记录的消息。

Monolog LineFormatter

`LineFormatter` 将日志记录格式化为单行字符串。

line_format.php
<?php

require __DIR__ . '/vendor/autoload.php';

use Monolog\Logger;
use Monolog\Handler\StreamHandler;
use Monolog\Formatter\LineFormatter;
use Monolog\Processor\ProcessIdProcessor;

$output = "[%datetime%] %channel%.%level_name%: %message% %context.user%\n";
$formatter = new LineFormatter($output);

$streamHandler = new StreamHandler('php://stdout');
$streamHandler->setFormatter($formatter);

$logger = new Logger('main');
$logger->pushHandler($streamHandler);

$logger->info('Information message from', ['user' => get_current_user()]);

在示例中,我们使用 `LineFormatter` 自定义消息记录。

$output = "[%datetime%] %channel%.%level_name%: %message% %context.user%\n";
$formatter = new LineFormatter($output);

我们创建一个自定义记录消息。它包含日期时间、 channel 名称、级别名称、消息和 context 数据。

$streamHandler->setFormatter($formatter);

我们使用 `setFormatter` 将格式化程序添加到 handler。

$ php line_format.php
[2019-05-15 17:43:36] main.INFO: Information message from Jano

Monolog 邮件日志消息

可以使用 `SwiftMailerHandler` 发送电子邮件

$ composer req swiftmailer/swiftmailer

对于此示例,我们需要安装 `swiftmailer/swiftmailer` 软件包。

注意: Gmail 不适合测试应用程序。我们应该使用 Mailtrap 或 Mailgun 等在线服务,或者使用网络托管公司提供的 SMTP 服务器。

在我们的示例中,我们使用 Mailtrap 服务。

mail_log.php
<?php

require __DIR__ . '/vendor/autoload.php';

use Monolog\Handler\StreamHandler;
use Monolog\Logger;
use Monolog\Handler\SwiftMailerHandler;

$transporter = new Swift_SmtpTransport('smtp.mailtrap.io', 2525, 'tls');
$transporter->setUsername('3178491df14b6d');
$transporter->setPassword('7c1d6fa59a5v08');

$mailer = new Swift_Mailer($transporter);

$message = new Swift_Message('Critical message');
$message->setFrom(['approot@example.com' => 'App root']);
$message->setTo(['support@example.com' => 'Support']);

$logger = new Logger('main');
$logger->pushHandler(new SwiftMailerHandler($mailer, $message, Logger::CRITICAL));
$logger->critical('Could not connect to the database');

在示例中,我们使用 `SwiftMailerHandler` 将消息记录发送到邮件收件箱。

$transporter = new Swift_SmtpTransport('smtp.mailtrap.io', 2525, 'tls');
$transporter->setUsername('3178491df14b6d');
$transporter->setPassword('7c1d6fa59a5v08');

使用 Mailtrap 连接详细信息,我们构建了传输器。

$mailer = new Swift_Mailer($transporter);

创建一个 `Swinf_Mailer`。

$message = new Swift_Message('Critical message');
$message->setFrom(['approot@example.com' => 'App root']);
$message->setTo(['support@example.com' => 'Support']);

创建一个 `Swift_Message`。它包含 from 和 to 字段。

$logger = new Logger('main');
$logger->pushHandler(new SwiftMailerHandler($mailer, $message, Logger::CRITICAL));
$logger->critical('Could not connect to the database');

我们将 `SwiftMailerHandler` 添加到记录器,并使用 `critical` 创建一条 critical 消息。

来源

Monolog Github 存储库

在本文中,我们使用 Monolog 在 PHP 中进行日志记录。

作者

我叫 Jan Bodnar,是一位充满激情的程序员,拥有丰富的编程经验。自 2007 年以来,我一直在撰写编程文章。迄今为止,我撰写了 1,400 多篇文章和 8 本电子书。我拥有十多年的编程教学经验。

列出所有 PHP 教程。