hamidrezaniazi / pecs
PHP ECS (弹性通用模式): 利用弹性通用模式简化日志记录。
Requires
- php: ^8.1
- illuminate/collections: >=8
- monolog/monolog: ^3.0
- nesbot/carbon: ^2.0
Requires (Dev)
- fakerphp/faker: ^1.0
- friendsofphp/php-cs-fixer: ^3.16
- phpstan/phpstan: ^1.0
- phpunit/phpunit: ^9.0
This package is auto-updated.
Last update: 2024-09-05 17:08:12 UTC
README
PHP ECS (弹性通用模式)
PECS 是一个 PHP 包,它简化了在 PHP 应用程序中使用 ECS (弹性通用模式)。ECS 是一种规范,有助于结构化和标准化日志事件。
PECS 提供了一种将 ECS 集成到 PHP 应用程序中的实用方法。通过使用类型提示的类,您可以使用 ECS 字段增强数据层。PECS 简化了将这些数据层转换为标准 ECS 模式的转换。
安装
您可以通过 composer 安装此包
composer require hamidrezaniazi/pecs
集成
Monolog
PECS 可以与流行的 PHP 日志库 Monolog 一起使用,将格式化程序应用于处理程序。
use Monolog\Logger; use Monolog\Handler\StreamHandler; use Hamidrezaniazi\Pecs\Monolog\EcsFormatter; use Hamidrezaniazi\Pecs\Fields\Event; $log = new Logger('logger name'); $handler = new StreamHandler('ecs.logs'); $log->pushHandler($handler->setFormatter(new EcsFormatter())); $log->info('message', [ new Event(action: 'test event'), ]);
EcsFormatter
确保由 Monolog 生成的默认记录正确映射到相应的 ECS 字段。此外,它负责渲染上下文数组中的剩余字段,以符合 ECS 模式。以下是上述示例的输出
[ '@timestamp' => '2023-05-27T00:13:16Z', 'message' => 'message', 'log' => [ 'level' => 'INFO', 'logger' => 'logger name', ], 'event' => [ 'action' => 'test event', ], ]
Symfony
在 Symfony 应用程序中,您可以将 EcsFormatter
应用于日志通道。首先,您需要在 config/services.yaml
中将其定义为一个服务
services: ecs: class: Hamidrezaniazi\Pecs\Monolog\EcsFormatter
然后,在 config/packages/monolog.yaml
中定义一个自定义通道
monolog: channels: - ecs handlers: ecs: formatter: ecs type: stream path: '%kernel.logs_dir%/ecs.log' channels: [ "ecs" ]
现在,您可以通过自动注入日志通道在您的 Symfony 应用程序中使用 ecs
通道
public function __construct(LoggerInterface $ecsLogger) { $ecsLogger->info('sample message', [ new Event(kind: EventKind::METRIC), ]); }
有关更多信息,请参阅 Symfony 的文档
Laravel
在 Laravel 应用程序中,您可以将 EcsFormatter
应用于日志驱动程序。首先,您需要创建一个类,该类实现了以下所示的 __invoke
方法
use Illuminate\Log\Logger; use Monolog\Handler\FormattableHandlerInterface; use Hamidrezaniazi\Pecs\Monolog\EcsFormatter; class LaravelEcsFormatter { public function __invoke(Logger $logger): void { foreach ($logger->getHandlers() as $handler) { /** @var FormattableHandlerInterface $handler */ $handler->setFormatter(app(EcsFormatter::class)); } } }
然后,要将此格式化程序应用于日志驱动程序,您需要在 config/logging.php
中将 tap
键添加到所需的日志配置中
'ecs' => [ 'driver' => 'single', 'tap' => [LaravelEcsFormatter::class], 'path' => storage_path('logs/ecs.log'), 'level' => 'debug', ],
有关此方法的更多信息,请参阅 Laravel 的文档
现在,您可以在 Laravel 应用程序的日志配置中使用 ecs
驱动程序来将 ECS 格式化程序应用于日志。
Log::channel('ecs')->info('sample message', [ new Event(kind: EventKind::METRIC), ]);
由于 Laravel 使用 Monolog 作为其底层日志系统,因此有关自动配置 @timestamp
、message
、level
和 logger
字段的相同行为也适用。
使用方法
需要注意的是,数据层中的空值(如
null
、[]
等)将被自动删除。您不需要像字符串N/A
一样显式处理它们。但是,这些值(如0
、0.0
、'0'
、'0.0'
、false
、'false'
)已被列入白名单,并将出现在日志中。
辅助工具
当您想使用多个字段进行日志记录时,语法可能会变得有些冗长。为了使其更简洁,您可以实现辅助类
use Hamidrezaniazi\Pecs\Fields\Error; use Hamidrezaniazi\Pecs\Fields\Log; class ThrowableHelper { public static function from(Throwable $throwable): array { return [ new Error( code: $throwable->getCode(), message: $throwable->getMessage(), stackTrace: $throwable->getTraceAsString(), type: get_class($throwable), ), new Log( originFileLine: $throwable->getLine(), originFileName: $throwable->getFile(), ) ]; } }
然后使用方法将缩短为
try { // ... } catch (Throwable $throwable) { Log::error('helpers example', ThrowableHelper::from($throwable)); }
多个字段
完全有可能有多个相同类型的字段。在发生冲突时,最近的属性将具有优先权。
use Hamidrezaniazi\Pecs\EcsFieldsCollection; use Hamidrezaniazi\Pecs\Fields\Base; use Hamidrezaniazi\Pecs\Properties\ValueList; (new EcsFieldsCollection([ new Base(message: 'Hello World'), new Base(message: 'test', tags: (new ValueList())->push('staging')), ]))->render()->toArray();
[ 'message' => 'test', 'tags' => [ 'staging', ], ]
您可以在此目录中找到用于定义ECS字段的可用类。
自定义字段
您还可以通过扩展AbstractEcsField
类来创建自己的自定义字段。
use Hamidrezaniazi\Pecs\Fields\AbstractEcsField; class FooField extends AbstractEcsField { public function __construct( private string $input ) { parent::__construct(); } protected function key(): ?string { return 'Foo'; } protected function custom(): Collection { return collect([ 'Input' => $this->input ]); } }
请查阅ECS自定义字段文档了解命名约定和使用案例。请注意,自定义字段键和属性名必须使用驼峰式命名法,以避免与ECS字段冲突。
包装器
您可能需要将自定义字段与现有的ECS字段类组合。这可以通过在您的类中重写wrapper
来实现。
use Hamidrezaniazi\Pecs\Fields\AbstractEcsField; use Hamidrezaniazi\Pecs\Fields\Event; use Hamidrezaniazi\Pecs\Properties\EventKind; class BarField extends AbstractEcsField { protected function key(): ?string { return 'Bar'; } protected function custom(): Collection { return collect([ 'Bleep' => 'bloop' ]); } public function wrapper(): EcsFieldsCollection { return parent::wrapper()->push(new Event(kind: EventKind::METRIC)); }
包装器中的所有字段将与自定义字段在同一级别渲染。在给定示例中,渲染的数组将是
[ 'Bar' => [ 'Bleep' => 'bloop', ], 'event' => [ 'kind' => 'metric', ], ]
空值
您还可以通过重写白名单数组来自定义空值的行为
class FooFields extends AbstractEcsField { protected array $validEmpty = [0, 0.0];
目前,只有0
和0.0
被列入白名单,将在日志中显示。其余的空值,如null
、[]
、false
、'0'
等,将被删除。
自定义格式化程序
默认格式化程序是集成部分中提到的EcsFormatter
类。但是,您可以通过重写prepare
方法来加载更多默认字段。
<?php use Hamidrezaniazi\Pecs\Fields\Ecs; use Hamidrezaniazi\Pecs\Monolog\EcsFormatter; class CustomEcsFormatter extends EcsFormatter { protected function prepare(array $record): EcsFieldsCollection { return parent::prepare($record)->push(new Ecs(version: '1.0.0')); } }
通过注册上述格式化程序,渲染的数组将包含ecs.version
以及默认字段。
收集
以下是使用EcsFieldsCollection
渲染ECS字段数组的示例
use Hamidrezaniazi\Pecs\EcsFieldsCollection; use Hamidrezaniazi\Pecs\Fields\Base; use Hamidrezaniazi\Pecs\Fields\Log; (new EcsFieldsCollection([ new Base(message: 'Hello World'), new Log(level: 'info'), ]))->render()->toArray();
上述代码将输出
[ 'message' => 'Hello World', 'log' => [ 'level' => 'info', ], ]
EcsFieldsCollection
是可定制的,可用于各种日志驱动程序,而不仅限于Monolog。Monolog的实际使用案例在集成部分中提到。
测试
composer test
变更日志
有关最近更改的更多信息,请参阅变更日志。
贡献
有关详细信息,请参阅贡献。
鸣谢
许可证
MIT许可(MIT)。有关更多信息,请参阅许可文件。