# PHP Monolog 教程

> 原文： [https://zetcode.com/php/monolog/](https://zetcode.com/php/monolog/)

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

## 记录

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

## 记录目的

完成记录是出于以下目的：

*   信息收集
*   故障排除
*   产生统计数据
*   审计
*   性能分析

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

## 要记录的事件

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

## 不记录的事件

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

## 记录最佳做法

以下是一些日志记录最佳做法：

*   日志记录应该有意义。
*   日志应包含上下文。
*   日志应保持平衡； 它不应包含过多或过多的信息。
*   记录消息应该是人类可以理解的，并且可以被机器解析。
*   日志记录应在不同级别进行结构化和完成。
*   日志应适应开发和生产。
*   记录更复杂的应用应产生几个日志文件。

## PHP Monolog

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

```php
$ composer req monolog/monolog

```

我们用 Composer 安装 Monolog。

## Monolog 结构

Monolog 记录器实例具有通道（名称）和处理器的栈。 处理器负责将消息保存到文件，数据库或将其发送到邮件。

记录的消息在处理器栈中传播。 最后一个处理器首先执行。 消息的进一步传播由`bubble`变量控制，该变量默认设置为`true`。

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

| 键 | 类型 | 描述 |
| --- | --- | --- |
| `info` | `string` | 日志消息。 |
| `level` | `integer` | 日志消息的严重性。 |
| `level_name` | `string` | 日志级别的字符串表示形式。 |
| `context` | `array` | 随消息的构造传递任意数据。 |
| `channel` | `string` | 此消息被登录到的频道。 |
| `datetime` | `Monolog`/`DateTimeImmutable` | 消息记录的日期和时间。 |
| `extra` | `array` | 处理器可在其中放置其他数据的占位符数组。 |

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

`context`是数组数据，其中包含的附加信息不太适合主字符串。 上下文是日志记录方法的参数（例如`info()`或`warn()`）。

格式化程序用于格式化消息记录。

## Monolog 日志级别

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

*   `DEBUG` - 详细的调试信息
*   `INFO` - 有趣的事件
*   `NOTICE` - 正常但重要的事件
*   `WARNING` - 并非错误的特殊情况
*   `ERROR` - 不需要立即采取措施的运行时错误
*   `CRITICAL` - 关键条件
*   `ALERT` - 必须立即采取措施的事件
*   `EMERGENCY` - 紧急事件

日志级别较低的处理器将不处理不太严重的日志。 通过将日志级别设置为`ERROR`，我们将获得`ERROR`级别及更高级别的消息。

每个处理器都指定了一个日志级别； 默认值为`DEBUG`。 为了产生具有特定日志级别的消息，我们有包括`info()`，`warn()`，`error()`和`critical()`的方法。 由于 Monolog 早于 PSR-3，因此它包含重复的方法（例如`addInfo()`或`addWarning()`）。

## Monolog 简单示例

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

`simple.php`

```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');

```

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

```php
$logger = new Logger('main');

```

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

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

```

我们使用`pushHandler()`将`StreamHandler`添加到记录器。 处理器以`DEBUG`严重性将消息写入指定的文件。

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

```

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

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

```

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

## Monolog 控制台日志记录

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

`console_log.php`

```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');

```

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

## Monolog 上下文数组

Monolog 上下文数组允许在用户级级别将信息添加到消息记录中。

`context_array.php`

```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()`方法的第二个参数是上下文数组。 我们将当前用户添加到消息中。

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

```

这是输出。

## Monolog 记录器处理器栈

我们可以将多个处理器添加到栈中。 最后添加的处理器将首先执行。

`handler_stack.php`

```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');

```

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

## Monolog 定制处理器

可以向`pushProcessor()`添加自定义处理器。

`custom_processor.php`

```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');

```

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

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

```

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

## Monolog `JsonFormatter`

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

`json_formatter.php`

```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 格式写入文件。

```php
$ 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
<?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`自定义消息记录。

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

```

我们创建一个自定义记录消息。 它包含日期时间，频道名称，级别名称，消息和上下文数据。

```php
$streamHandler->setFormatter($formatter);

```

我们使用`setFormatter()`将格式化程序添加到处理器中。

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

```

这是一个示例输出。

## Monolog 邮件日志消息

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

```php
$ composer req swiftmailer/swiftmailer

```

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

> **注意**：Gmail 不是理想的测试应用。 我们应该使用诸如 Mailtrap 或 Mailgun 之类的在线服务，或者使用由网络托管公司提供的 SMTP 服务器。

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

`mail_log.php`

```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`将消息记录发送到邮件收件箱。

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

```

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

```php
$mailer = new Swift_Mailer($transporter);

```

创建了`Swinf_Mailer`。

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

```

创建了`Swift_Message`。 它包含从到字段。

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

```

我们将`SwiftMailerHandler`添加到记录器，并使用`critical()`创建关键消息。

您可能也对以下相关教程感兴趣： [PHP Rakit 验证教程](/php/rakitvalidation/)， [PHP PDO 教程](/php/pdo/)， [PHP 教程](/lang/php/)或列出[所有 PHP 教程。](/all/#php)

在本教程中，我们使用 Monolog 在 PHP 中进行日志记录。