mensbeam/logger

简单且可配置的记录器

v1.3 2024-02-04 16:28 UTC

This package is auto-updated.

Last update: 2024-09-04 17:36:10 UTC


README

Logger 是一个简单且可配置的 PHP 记录器。它是 PSR-3 记录器接口 的有意见实现。它使用称为 处理器 的类来处理要记录的消息。目前只有一个处理器:StreamHandler,它允许将日志记录到文件或到流,例如 php://stdoutphp://stderr。处理器可以轻松编写并插入到记录器中。

有意见吗?

这个库试图实现我们所说的“有意见”的 PSR-3 实现。这是因为虽然它成功实现了 Psr\Log\LoggerInterface,但 Logger 在许多方面偏离了规范的真正精神。

  1. 在 PSR-3 的 1.1 节 中,PSR-3 指出,在调用具有日志级别常量之一的 log 方法时(稍后显示为 Psr\Log\LogLevel),必须具有与调用特定级别方法相同的结果。在 Psr\Log\LogLevel 中的日志级别常量是字符串,但 Psr\Log\LoggerInterfacelog 方法的 $level 参数是无类型的。规范中的话暗示 $level 参数应该是一个字符串,但实际上代码实现者没有指定类型。同样,该部分还引用了 RFC 5424 来提及日志级别方法,但为什么使用字符串,而可以使用用于标识严重性的标准化整数呢?由于接口允许任何类型的 $levelLogger 将优先使用实际的 RFC 标准的整数,但将接受并内部将 PSR-3 的字符串转换为整数,以便保持与 PSR-3 兼容。

  2. 在规范的 1.2 节 中,它描述了一个可选功能,占位符,并要求实现者编写代码来解析和替换占位符,使用的是 PHP 整体中任何地方都没有的语法和方法。 Logger 不会支持此功能,因为日志库的位置是记录消息,而不是插值模板字符串。应该使用单独的库或内置函数,如 sprintf。但是,Logger 提供了一种转换消息的方法,如果需要,可以用来挂钩首选的插值方法。

  3. 规范中的 1.3 节 还指定,如果 Exception 对象传递给 $context 参数,它必须在 exception 键中。这是有道理的,但奇怪的是,没有提到 Error 对象的处理方法。它们自 PHP 7 以来就存在,可以像异常一样抛出。《Logger》将接受 exception 键中的任何 Throwable,但目前对此不采取任何行动。理论上,将来可以编写处理器来利用它进行结构化数据。

  4. 在第一项的第1.3节中,它明确指出,实现者必须在$context数组中存在错误数据时不要触发警告,并尽可能宽容地处理。然后,在下一条中提到,如果上下文数据中存在异常,它必须位于exception键中,并且实现者必须验证exception键。这是矛盾的。在没有错误或异常时验证exception键不会引发错误或异常,用户应该被告知他们犯了错误;否则设计是不好的。我们解决这个问题的方案是从$context中删除错误的可抛出对象,并在遇到它们时触发警告。然而,提供了Logger->$warnOnInvalidContextThrowables以方便必要时抑制警告。

要求

注意

此库使用mensbeam/filesystem,该库为ext-ctypeext-mbstring提供填充。如果您已安装这些扩展,则不会使用填充。但是,它们仍然被安装。如果您不想无谓地安装填充,可以将此添加到项目的composer.json中。

{
    "require": {
        "ext-ctype": "*",
        "ext-mbstring": "*"
    },
    "provide": {
        "symfony/polyfill-ctype": "*",
        "symfony/polyfill-mbstring": "*"
    }
}

安装

composer require mensbeam/logger

用法

此库无需配置即可使用,但它可能不完全符合您的默认预期

use MensBeam\Logger;

$logger = new Logger();

这将创建一个将所有调试、信息、通知和警告条目输出到STDOUT的记录器,而任何错误、关键、警报和紧急条目则输出到STDERR。这似乎会是一个奇怪的默认设置,因为它会在错误时向shell输出重复输出。然而,如果与Catcher之类的错误处理程序一起使用,它突然就有意义了。

