日志记录
Symfony 带有两个极简的 PSR-3 日志记录器:Logger 用于 HTTP 上下文,ConsoleLogger 用于 CLI 上下文。根据 十二要素应用方法,它们将从 WARNING
级别开始的消息发送到 stderr。
可以通过设置 SHELL_VERBOSITY
环境变量来更改最小日志级别
SHELL_VERBOSITY 值 |
最小日志级别 |
---|---|
-1 |
ERROR |
1 |
NOTICE |
2 |
INFO |
3 |
DEBUG |
最小日志级别、默认输出和日志格式也可以通过将适当的参数传递给 Logger 和 ConsoleLogger 的构造函数来更改。
Logger 类通过 logger
服务提供。要传递您的配置,您可以覆盖 “logger” 服务定义。
有关 ConsoleLogger
的更多信息,请参阅使用 Logger。
记录消息
要记录消息,请在您的控制器或服务中注入默认的 logger
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
use Psr\Log\LoggerInterface;
// ...
public function index(LoggerInterface $logger): Response
{
$logger->info('I just got the logger');
$logger->error('An error occurred');
// log messages can also contain placeholders, which are variable names
// wrapped in braces whose values are passed as the second argument
$logger->debug('User {userId} has logged in', [
'userId' => $this->getUserId(),
]);
$logger->critical('I left the oven on!', [
// include extra "context" info in your logs
'cause' => 'in_hurry',
]);
// ...
}
建议向日志消息添加占位符,因为
- 检查日志消息更容易,因为许多日志工具将除了其中一些变量值之外相同的日志消息分组在一起;
- 翻译这些日志消息要容易得多;
- 这对安全性更好,因为转义可以由实现以上下文感知的方式完成。
logger
服务对于不同的日志级别/优先级有不同的方法。有关 logger 上所有方法的列表,请参阅 LoggerInterface。
Monolog
Symfony 与 Monolog(最流行的 PHP 日志记录库)无缝集成,以创建日志消息并将其存储在各种不同的位置,并触发各种操作。
例如,使用 Monolog,您可以配置 logger 根据消息的级别执行不同的操作(例如,当发生错误时发送电子邮件)。
运行此命令以在安装 Monolog 之前安装基于 Monolog 的 logger
1
$ composer require symfony/monolog-bundle
以下部分假设已安装 Monolog。
日志存储位置
默认情况下,当您处于 dev
环境时,日志条目会写入 var/log/dev.log
文件。
在 prod
环境中,日志将写入 STDERR PHP 流,这在部署到没有磁盘写入权限的服务器的现代容器化应用程序中效果最佳。
如果您希望将生产日志存储在文件中,请将日志处理器 (handler) 的 path
设置为您要使用的文件路径(例如 var/log/prod.log
)。
处理器:将日志写入不同位置
logger 有一个处理器堆栈,每个处理器都可以用于将日志条目写入不同的位置(例如,文件、数据库、Slack 等)。
提示
您还可以配置日志“通道”,它们类似于类别。每个通道都可以有其自己的处理器,这意味着您可以将不同的日志消息存储在不同的位置。请参阅如何将消息记录到不同的文件。
Symfony 在默认的 monolog.yaml
配置文件中预先配置了一些基本处理器。查看这些文件以获取一些实际示例。
此示例使用两个处理器:stream
(用于写入文件)和 syslog
,用于使用 syslog 函数写入日志
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
# config/packages/prod/monolog.yaml
monolog:
handlers:
# this "file_log" key could be anything
file_log:
type: stream
# log to var/log/(environment).log
path: "%kernel.logs_dir%/%kernel.environment%.log"
# log *all* messages (debug is lowest level)
level: debug
syslog_handler:
type: syslog
# log error-level messages and higher
level: error
这定义了一个处理器堆栈,每个处理器都按照定义的顺序调用。
注意
如果您想通过另一个配置文件覆盖 monolog
配置,您将需要重新定义整个 handlers
堆栈。两个文件中的配置无法合并,因为顺序很重要,并且合并不允许您控制顺序。
修改日志条目的处理器
一些处理器不是将日志文件写入某个位置,而是用于在将日志条目发送到其他处理器之前对其进行过滤或修改。一个强大的内置处理器称为 fingers_crossed
,默认情况下在 prod
环境中使用。它存储请求期间的所有日志消息,但仅当其中一条消息达到 action_level
时才将它们传递给第二个处理器。以下示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
# config/packages/prod/monolog.yaml
monolog:
handlers:
filter_for_errors:
type: fingers_crossed
# if *one* log is error or higher, pass *all* to file_log
action_level: error
handler: file_log
# now passed *all* logs, but only if one log is error or higher
file_log:
type: stream
path: "%kernel.logs_dir%/%kernel.environment%.log"
# still passed *all* logs, and still only logs error or higher
syslog_handler:
type: syslog
level: error
现在,如果甚至一个日志条目具有 LogLevel::ERROR
级别或更高,那么该请求的所有日志条目都将通过 file_log
处理器保存到文件中。这意味着您的日志文件将包含有关问题请求的所有详细信息 - 使调试变得更加容易!
提示
名为 “file_log” 的处理器将不会包含在堆栈本身中,因为它用作 fingers_crossed
处理器的嵌套处理器。
所有内置处理器
Monolog 附带许多内置处理器,用于通过电子邮件发送日志、将日志发送到 Loggly 或在 Slack 中通知您。这些都在 MonologBundle 本身中进行了文档记录。有关完整列表,请参阅Monolog 配置。
如何轮换您的日志文件
随着时间的推移,日志文件可能会变得巨大,无论是在开发过程中还是在生产环境中。一个最佳实践解决方案是使用诸如 logrotate Linux 命令之类的工具来轮换日志文件,以防止它们变得太大。
另一种选择是让 Monolog 为您轮换文件,方法是使用 rotating_file
处理器。此处理器每天创建一个新的日志文件,并且还可以自动删除旧文件。要使用它,请将您的处理器的 type
选项设置为 rotating_file
1 2 3 4 5 6 7 8 9 10
# config/packages/prod/monolog.yaml
monolog:
handlers:
main:
type: rotating_file
path: '%kernel.logs_dir%/%kernel.environment%.log'
level: debug
# max number of log files to keep
# defaults to zero, which means infinite files
max_files: 10
在服务中使用 Logger
如果您的应用程序使用服务自动配置,则任何类实现 Psr\Log\LoggerAwareInterface
的服务都将收到对其 setLogger()
方法的调用,并将默认的 logger 服务作为服务传递。
如果您想在自己的服务中使用预配置的 logger,该 logger 使用特定通道(默认情况下为 app
),您可以自动装配 monolog 通道,或者使用带有 channel
属性的 monolog.logger
标签,如 依赖注入参考中所述。
处理长时间运行进程中的日志
在长时间运行的进程中,日志可能会累积到 Monolog 中,并导致一些缓冲区溢出、内存增加,甚至不合逻辑的日志。可以使用 Monolog\Logger
实例上的 reset()
方法清除 Monolog 内存中的数据。这通常应该在长时间运行的进程正在处理的每个作业或任务之间调用。