相关环境:PHP7.4 + Laravel8.83.8
日志记录是项目必不可少的,他可以帮助我们排查一些问题,那这节我们就以接口请求与接口返回的记录作为重点。
项目里面的接口很多,我们不可能在每个方法里面人工的加Log::info()这种,这样太累也不好维护。所以我是使用中间件来做的:
第一步新建一个中间件
php artisan make:middleware AccessLog
便会在app\Http\Middleware下面生成AccessLog.php,代码如下:
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
class AccessLog
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure(\Illuminate\Http\Request): (\Illuminate\Http\Response|\Illuminate\Http\RedirectResponse) $next
* @return \Illuminate\Http\Response|\Illuminate\Http\RedirectResponse
*/
public function handle(Request $request, Closure $next)
{
$traceId = md5(time() . mt_rand(1, 1000000));
// 记录请求信息
$requestMessage = [
'traceId' => $traceId,
'url' => $request->url(),
'method' => $request->method(),
'ip' => $request->ips(),
'headers' => $request->header('Authorization'),
'params' => $request->all()
];
Log::info("请求信息:", $requestMessage);
$respone = $next($request);
$responeData = [
'traceId' => $traceId,
'respone' => json_decode($respone->getContent(), true) ?? ""
];
Log::info("返回信息:", $responeData);
return $respone;
}
}
以$respone = $next($request);为界限,上面是请求下面是返回。
第二步配置全局路由
在app\Http\Kernel.php里的$middleware数组加入AccessLog:
<?php
namespace App\Http;
use App\Http\Middleware\AccessLog;
use Illuminate\Foundation\Http\Kernel as HttpKernel;
class Kernel extends HttpKernel
{
/**
* The application's global HTTP middleware stack.
*
* These middleware are run during every request to your application.
*
* @var array<int, class-string|string>
*/
protected $middleware = [
\App\Http\Middleware\TrustProxies::class,
\Fruitcake\Cors\HandleCors::class,
\App\Http\Middleware\PreventRequestsDuringMaintenance::class,
\Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
\App\Http\Middleware\TrimStrings::class,
\Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
AccessLog::class, //全局请求返回日志记录
];
这样所有请求都会进入这个AccessLog里面。
测试一下:
控制器代码:
<?php
namespace App\Http\Admin\User;
use App\Http\Admin\Controller;
class UserController extends Controller
{
public function detail()
{
$id = request()->input("id");
$data = UserAdmin::find($id, ['id', 'mobile']);
return response()->json([
'code' => 0,
'message' => "success",
'data' => $data
]);
}
}
日志打印如下:
补充:
如果代码出现异常,我们发现日志会把这异常也记录下来,模拟一下,查个不存在的字段:
<?php
namespace App\Http\Admin\User;
use App\Http\Admin\Controller;
class UserController extends Controller
{
public function detail()
{
$id = request()->input("id");
$data = UserAdmin::find($id, ['id', 'test']); //不存在的test字段
return response()->json([
'code' => 0,
'message' => "success",
'data' => $data
]);
}
}
这个时候去看日志,除了请求返回日志外,还有一个很长很长的异常日志【图片只截取一小部分,返回日志在下面没有截取出来】:
那如果你不想打印这个异常日志,也可以实现,在app\Exceptions\Handler.php里面有个$dontReport数组,他就是配置不会被记录到日志文件的异常类型数组。我们看异常日志,他是通过Illuminate\Database\QueryException类抛出的,那我们在$dontReport加上他:
<?php
namespace App\Exceptions;
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
use Throwable;
class Handler extends ExceptionHandler
{
/**
* A list of the exception types that are not reported.
* 不会被记录到日志文件的异常类型数组
*
* @var array<int, class-string<Throwable>>
*/
protected $dontReport = [
\Illuminate\Database\QueryException::class,
];
/**
* A list of the inputs that are never flashed for validation exceptions.
*
* @var array<int, string>
*/
protected $dontFlash = [
'current_password',
'password',
'password_confirmation',
];
/**
* Register the exception handling callbacks for the application.
*
* @return void
*/
public function register()
{
$this->reportable(function (Throwable $e) {
//
});
}
public function render($request, Throwable $e)
{
return response()->json([
'code' => $e->getCode() ?? 1,
'file' => $e->getFile(),
'line' => $e->getLine(),
'message' => $e->getMessage() ?? "error!",
'data' => []
]);
}
}
再次运行接口,发现异常日志就不会记录了: