Laravel8記錄請求和返回日誌

bluememory發表於2022-05-14

相關環境: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 協議》,轉載必須註明作者和本文連結

相關文章