ryunosuke / simple-logger
简单的 psr-3 日志记录器
Requires
- php: >=8.0
- psr/log: *
Requires (Dev)
- aws/aws-sdk-php: 3.*
- monolog/monolog: *
- phpunit/phpunit: 9.*
- predis/predis: 2.*
- ryunosuke/phpunit-extension: 4.*
- ryunosuke/stream-wrapper: 1.*
- symfony/yaml: 5.*|6.*
README
描述
这是一个实现了 psr-3 的简单日志记录器包。虽然只实现了流式输出,但由于 PHP 有强大的 StreamWrapper,所以(如果有 Wrapper)实际上可以将内容写入任何对象。
输出格式由扩展名决定。
安装
{ "require": { "ryunosuke/simple-logger": "dev-master" } }
功能
(如果有 Wrapper)可以在任何地方输出。例如以下示例将日志输出到 S3。
$s3client = new \Aws\S3\S3Client([
'credentials' => [
'key' => 'foo',
'secret' => 'bar',
],
'region' => 'ap-northeast-1',
'version' => 'latest',
]);
\Aws\S3\StreamWrapper::register($s3client);
$logger = new \ryunosuke\SimpleLogger\StreamLogger('s3://bucket-name/log.txt');
$logger->debug('debug message');
$logger->alert('alert message');
但实际上(在我的观察范围内),用 file 方案进行日志记录的情况最多,我个人认为只要有文件日志加 fluentd 就足够了(让专家来做)。StreamLogger 只是“假设给定的路径是 file://”这一点而已。
除了 StreamLogger 之外,还有一个叫做 ChainLogger 的东西。这是一个所谓的复合日志记录器,它会将日志展开到所有维护的日志记录器。
也就是说,以下情况是可能的。通常,notice 级以上的日志会被写入到 path/to/log.jsonl
中,而 error 级以上的日志将通过 smtp 方案发送电子邮件。
$logger = new \ryunosuke\SimpleLogger\ChainLogger([
(new \ryunosuke\SimpleLogger\StreamLogger('file://path/to/log.jsonl'))->setPresetPlugins()->prependPlugin(new \ryunosuke\SimpleLogger\Plugins\LevelFilterPlugin('notice')),
(new \ryunosuke\SimpleLogger\StreamLogger('smtp://hoge@example.com/log.txt'))->setPresetPlugins()->prependPlugin(new \ryunosuke\SimpleLogger\Plugins\LevelFilterPlugin('error')),
]);
$logger->notice('notice message');
$logger->alert('alert message');
URL 的功能如下。
scheme://hostname/path/to/logfile.json
─── ──── ──── ──
│ │ │ └─ 出力形式です(monolog でいう Formatter に相当します)
│ │ └──────── ストリームラッパーによって様々です(例えば file ではディレクトリです)
│ └───────────── ストリームラッパーによって様々です。不必要なこともあります(例えば file では不要ですが、S3 ならバケット名になります)
└────────────────── 出力先です(monolog でいう Handler に相当します)
monolog 中的 Processor 使用 Plugin 架构。基于日志级别的过滤或添加时间字符串等所有内容都通过 Plugin 实现。作为日志记录器,它不会特别处理这些信息。
插件
正如上述,StreamLogger 只负责处理对流的抽象写入,对日志内容没有任何发言权。输出格式由扩展名控制,但输出内容可以通过设置 Plugin 来控制。
- AbstractPlugin
- 插件抽象类
- 需要至少实现 apply 方法
- apply 对日志进行某种修改。返回 null 则跳过该日志(LevelFilterPlugin 是一个很好的例子)
- ClosurePlugin
- 调用指定的闭包
- 为了创建自定义插件,通常需要使用匿名类
new class() extends AbstractPlugin{...}
,但为了避免这种做法,可以使用闭包来创建
- ContextAppendPlugin
- 向上下文中添加任意条目
- 通常情况下,日期、主机名或进程 ID 等也是日志中很有用的信息
- ContextConsumePlugin
- 用于
message {context.key}
这样的值嵌入的上下文 - 上下文通常有两种用途:“想要嵌入到消息中”和“作为日志的参考信息”。前者用于嵌入到消息中,因此上下文是不必要的
- 在这种情况下,可以将其隐藏起来。如果输出格式不是结构化文本,则指定没有意义
- 用于
- ContextFilterPlugin
- 转换或删除任意上下文
- 有时上下文中会传递非常大的对象,因此可以进行切割或删除
- ContextOrderPlugin
- 指定上下文的输出顺序
- 例如,“行号……杂项……文件名”这样的顺序会让日志看起来很乱。大多数日志更倾向于“日期……日志级别”在开头
- 可以指定这样的顺序。如果输出格式不是结构化文本,则指定没有意义
- LevelAutoPlugin
- 在收到 null 日志级别时自动设置
- 基于 context 的 level、exception、message 等自动设置
- LevelFilterPlugin
- 基于日志级别的过滤。对于日志记录器来说,这实际上是必需的
- 不仅可以指定“NOTICE 以上”,还可以指定“NOTICE 以上 ERROR 以下”等
- LevelFromPhpPlugin
- 将 PHP 的错误常量(E_ERROR 等)转换为标准日志级别
- 这样就可以在 log 方法中传递 E_ERROR 等常量
- 由于一些常量的值重复,如果在 log 方法中使用 LOG_ERR 等值,则会导致值混乱。为了避免这种情况,负数的 PHP 错误常量也可以进行转换
- LevelNormalizePlugin
- 将日志级别字符串化
- psr3 没有规定日志级别的类型,因此传递的级别可以是 int、大写或小写。将其统一为字符串
- LevelUnsetPlugin
- 隐藏 level
- 通常不使用。“不需要级别”的特定情况下会使用
- LocationAppendPlugin
- 将日志方法的调用位置(文件、行号等)添加到上下文中
- MessageCallbackPlugin
- 当日志消息传递了 callable 时,将调用回调
- 例如
$logger->debug(function () {...})
可以减少日志生成的成本
- MessageStringifyPlugin
- 当日志消息传递了非 stringable 时,将其字符串化
- 例如
$logger->debug([1, 2, 3])
这样非 stringable 也可以被记录
- MessageRewritePlugin
- 重写日志消息
- SuppressPlugin
- 在指定的时间内抑制同一日志的输出
- 通常在 DEBUG 级别使用
- ThrowableManglePlugin
- 妥善处理异常对象
- 根据日志消息本身是 Throwable 或 {exception} 上下文等,尽可能地整理和显示 Throwable
注意
输出高度依赖于 StreamWrapper 的实现。这既是优点也是缺点。例如,有些中间件与 fopen 的 a 标志不兼容,因此无法控制逐个 flush 或逐个 write 的日志。要实现这种细粒度控制,需要编写专门的 StreamWrapper。
许可证
MIT
常见问题解答
- Q. 为什么要重新开发轮子?
- A. 我最初很喜欢使用 monolog,但后来感觉有点冗长,实际上 logger 只使用 StreamHandler,所以我想要自己实现一个更清晰的版本。
- Q. 但是 monolog 也有 Redis、Slack 等有用的功能呢?
- A. PHP 已经存在一个强大的抽象层 StreamWrapper。与其编写专门的 handler,不如使用像
s3://hoge/log.txt
或redis://hoge/log.txt
这样的格式,它就可以很好地判断。
- A. PHP 已经存在一个强大的抽象层 StreamWrapper。与其编写专门的 handler,不如使用像
- Q. 那为什么还要自己实现呢?
- A. monolog 输出的日志格式我不太喜欢。我只是想输出简单的 json 日志,而需要一些设置,所以我想要一个几乎无配置的 logger。
发布
版本控制遵循浪漫版本控制(而不是语义版本控制)。
- 主要版本:在发生重大兼容性破坏时升级(架构、类结构更改等)
- 次要版本:在发生小规模兼容性破坏时升级(参数更改、类型提示添加等)
- 补丁版本:没有兼容性破坏(默认参数添加、新类添加、代码格式化等)
1.1.5
- [feature] 自动决定日志级别的插件
1.1.4
- [feature] 将 php 错误常量转换为标准级别的插件
- [feature] 隐藏级别的插件
- [refactor] 多处更改
1.1.3
- [feature] 在一定时间内抑制同一日志的插件
- [feature] 在嵌入上下文值后将上下文隐藏的插件
- [feature] 将插件命名以便更容易排序
1.1.2
- [fixbug] ThrowableManglePlugin 的错误修复
1.1.1
- [feature] 插件替换功能
- [feature] 处理异常对象的参数
1.1.0
- [change] php>=8.0
1.0.1
- [feature] 添加自动轮换功能
1.0.0
- 发布