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 具有以下日志级别
- DEBUG - 详细的调试信息
- INFO - 值得关注的事件
- NOTICE - 正常但重要的事件
- WARNING - 并非错误的例外情况
- ERROR - 不需要立即操作的运行时错误
- CRITICAL - 关键条件
- ALERT - 必须立即采取行动的事件
- EMERGENCY - 紧急事件
不太严重的日志不会被具有更严重日志级别的 handlers 处理。通过将日志级别设置为 ERROR,我们可以获得 ERROR 级别及以上的消息。
每个 handler 都指定一个日志级别;默认值为 `DEBUG`。要生成具有特定日志级别的消息,我们有包括 `info`、`warn`、`error` 和 `critical` 在内的方法。由于 Monolog 早于 PSR-3,因此它包含重复的方法(例如 `addInfo` 或 `addWarning`)。
Monolog 简单示例
在第一个示例中,我们使用 `Streamhandler` 将消息记录到文件。
<?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 控制台日志记录
我们可以将日志消息写入终端。
<?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 数组允许在用户端级别向消息记录添加信息。
<?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 首先执行。
<?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` 添加自定义处理器。
<?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 格式写入记录。
<?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` 将日志记录格式化为单行字符串。
<?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` 软件包。
在我们的示例中,我们使用 Mailtrap 服务。
<?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 在 PHP 中进行日志记录。
作者
列出所有 PHP 教程。