use MensBeam\{
    Catcher,
    Logger
};
use MensBeam\Catcher\PlainTextHandler;

$catcher = new Catcher(new PlainTextHandler([
    'logger' => new Logger('log'),
    'silent' => true
]));

现在,Logger将负责打印。但是,Logger可以做更多。

文档

MensBeam\Logger

namespace MensBeam;
use MensBeam\Logger\{
    Handler,
    Level
};


class Logger implements Psr\Log\LoggerInterface {
    public bool $warnOnInvalidContextThrowables = true;

    public function __construct(?string $channel = null, Handler ...$handlers);

    public function getChannel(): ?string;
    public function getHandlers(): array;
    public function popHandler(): Handler;
    public function pushHandler(Handler ...$handlers): void;
    public function setChannel(?string $value): void;
    public function setHandlers(Handler ...$handlers): void;
    public function shiftHandler(): Handler;
    public function unshiftHandler(Handler ...$handlers): void;

    public function emergency(string|\Stringable $message, array $context = []): void;
    public function alert(string|\Stringable $message, array $context = []): void;
    public function critical(string|\Stringable $message, array $context = []): void;
    public function error(string|\Stringable $message, array $context = []): void;
    public function warning(string|\Stringable $message, array $context = []): void;
    public function notice(string|\Stringable $message, array $context = []): void;
    public function info(string|\Stringable $message, array $context = []): void;
    public function debug(string|\Stringable $message, array $context = []): void;
    public function log(int|string|Level $level, string|\Stringable $message, array $context = []): void;
}

属性

warnOnInvalidContextThrowables:当设置为true时,Logger将在日志方法中的$context数组中存在无效的Throwable时触发警告。

MensBeam\Logger::getChannel

返回Logger实例的通道名称。

MensBeam\Logger::getHandlers

返回用于Logger实例的处理器定义的数组。

MensBeam\Logger::popHandler

从堆栈中删除最后一个处理器并返回它

MensBeam\Logger::pushHandler

将指定的处理器推送到堆栈上

MensBeam\Logger::setChannel

将通道设置为指定的字符串

MensBeam\Logger::setHandlers

用参数指定的处理器替换处理器堆栈

MensBeam\Logger::shiftHandler

从处理器堆栈中移除第一个处理器并返回它

MensBeam\Logger::unshiftHandler

将指定的处理器推送到堆栈的开始位置

MensBeam\Logger::emergency

向日志中添加紧急条目

MensBeam\Logger::alert

向日志中添加警报条目

MensBeam\Logger::critical

向日志中添加关键条目

MensBeam\Logger::error

向日志中添加错误条目

MensBeam\Logger::warning

向日志中添加警告条目

MensBeam\Logger::notice

向日志中添加通知条目

MensBeam\Logger::info

向日志中添加信息条目

MensBeam\Logger::debug

向日志中添加调试条目

MensBeam\Logger::log

在指定级别向日志中添加条目

MensBeam\Logger\Level

这是一个枚举,包含每个日志级别的RFC 5424整数值;同时也提供了转换到和从PSR-3字符串值的方法。

namespace MensBeam;

enum Level: int {
    case Emergency = 0;
    case Alert = 1;
    case Critical = 2;
    case Error = 3;
    case Warning = 4;
    case Notice = 5;
    case Info = 6;
    case Debug = 7;

    public static function fromPSR3(string $level): self;
    public function toPSR3(): string;
}

MensBeam\Logger\Level::fromPSR3

接收一个提供的PSR-3字符串级别并返回一个Level枚举。

MensBeam\Logger\Level::toPSR3

返回枚举的PSR-3字符串级别表示。

MensBeam\Logger\Handler

处理器继承自这个抽象类。因为这个抽象类是为了构建处理器,所以保护方法和属性也会在这里进行文档说明。

namespace MensBeam\Logger;

abstract class Handler {
    protected array $levels;

    protected bool $_bubbles = true;
    protected callable $_messageTransform = null;
    protected string $_timeFormat = 'M d H:i:s';

    public function __construct(array $levels = [ 0, 1, 2, 3, 4, 5, 6, 7 ], array $options = []);

