记录 Drupal 配置 monolog 遇到的坑

在开发 Drupal CMS 网站时,我们发现 Drupal 默认的日志记录在 watchdog 数据表中,为了把日志记录在文件、控制台输出,可以使用 monolog 模块。这个模块提供的文档较少,在此记录一下遇到的问题。

TOC

  1. 启用模块报错 The service “monolog.handler.rotating_file” has a dependency on a non-existent parameter “monolog.level.debug”.
  2. 修改日志格式为 JSON
  3. 输出到控制台(在 Drupal 运行在 Docker 容器中时非常有用)
  4. arguments 的配置
  5. processors 的配置
  6. 分离 INFO 和 ERROR

启用模块报错

阅读模块的 README 之后,我按照 Quick start 一模一样地配置了相应的文件

You should create a site specific services.yml (monolog.services.yml for example) in the same
folder of your settings.php and then add this line to settings.php itself:

settings.php
1
$settings['container_yamls'][] = 'sites/default/monolog.services.yml';

The simplest configuration that allows Monolog to log to a rotating file might be:

monolog.services.yml
1
2
3
4
5
6
7
parameters:
monolog.channel_handlers:
default: ['rotating_file']
services:
monolog.handler.rotating_file:
class: Monolog\Handler\RotatingFileHandler
arguments: ['private://logs/debug.log', 10, '%monolog.level.debug%']

解压模块到 drupal/modules/contrib/monolog,用 drush en monolog 命令启用,却遇到了报错。

1
The service "monolog.handler.rotating_file" has a dependency on a non-existent parameter "monolog.level.debug".

原因在于,”monolog.level.debug” 是在 monolog 的 monolog.services.yml 里面定义的,未开启 monolog 时,这个常量不存在,而这个常量不存在,setting.php 就会报错,setting.php 报错,就无法开启 monolog …… 这是一个死循环。

解决办法1:用 try 包裹 setting.php 中引入 monolog.services.yml 的行。

settings.php
1
2
3
try {
$settings['container_yamls'][] = 'sites/default/monolog.services.yml';
} catch (\Exception $e) {}

解决办法2:在自己的 monolog.services.yml 中同样声明 monolog.level.debug。

monolog.services.yml
1
2
3
4
5
6
7
8
parameters:
monolog.channel_handlers:
default: ['rotating_file']
monolog.level.debug: '100'
services:
monolog.handler.rotating_file:
class: Monolog\Handler\RotatingFileHandler
arguments: ['private://logs/debug.log', 10, '%monolog.level.debug%']

修改日志格式

只需给 handler 配置 formatter

monolog.services.yml
1
2
3
4
5
parameters:
monolog.channel_handlers:
default:
handlers: ['rotating_file']
formatter: 'json'

输出到控制台

只需用 StreamHandler 输出到 php://stdout

monolog.services.yml
1
2
3
4
services:
monolog.handler.std_info:
class: Monolog\Handler\StreamHandler
arguments: ['php://stdout', '%monolog.level.info%']

arguments 的配置

看了前两个你也会发现,这里面的 arguments 是啥?只见其值不见其名,它是什么意思?

我们可以理解 handler 控制日志输出的方式,不仅支持写到文件、控制台,甚至可以配置发送邮件,arguments 则是 handler 的参数。

关于有哪些 handler,可以到 02-handlers-formatters-processors 查看;

关于每一种 handler 需要怎么配置 arguments,可以到这个页面搜索 handler 的名字,看 __construct 前的注释。

processors 的配置

processor 控制日志包含的内容,有哪些 processor 也需要在模块的代码里看。https://git.drupalcode.org/project/monolog/-/tree/8.x-1.x/src%2FLogger%2FProcessor

强烈吐槽:为什么我需要看代码来研究一个 “世界上最好的日志插件” 怎么用……

分离 INFO 和 ERROR

很多时候我们需要把不同级别的 LOG 打到不同的地方去,一开始我试着这样配

monolog.services.yml
1
2
3
4
5
6
7
services:
monolog.handler.rotating_file_info:
class: Monolog\Handler\RotatingFileHandler
arguments: ['/var/log/drupal/info.log', 7, '%monolog.level.info%']
monolog.handler.rotating_file_error:
class: Monolog\Handler\RotatingFileHandler
arguments: ['/var/log/drupal/error.log', 7, '%monolog.level.error%']

发现一个问题,info.log 不仅包含了 INFO,还包含了所有级别高于 INFO(如 WARN, ERROR)的日志,我不想把 ERROR 打在 info.log 里,怎么办?

我先提交了一个 issue Is it possible to set maxium logging level?

后来经过 Will 巨佬指点,可以利用冒泡参数实现这样的需求。

StreamHandler.php
1
2
3
4
5
6
7
8
9
/**
* @param resource|string $stream If a missing path can't be created, an UnexpectedValueException will be thrown on first write
* @param string|int $level The minimum logging level at which this handler will be triggered
* @param bool $bubble Whether the messages that are handled can bubble up the stack or not
* @param int|null $filePermission Optional file permissions (default (0644) are only for owner read/write)
* @param bool $useLocking Try to lock log file before doing any writes
*
* @throws \InvalidArgumentException If stream is not a resource or string
*/

$bubble 默认为 TRUE。我们首先配置两个 handler,一个打 INFO,一个打 ERROR,然后将 ERROR handler 的 $bubble 设置为 FALSE,阻止其向上一个 INFO handler 冒泡,这样,当 INFO log 来时,不会走 ERROR handler,而经由 INFO handler 打出,当 ERROR log 来时,会先由 ERROR handler 打出,然后不会冒泡到 INFO handler,达到目的。像这样——

monolog.services.yml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
parameters:
monolog.channel_handlers:
default:
# 注意:monolog 是【从后往前】处理 handlers 的
handlers: ['rotating_file_info', 'rotating_file_error']
formatter: 'json'
# Enabled processors.
monolog.processors: ['message_placeholder', 'current_user', 'request_uri', 'ip', 'referer', 'filter_backtrace', 'introspection']
# Logger levels.
monolog.level.error: '400'
monolog.level.info: '200'
services:
monolog.handler.rotating_file_info:
class: Monolog\Handler\RotatingFileHandler
arguments: ['/var/log/drupal/info.log', 7, '%monolog.level.info%']
monolog.handler.rotating_file_error:
class: Monolog\Handler\RotatingFileHandler
# 设置 $bubble = FALSE 阻止 ERROR 打进 INFO log
arguments: ['/var/log/drupal/error.log', 7, '%monolog.level.error%', FALSE]

评论