相關環境: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' => []
]);
}
}
再次執行介面,發現異常日誌就不會記錄了:
本作品採用《CC 協議》,轉載必須註明作者和本文連結