本文目录导读:

我来介绍几种在 Laravel 中通过中间件实现请求日志记录的方法:
创建日志中间件
php artisan make:middleware RequestLogger
基础版本实现
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
class RequestLogger
{
/**
* 处理请求
*/
public function handle(Request $request, Closure $next)
{
// 请求前记录
$startTime = microtime(true);
$response = $next($request);
// 请求后记录
$duration = microtime(true) - $startTime;
$this->logRequest($request, $response, $duration);
return $response;
}
/**
* 记录请求日志
*/
protected function logRequest($request, $response, $duration)
{
$logData = [
'method' => $request->method(),
'url' => $request->fullUrl(),
'ip' => $request->ip(),
'user_agent' => $request->userAgent(),
'status' => $response->status(),
'duration' => round($duration * 1000, 2) . 'ms',
'timestamp' => now()->toDateTimeString(),
];
// 记录到日志文件
Log::channel('request_log')->info('Request Log', $logData);
}
}
配置日志通道
在 config/logging.php 中添加专门通道:
'channels' => [
'request_log' => [
'driver' => 'daily',
'path' => storage_path('logs/requests/request.log'),
'level' => 'info',
'days' => 30,
'formatter' => Monolog\Formatter\LineFormatter::class,
'formatter_with' => [
'format' => "[%datetime%] %message% %context%\n",
],
],
],
高级版本(支持数据库记录)
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
use App\Models\RequestLog;
use Illuminate\Support\Str;
class AdvancedRequestLogger
{
// 需要排除的路径
protected $excludedPaths = [
'livewire/*',
'telescope/*',
'horizon/*',
'_debugbar/*',
];
// 敏感字段(记录时需隐藏)
protected $sensitiveFields = [
'password',
'password_confirmation',
'token',
'secret',
];
public function handle(Request $request, Closure $next)
{
// 检查是否需要排除
if ($this->shouldExclude($request)) {
return $next($request);
}
$startTime = microtime(true);
$requestId = (string) Str::uuid();
// 添加请求ID到请求头
$request->headers->set('X-Request-Id', $requestId);
$response = $next($request);
$duration = microtime(true) - $startTime;
$this->logRequest($request, $response, $duration, $requestId);
// 添加响应头
$response->headers->set('X-Request-Id', $requestId);
$response->headers->set('X-Response-Time', round($duration * 1000, 2) . 'ms');
return $response;
}
protected function logRequest($request, $response, $duration, $requestId)
{
$requestData = $request->all();
$this->hideSensitiveData($requestData);
$logData = [
'request_id' => $requestId,
'method' => $request->method(),
'url' => $request->fullUrl(),
'ip' => $request->ip(),
'user_agent' => $request->userAgent(),
'user_id' => auth()->id(),
'headers' => $this->getRelevantHeaders($request),
'input_data' => $requestData,
'response_status' => $response->status(),
'response_body' => $this->getResponseBody($response),
'duration' => round($duration * 1000, 2),
'memory_usage' => memory_get_peak_usage(true) / 1024 / 1024 . 'MB',
];
// 记录到文件
Log::channel('request_log')->info('API Request', $logData);
// 可选:记录到数据库
try {
RequestLog::create($logData);
} catch (\Exception $e) {
Log::error('Failed to save request log to database', ['error' => $e->getMessage()]);
}
}
protected function shouldExclude($request)
{
foreach ($this->excludedPaths as $pattern) {
if ($request->is($pattern)) {
return true;
}
}
return false;
}
protected function hideSensitiveData(&$data)
{
foreach ($data as $key => &$value) {
if (in_array($key, $this->sensitiveFields)) {
$value = '******';
}
}
}
protected function getRelevantHeaders($request)
{
$relevantHeaders = [
'content-type',
'accept',
'authorization',
'x-requested-with',
];
$headers = [];
foreach ($relevantHeaders as $header) {
if ($value = $request->header($header)) {
$headers[$header] = $header === 'authorization' ? '******' : $value;
}
}
return $headers;
}
protected function getResponseBody($response)
{
$content = $response->getContent();
// 限制响应体大小
if (strlen($content) > 10000) {
return substr($content, 0, 10000) . '...';
}
return $content;
}
}
创建数据库模型
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class RequestLog extends Model
{
protected $fillable = [
'request_id',
'method',
'url',
'ip',
'user_agent',
'user_id',
'headers',
'input_data',
'response_status',
'response_body',
'duration',
'memory_usage',
];
protected $casts = [
'headers' => 'array',
'input_data' => 'array',
];
// 按用户范围查询
public function scopeByUser($query, $userId)
{
return $query->where('user_id', $userId);
}
// 按状态码查询
public function scopeByStatus($query, $status)
{
return $query->where('response_status', $status);
}
}
注册中间件
在 app/Http/Kernel.php 中注册:
protected $middlewareGroups = [
'web' => [
// ... 其他中间件
\App\Http\Middleware\RequestLogger::class, // 基础版本
// \App\Http\Middleware\AdvancedRequestLogger::class, // 高级版本
],
'api' => [
// ... 其他中间件
\App\Http\Middleware\AdvancedRequestLogger::class,
],
];
// 或者注册为路由中间件
protected $routeMiddleware = [
// ... 其他中间件
'log.request' => \App\Http\Middleware\RequestLogger::class,
];
使用示例
在路由中使用:
// 应用到整个路由组
Route::middleware(['log.request'])->group(function () {
Route::get('/users', [UserController::class, 'index']);
Route::post('/users', [UserController::class, 'store']);
});
// 应用到单个路由
Route::get('/special-page', [PageController::class, 'show'])
->middleware('log.request');
查看日志:
# 查看日志文件
tail -f storage/logs/requests/request-2024-01-01.log
# 查询数据库
DB::table('request_logs')
->where('response_status', '>=', 500)
->orderBy('created_at', 'desc')
->get();
优化建议
// 1. 异步记录日志(使用队列)
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
class LogRequestJob implements ShouldQueue
{
use Queueable, Dispatchable, InteractsWithQueue, SerializesModels;
public function handle($logData)
{
// 处理日志记录
}
}
// 2. 使用事件系统
Event::listen('request.logged', function ($logData) {
// 发送到外部日志系统
});
// 3. 条件记录
if (config('app.log_requests')) {
$this->logRequest($request, $response, $duration);
}
这样实现后,你的 Laravel 应用就会自动记录所有请求的详细信息,便于调试、监控和审计。