garbetjie/http-request-logger

一个可以记录所有传入和传出请求及响应的请求记录器。

5.1.0 2021-08-18 19:03 UTC

README

一个HTTP请求记录库,可轻松实现记录所有HTTP请求和响应。

支持 LaravelGuzzle 和 PHP 的 SoapServerSoapClient 类。

目录

简介

能够查看应用程序生成的所有请求和响应通常非常有用,尤其是在开发期间。此库使得使用 Monolog 记录所有传入和传出请求和响应变得非常简单。

默认情况下,所有请求和响应都记录在 debug 日志级别。使用合理的默认日志上下文,并且可能包含敏感值的头(如 CookieSet-CookieAuthorization)的值被混淆。每个请求和相应的响应通过一个唯一 ID 相关联。这允许您查询日志以找到任何给定请求的匹配响应。

安装

composer require garbetjie/http-request-logger

使用

Laravel 中的传入请求

在 Laravel 中启用请求记录就像将中间件添加到您的 App\Http\Kernel 类的 $middleware 属性一样简单。下面所示的示例足以确保在 Laravel 中记录所有传入请求和传出响应

<?php

namespace App\Http;

use Illuminate\Foundation\Http\Kernel as HttpKernel;

class Kernel extends HttpKernel
{
    // ...
    protected $middleware = [
        \Garbetjie\RequestLogging\Http\Middleware\IncomingRequestLoggingMiddleware::class
    ];
    // ...
} 

注意:您应将请求记录中间件添加到应用程序的全局中间件堆栈中,并在服务提供程序中确定何时进行日志记录。如果将其添加到中间件组中,任何未捕获的异常都会阻止请求和响应的记录。

有关更多信息,请参阅切换启用/禁用部分。

PSR-15 兼容框架中的传入请求

如果您不是使用 Laravel,而是使用遵循 PSR-15 的框架,您在创建中间件时需要更明确一些。下面的示例使用 Slim 框架作为示例

<?php

$app = Slim\Factory\AppFactory::create();
$app->add(\Garbetjie\RequestLogging\Http\Middleware\IncomingRequestLoggingMiddleware::class);
$app->run();

传入 SOAP 请求

记录传入 SOAP 请求非常简单。只需创建一个 \SoapServer 实例,而是创建一个 \Garbetjie\RequestLogging\Http\SoapServer 的新实例。您可以像使用 \SoapServer 实例一样使用此新实例。以下是一个示例

<?php

$monolog = new Monolog\Logger('logger');
$logger = new Garbetjie\RequestLogging\Http\Logger($monolog);
$server = new Garbetjie\RequestLogging\Http\SoapServer($logger, '/path/to/wsdl/or/null');
$server->setObject(new stdClass());
$server->handle();;

Laravel 中的传出请求

当使用 Laravel 时,您可以在 Laravel 进行依赖注入的任何地方使用 GuzzleHttp\ClientInterfaceGuzzleHttp\Client 类型提示。此软件包包含一个服务提供程序,它会自动为您将中间件添加到处理程序堆栈中。

以下是一个在控制器中使用此类型提示的示例

<?php

namespace App\Http\Controllers;

class MyController
{
    public function myAction(\GuzzleHttp\ClientInterface $client)
    {
        return $client->request('GET', 'https://example.org')->getBody()->getContents();
    }
}

通过 Guzzle 进行传出请求

出站请求日志通过Guzzle中间件处理。为了记录出站请求,只需将一个OutgoingRequestLoggingMiddleware实例添加到您的处理程序堆栈中。理想情况下,这应该是处理程序堆栈中的最后一个中间件,以确保记录的请求表示尽可能准确。

<?php

$monolog = new Monolog\Logger('logger');
$logger = new Garbetjie\RequestLogging\Http\Logger($monolog);

$stack = GuzzleHttp\HandlerStack::create();
$stack->push(new Garbetjie\RequestLogging\Http\Middleware\OutgoingRequestLoggingMiddleware($logger));
$client = new GuzzleHttp\Client(['stack' => $stack]);

$client->request('GET', 'https://example.org');

出站SOAP请求

为了记录出站SOAP请求,只需像记录任何其他出站HTTP请求一样创建您的Guzzle客户端,并将此客户端实例传递给一个新的Garbetjie\RequestLogging\Http\SoapClient实例。您可以使用此SOAP客户端实例,就像使用本地的SoapClient实例一样。

<?php

/* @var GuzzleHttp\Client $guzzleClient */

