定制 lezhnev74/psr-logging-masking-middleware 二次开发

按需修改功能、优化性能、对接业务系统,提供一站式技术支持

邮箱:yvsm@zunyunkeji.com | QQ:316430983 | 微信:yvsm316

lezhnev74/psr-logging-masking-middleware

Composer 安装命令:

composer require lezhnev74/psr-logging-masking-middleware

包简介

A logging middleware for any PSR-7 HTTP client that logs every request/response through a PSR-3 logger, with headers, query args and body keys masked. Built on PSR-7, PSR-17 and PSR-3.

README 文档

README

tests codecov PHPStan

A logging middleware for any PSR-7 HTTP client that logs every request and response for later debugging, with secrets redacted - headers, query-string args and body keys masked per message. Built on PSR-7 (messages), PSR-17 (factories, auto-discovered) and PSR-3 (logging), so it works with any PSR-7 impl and PSR-3 logger - none a hard dependency.

Requirements

  • PHP 8.1 - 8.5
  • Any PSR-7 / PSR-17 implementation in your app (e.g. guzzlehttp/psr7 or nyholm/psr7), discovered via php-http/discovery.

Installation

composer require lezhnev74/psr-logging-masking-middleware

Usage

The core is a MessageLogger: give it a PSR-3 logger and a Masker - the config-bound masking policy. It masks each message and writes the exchange as a single debug record. Bodies are masked by content-type - JSON by key (recursively), x-www-form-urlencoded by field name, any other type replaced with a <content-type: N bytes> note so an opaque body never leaks.

use Lezhnev74\PsrLoggingMaskingMiddleware\MaskingConfig;
use Lezhnev74\PsrLoggingMaskingMiddleware\MessageLogger;
use Lezhnev74\PsrLoggingMaskingMiddleware\MessageMasker;

$logger = new MessageLogger(
    $psr3Logger,                        // inject your app's logger
    new MessageMasker(
        MaskingConfig::create(          // applied to request AND response
            headerNames: ['Authorization', 'Set-Cookie'],
            queryNames: ['api_key'],
            bodyKeys: ['password'],
        ),
    ),
);

One config masks both messages - list every secret name wherever it may appear. MessageMasker's further constructor arguments pin a PSR-17 stream factory or customize the replacement string; pass a NullMasker to the logger to log exchanges unmasked. See tests/MessageLoggerTest.php.

Fluent builder

MessageLoggerBuilder::for($psr3Logger) wires the same MessageLogger in one chain - no hand-built MaskingConfig or MessageMasker:

use Lezhnev74\PsrLoggingMaskingMiddleware\MaskTarget;
use Lezhnev74\PsrLoggingMaskingMiddleware\MessageLoggerBuilder;
use Psr\Log\LogLevel;

$logger = MessageLoggerBuilder::for($psr3Logger)
    ->maskHeaders('Authorization', 'Set-Cookie')
    ->maskQuery('api_key')
    ->maskBody('password', 'card.number')
    ->placeholder('[redacted]')          // or ->replaceWith(fn (MaskTarget $t) => '***')
    ->logLevel(LogLevel::INFO)           // defaults to debug
    // ->streamFactory($psr17Factory)    // optional; discovered when omitted
    ->build();

The mask*() calls accumulate (call one twice to add more names), and placeholder()/replaceWith() share one slot so the last one set wins. See tests/MessageLoggerBuilderTest.php.

Guzzle

HandlerMiddleware::for($logger) returns a generic fn(callable): callable middleware for any handler stack. Push it onto Guzzle's:

use GuzzleHttp\Client;
use GuzzleHttp\HandlerStack;
use Lezhnev74\PsrLoggingMaskingMiddleware\HandlerMiddleware;

$stack = HandlerStack::create();
$stack->push(HandlerMiddleware::for($logger));

$client = new Client(['handler' => $stack]);

See tests/GuzzleClientTest.php and tests/HandlerMiddlewareTest.php.

More

Each is a green test - the executable, always-current spec:

  • Per-message masking - subclass MessageLogger and override the single resolveMasker() seam to vary the masking by path, method, headers, etc.; it receives the message being masked and the exchange's request, so it can key on either (return a NullMasker = unmasked): tests/MessageLoggerTest.php.
  • Custom replacement - pass a replacer: closure to MessageMasker; it receives a MaskTarget (message, kind, path, value) and returns the string to substitute (default '***'): tests/MessageMaskerTest.php.
  • Laravel Http facade - pass the same handler stack via Http::withOptions(['handler' => $stack]) (or Http::globalOptions(...) in a provider): tests/LaravelHttpFacadeTest.php.
  • Any PSR-18 client (no handler stack) - wrap it with the LoggingClient decorator: tests/LoggingClientTest.php.
  • Standalone masking (no logging) - the Masker interface is the masking contract (mask(MessageInterface): MessageInterface, returns a masked clone) that MessageLogger itself consumes. MessageMasker is its canonical implementation, bound to one MaskingConfig; NullMasker is the no-op implementation. Build the masker with preserveUnknownBodies: true to keep a body whose media type has no built-in masker byte-for-byte instead of collapsing it to a size note - for consumers (e.g. traffic recorders) that must keep bodies faithful: tests/MaskerContractTest.php.

Local development

QA tooling runs in a PHP 8.5 Docker container (with Xdebug) via the dev wrapper - no host PHP needed. ./dev <script> is composer <script> in Docker; anything unrecognized is passed to composer.

cp .env.dist .env  # set DOCKER_USER_ID / DOCKER_GROUP_ID (see `id -u` / `id -g`)
./dev install      # composer install
./dev test         # run the test suite
./dev check        # code style + static analysis + tests
./dev cs-fix       # auto-fix code style

Contributing

  • TDD. Every new branch needs a covering test; the masking/serialization suite must stay green against both Guzzle and Nyholm PSR-7 impls.
  • Conventional Commits (spec) drive semantic versioning (fix → patch, feat → minor, !/BREAKING CHANGE → major).
  • Run ./dev check before opening a PR.

License

MIT - see LICENSE.

统计信息

  • 总下载量: 0
  • 月度下载量: 0
  • 日度下载量: 0
  • 收藏数: 0
  • 点击次数: 1
  • 依赖项目数: 1
  • 推荐数: 0

GitHub 信息

  • Stars: 0
  • Watchers: 0
  • Forks: 0
  • 开发语言: PHP

其他信息

  • 授权协议: MIT
  • 更新时间: 2026-07-08

承接程序开发

PHP开发

VUE

Vue开发

前端开发

小程序开发

公众号开发

系统定制

数据库设计

云部署

网站建设

安全加固