Laravel请求响应日志怎么中间件

wen PHP项目 1

本文目录导读:

Laravel请求响应日志怎么中间件

  1. 方法一:创建自定义中间件(推荐)
  2. 方法二:使用Terminable中间件(异步日志)
  3. 方法三:记录敏感信息过滤
  4. 方法四:使用事件监听器(高级用法)
  5. 使用建议

在Laravel中,使用中间件记录请求和响应日志是一个很常见的需求,以下是几种实现方式:

创建自定义中间件(推荐)

创建中间件

php artisan make:middleware RequestResponseLogger

编写中间件逻辑

<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
use Symfony\Component\HttpFoundation\Response;
class RequestResponseLogger
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle(Request $request, Closure $next)
    {
        // 记录请求日志
        $this->logRequest($request);
        // 继续处理请求
        $response = $next($request);
        // 记录响应日志
        $this->logResponse($request, $response);
        return $response;
    }
    /**
     * 记录请求日志
     */
    protected function logRequest(Request $request)
    {
        Log::channel('api')->info('Request', [
            'method' => $request->method(),
            'url' => $request->fullUrl(),
            'headers' => $request->headers->all(),
            'body' => $request->all(),
            'ip' => $request->ip(),
            'user_agent' => $request->userAgent(),
        ]);
    }
    /**
     * 记录响应日志
     */
    protected function logResponse(Request $request, Response $response)
    {
        Log::channel('api')->info('Response', [
            'url' => $request->fullUrl(),
            'status' => $response->getStatusCode(),
            'headers' => $response->headers->all(),
            'body' => $response->getContent(),
        ]);
    }
}

创建日志频道(可选)

config/logging.php 中添加:

'channels' => [
    // ... 其他频道
    'api' => [
        'driver' => 'daily',
        'path' => storage_path('logs/api.log'),
        'level' => 'info',
        'days' => 14,
        'formatter' => Monolog\Formatter\LineFormatter::class,
        'formatter_with' => [
            'format' => "[%datetime%] %channel%.%level_name%: %message% %context%\n",
        ],
    ],
],

注册中间件

app/Http/Kernel.php 中注册:

全局中间件(所有请求):

protected $middleware = [
    // ...
    \App\Http\Middleware\RequestResponseLogger::class,
];

路由组中间件:

protected $middlewareGroups = [
    'web' => [
        // ...
    ],
    'api' => [
        // ...
        \App\Http\Middleware\RequestResponseLogger::class,
    ],
];

单个路由中间件:

protected $routeMiddleware = [
    // ...
    'log.api' => \App\Http\Middleware\RequestResponseLogger::class,
];

使用Terminable中间件(异步日志)

如果希望在响应发送后记录日志,可以使用Terminable中间件:

<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
class AsyncRequestLogger
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle(Request $request, Closure $next)
    {
        // 保存请求数据以便后续使用
        $request->attributes->set('start_time', microtime(true));
        return $next($request);
    }
    /**
     * Handle tasks after the response has been sent to the browser.
     */
    public function terminate(Request $request, $response)
    {
        $duration = microtime(true) - $request->attributes->get('start_time', 0);
        Log::channel('api')->info('API Request Log', [
            'method' => $request->method(),
            'url' => $request->fullUrl(),
            'status' => $response->getStatusCode(),
            'duration' => round($duration * 1000, 2) . 'ms',
            'ip' => $request->ip(),
        ]);
    }
}

记录敏感信息过滤

在实际应用中,可能需要过滤敏感信息(如密码、token等):

<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
class SecureRequestLogger
{
    protected $hiddenFields = [
        'password',
        'password_confirmation',
        'token',
        'secret',
        'credit_card',
    ];
    public function handle(Request $request, Closure $next)
    {
        $requestData = $this->filterSensitiveData($request->all());
        Log::info('Request', [
            'method' => $request->method(),
            'url' => $request->fullUrl(),
            'body' => $requestData,
        ]);
        $response = $next($request);
        Log::info('Response', [
            'url' => $request->fullUrl(),
            'status' => $response->getStatusCode(),
        ]);
        return $response;
    }
    protected function filterSensitiveData(array $data)
    {
        foreach ($this->hiddenFields as $field) {
            if (isset($data[$field])) {
                $data[$field] = '*** FILTERED ***';
            }
        }
        return $data;
    }
}

使用事件监听器(高级用法)

也可以使用Laravel的事件系统来实现:

// app/Providers/EventServiceProvider.php
protected $listen = [
    \Illuminate\Foundation\Http\Events\RequestHandled::class => [
        \App\Listeners\LogRequestResponse::class,
    ],
];

创建监听器:

php artisan make:listener LogRequestResponse
<?php
namespace App\Listeners;
use Illuminate\Foundation\Http\Events\RequestHandled;
use Illuminate\Support\Facades\Log;
class LogRequestResponse
{
    public function handle(RequestHandled $event)
    {
        $request = $event->request;
        $response = $event->response;
        Log::info('Request handled', [
            'method' => $request->method(),
            'url' => $request->fullUrl(),
            'status' => $response->getStatusCode(),
            'duration' => microtime(true) - LARAVEL_START,
        ]);
    }
}

使用建议

  1. 性能考虑:生产环境中建议使用异步日志记录
  2. 日志级别:根据不同环境设置不同的日志级别
  3. 敏感信息:确保过滤掉密码、token等敏感信息
  4. 日志轮转:使用 daily 驱动自动管理日志文件大小
  5. 条件记录:可以根据需求只记录特定状态码或错误的请求
// 条件记录示例
if ($response->getStatusCode() >= 400) {
    Log::warning('Error response', [...]);
}

选择哪种方式取决于你的具体需求,一般推荐使用方法一(自定义中间件),因为它最灵活且易于维护。

抱歉,评论功能暂时关闭!