    public function getLevels();
    public function getOption(string $name): mixed;
    public function setLevels(int ...$levels): void;
    public function setOption(string $name, mixed $value): void;
    public function __invoke(int $level, ?string $channel, string $message, array $context = []): void;

    abstract protected function invokeCallback(string $time, int $level, string $channel, string $message, array $context = []): void;
}

属性(保护)

levels:这是处理器配置要支持级别的存储位置

选项

以下划线开头的属性都是选项。它们可以通过构造函数设置,也可以通过setHandler按名称设置,去掉开头的下划线(_)。所有处理器都继承这些选项。继承类中的选项也应该以下划线开头(_)。

bubbles:当设置为true时,堆栈循环将转到下一个处理器;如果为false则不会。默认为true
messageTransform:用于在输出前转换消息的可调用对象。默认为null
timeFormat:用于输出时间所用的PHP标准日期格式。默认为"M d H:i:s"

消息转换

messageTransform选项允许对日志消息进行操作。它接受任何具有以下结构的可调用对象

function (string $message, array $context): string;

此功能的一个常见用途是对字符串进行插值,这不是库所处理的。通过提供消息转换,可以使用任何首选的插值方法

$handler = new StreamHandler(options: [
    'messageTransform' => function (string $message, array $context): string {
        return vsprintf($message, $context);
    }
]);

当然,这是一个简单的例子。在将$context数组转换为数值键(或直接使用数值键)之前,可以在vsprintf中使用它,但如所见,这是完全可能的。

MensBeam\Logger\Handler::getLevels

返回处理器配置要支持的级别

MensBeam\Logger\Handler::getOption

返回提供的选项名称的值

MensBeam\Logger\Handler::setLevels

设置处理器将支持的级别

MensBeam\Logger\Handler::setOption

设置提供的选项及其值

MensBeam\Logger\Handler::__invoke

输出/分发日志条目

MensBeam\Logger\Handler::invokeCallback (protected)

这是一个回调方法,由继承类扩展以输出/分发日志条目

MensBeam\Logger\StreamHandler

namespace MensBeam\Logger;

class StreamHandler extends Handler {
    public function __construct(resource|string $stream = 'php://stdout', array $levels = [ 0, 1, 2, 3, 4, 5, 6, 7 ], array $options = []);

    public function getStream(): ?resource;
    public function getURI(): ?string;
    public function setStream(resource|string $value): void;
}

选项

entryTransform:一个可调用对象,其中可以操作日志条目。默认为null

条目转换
function (string $time, int $level, string $levelName, string $channel, string $message, array $context): string;
参数
time
日志条目被分发时的戳记;可以使用timeFormat选项进行更改
level
日志条目的RFC 5424级别整数
levelName
日志条目的RFC 5424级别名称
channel
在创建记录器时定义的通道
message
消息字符串;可以使用messageTransform选项进行操作
context
在分发条目时使用的上下文数组

以下是如何使用entryTransform选项将条目输出到php://stdout作为JSONLines/NDJSON等的示例(哦,天哪)

use MensBeam\Logger,
    MensBeam\Logger\StreamHandler;

$handler = new StreamHandler(options: [
    'entryTransform' => function (string $time, int $level, string $levelName, string $channel, string $message, array $context): string {
        $entry = [
            'time' => $time,
            'level' => $level,
            'levelName' => $levelName,
            'channel' => $channel,
            'message' => $message
        ];

        return json_encode($entry, \JSON_THROW_ON_ERROR | \JSON_UNESCAPED_SLASHES | \JSON_UNESCAPED_UNICODE);
    }
]);

$logger = new Logger('ook', $handler);
$logger->info("ook\neek");
// {"time":"Feb 03 22:45:17","level":6,"levelName":"Info","channel":"ook","message":"ook\neek"}

MensBeam\Logger\StreamHandler::getStream

返回处理器将输出的资源

MensBeam\Logger\StreamHandler::getURI

返回处理器将输出的URI

MensBeam\Logger\StreamHandler::setStream

设置处理器将输出的位置