ryunosuke/simple-logger

简单的 psr-3 日志记录器

v1.1.5 2024-08-07 10:35 UTC

This package is auto-updated.

Last update: 2024-09-07 10:51:20 UTC


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.txtredis://hoge/log.txt 这样的格式,它就可以很好地判断。
  • 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

  • 发布