$soapClient = new Garbetjie\RequestLogging\Http\SoapClient($guzzleClient, null, []);
$soapResponse = $soapClient->MyCustomSoapMethod(['parameters']);

自定义

请求和响应日志的所有方面都是可定制的。这包括用于将请求和响应链接在一起的ID、日志消息以及与每个请求和响应一起记录的上下文。

日志上下文

默认情况下,入站和出站请求会生成一个类似于以下结构的上下文。敏感的标头,如AuthorizationCookie,其值被替换为***

<?php

$context = [
    'id' => '',  // string - contains the unique ID that links this request to its response.
    'method' => '',  // string - upper-cased request method.
    'url' => '',  // string - URL to which the request was sent.
    'body_length' => 0,  // integer - size of the body sent.
    'body' => base64_encode(''),  // string - base64-encoded body sent in the request.
    'headers' => [],  // array<string, string> - array of headers sent in the request. Names are normalized and lower-cased.
];

下面的上下文显示了响应日志上下文的默认结构。任何Set-Cookie标头的值都被移除并替换为***

<?php

$context = [
    'id' => '',  // string - contains the unique ID that links this response to the request that created it.
    'duration' => 0.0,  // float - the duration of the request, in seconds (with fractional milliseconds).
    'status_code' => 0,  // integer - the HTTP status code returned in the response.
    'body_length' => 0,  // integer - the size of the body sent.
    'body' => base64_encode(''),  // string - base64-encoded body sent in the response.
    'headers' => [],  // array<string, string> - array of headers sent in the response. Names are normalized and lower-cased.
];

自定义生成的日志上下文相当简单。

<?php

$monolog = new Monolog\Logger('name');
$logger = new Garbetjie\RequestLogging\Http\Logger($monolog);

$logger->context(
    function (Garbetjie\RequestLogging\Http\RequestEntry $entry): array {
        // Return an array containing the context to log.
        
        return [];
    },
    function (Garbetjie\RequestLogging\Http\ResponseEntry $entry): array {
        // Return an array containing the context to log.
    
        return [];
    }
);

如果您想简单地扩展默认创建的上下文,可以重用现有的上下文提取器。

<?php

$monolog = new Monolog\Logger('name');
$logger = new Garbetjie\RequestLogging\Http\Logger($monolog);

$logger->context(
    function (Garbetjie\RequestLogging\Http\RequestEntry $entry): array {
        $context = (new Garbetjie\RequestLogging\Http\Context\RequestContext())($entry);
        
        $body = base64_decode($context['body']);
        // Modify $body.
        $context['body'] = base64_encode($body);
        
        return $context;
    },
    null
);

ID生成。

默认情况下,用于将请求和响应链接在一起的ID生成是使用endyjasmi/cuid来生成一个完整的CUID。

可以通过提供一个返回包含要使用ID的字符串的可调用对象来轻松自定义。

<?php

$monolog = new Monolog\Logger('name');
$logger = new Garbetjie\RequestLogging\Http\Logger($monolog);

$logger->id(
    function(): string {
        return base64_encode(random_bytes(8));
    }
);

切换日志记录

默认情况下,所有请求和响应都会被记录。但是,可以切换是否记录请求或响应。只需提供一个布尔值,或者提供一个返回布尔值的可调用对象,该值表示是否为给定的请求启用日志记录。

<?php

$monolog = new Monolog\Logger('name');
$logger = new Garbetjie\RequestLogging\Http\Logger($monolog);

function shouldLog($request) {
    if ($request instanceof Symfony\Component\HttpFoundation\Request) {
        return stripos($request->getUri(), 'https://example.org') === false;
    } elseif ($request instanceof Psr\Http\Message\RequestInterface) {
        return stripos((string)$request->getUri(), 'https://example.org') === false;
    } else {
        return false;
    }
}

$logger->enabled(
    function(Garbetjie\RequestLogging\Http\RequestEntry $entry): bool {  // Provide a callable that returns a boolean.
        return shouldLog($entry->request());
    },
    function(Garbetjie\RequestLogging\Http\ResponseEntry $entry): bool {
        return shouldLog($entry->request());
    }
);

// Alternatively:
$logger->enabled(false, true);
$logger->enabled(
    new Garbetjie\RequestLogging\Http\Context\RequestContext(),
    new Garbetjie\RequestLogging\Http\Context\ResponseContext()
);

变更日志

有关完整的变更日志,请参阅CHANGELOG.md