1. 程式人生 > >Laravel 5.5 異常處理 & 錯誤日誌

Laravel 5.5 異常處理 & 錯誤日誌

簡介

Laravel 預設已經為我們配置好了錯誤和異常處理,我們在 App\Exceptions\Handler 類中觸發異常並將響應返回給使用者。

此外,Laravel 還集成了 Monolog 日誌庫以便提供各種功能強大的日誌處理器,預設情況下,Laravel 已經為我們配置了一些處理器,我們可以選擇單個日誌檔案,也可以選擇記錄錯誤資訊到系統日誌。

配置

除錯模式

配置檔案 config/app.php 中的 debug 配置項表示是否開啟除錯模式,除錯模式下會將錯誤資訊直接暴露給客戶端。

預設情況下,該配置項通過 .env 檔案中的環境變數 APP_DEBUG 進行設定,預設值為 true ,即開啟除錯模式。

對本地開發而言,你應該設定環境變數 APP_DEBUG 值為 true。在生產環境,該值應該被設定為 false。如果在生產環境被設定為 true,就有可能將一些敏感的資訊暴露給終端使用者。

日誌儲存

Laravel 支援的日誌檔案型別為 single, daily, syslog 和 errorlog。

  • single: 所有的日誌資訊會記錄到單個日誌檔案裡。
  • daily:按天生成日誌檔案。
  • syslog: 通過系統 syslog 服務處理日誌資訊。
  • errorlog: 通過 PHP error_log 處理器處理日誌資訊。

如果你想要日誌檔案按天生成而不是生成並記錄到單個檔案,應該在配置檔案 config/app.php 中設定 log 值如下:

'log' => 'daily'

注:底層處理機制可以參考 Illuminate\Log\LogServiceProvider 中的實現邏輯。

日誌檔案最大生命週期

使用 daily 日誌模式的時候,Laravel 預設最多為我們保留最近 5 天的日誌,如果你想要修改這個時間,需要新增一個配置 log_max_files 到 app 配置檔案:

'log_max_files' => 30

日誌錯誤級別

使用 Monolog 的時候,日誌訊息可能有不同的錯誤級別,預設情況下,Laravel 將所有級別日誌寫到儲存器,但是在生產環境中,你可能想要配置最低錯誤級別,這可以通過在配置檔案 app.php 中新增配置項 log_level 來實現。

該配置項被配置後,Laravel 會記錄所有錯誤級別大於等於這個指定級別的日誌。

例如,如果配置 log_level 為 error ,則會記錄 error、critical、alert 以及 emergency 級別的日誌資訊。

'log_level' => env('APP_LOG_LEVEL', 'error'),

注:Monolog 支援以下錯誤級別:debug、info、notice、warning、error、critical、alert、emergency。

自定義 Monolog 配置

如果你想要在應用中完全控制 Monolog 的配置,可以使用configureMonologUsing 方法。你需要在 bootstrap/app.php 檔案返回 $app 變數之前呼叫該方法:

$app->configureMonologUsing(function($monolog) {
    $monolog->pushHandler(...);
});

return $app;

自定義頻道名稱

預設情況下,Monolog 會通過一個與當前環境匹配的名字進行例項化,例如 production 或 local。如果想修改這個值,需要新增 log_channel 配置項到配置檔案 config/app.php:

'log_channel' => env('APP_LOG_CHANNEL', 'my-app-name'),

異常處理器

所有異常都由類 App\Exceptions\Handler 處理,該類包含兩個方法:report 和 render。

report 方法

report 方法用於記錄異常並將其傳送給外部服務如 Bugsnag 或 Sentry。

預設情況下,report 方法只是將異常傳遞給異常被記錄的基類,當然你也可以按自己的需要記錄異常並進行相關處理。

例如,如果你需要以不同方式報告不同型別的異常,可使用 PHP 的 instanceof 操作符:

/**
 * Report or log an exception.
 *
 * This is a great spot to send exceptions to Sentry, Bugsnag, etc.
 *
 * @param  \Exception  $exception
 * @return void
 */
public function report(Exception $exception)
{
    if ($exception instanceof CustomException) {
        //
    }
    parent::report($exception);
}

report 輔助函式

有時候你可能需要報告一個異常並繼續處理當前請求。輔助函式 report 允許你使用異常處理器的 report 方法快速報告一個異常而不會渲染錯誤頁:

public function isValid($value)
{
    try {
        // Validate the value...
    } catch (Exception $e) {
        report($e);

        return false;
    }
}

通過型別忽略異常

異常處理器的 $dontReport 屬性包含一個不會被記錄的異常型別陣列,預設情況下,404 錯誤異常不會被寫到日誌檔案,如果需要的話你可以新增其他異常型別到這個陣列:

/**
 * 不應該被報告的異常型別列表.
 *
 * @var array
 */
protected $dontReport = [
    \Illuminate\Auth\AuthenticationException::class,
    \Illuminate\Auth\Access\AuthorizationException::class,
    \Symfony\Component\HttpKernel\Exception\HttpException::class,
    \Illuminate\Database\Eloquent\ModelNotFoundException::class,
    \Illuminate\Validation\ValidationException::class,
];

render 方法

render 方法負責將給定異常轉化為傳送給瀏覽器的 HTTP 響應。

預設情況下,異常被傳遞給為你生成響應的基類。當然,你也可以按照自己的需要檢查異常型別或者返回自定義響應:

/**
 * 將異常渲染到HTTP響應中
 *
 * @param  \Illuminate\Http\Request  $request
 * @param  \Exception  $e
 * @return \Illuminate\Http\Response
 */
public function render($request, Exception $e){
    if ($e instanceof CustomException) {
        return response()->view('errors.custom', [], 500);
    }

    return parent::render($request, $e);
}

可報告 & 可渲染異常

除了在異常處理器的 report 和 render 方法中進行異常型別檢查外,還可以在自定義異常中直接定義 report 和 render 方法。

當異常中存在這些方法時,框架會自動呼叫它們:

<?php

namespace App\Exceptions;

use Exception;

class RenderException extends Exception
{
    /**
     * Report the exception.
     *
     * @return void
     */
    public function report()
    {
        //
    }

    /**
     * Render the exception into an HTTP response.
     *
     * @param  \Illuminate\Http\Request
     * @return \Illuminate\Http\Response
     */
    public function render($request)
    {
        return response(...);
    }
}

HTTP 異常

有些異常描述來自伺服器的 HTTP 錯誤碼,例如,這可能是一個“頁面未找到”錯誤(404),“認證失敗錯誤”(401)亦或是程式出錯造成的500錯誤,為了在應用中生成這樣的響應,可以使用 abort 輔助函式:

abort(404);

abort 輔助函式會立即引發一個會被異常處理器渲染的異常,此外,你還可以像這樣提供響應描述:

abort(403, '未授權操作');

該方法可在請求生命週期的任何時間點使用。

自定義 HTTP 錯誤頁面

在 Laravel 中,返回不同 HTTP 狀態碼的錯誤頁面很簡單,例如,如果你想要自定義 404 錯誤頁面,建立一個 resources/views/errors/404.blade.php 檔案,該檢視檔案用於渲染程式返回的所有 404 錯誤。

需要注意的是,該目錄下的檢視命名應該和相應的 HTTP 狀態碼相匹配。abort 函式觸發的 HttpException 異常會以 $exception 變數的方式傳遞給檢視:

<h2>{{ $exception->getMessage() }}</h2>

日誌

Laravel 基於強大的 Monolog 庫提供了簡單的日誌抽象層,預設情況下,Laravel 的日誌配置是為應用記錄單個日誌檔案。

日誌檔案的儲存位置是 storage/logs 目錄。

/*
|--------------------------------------------------------------------------
| Logging Configuration
|--------------------------------------------------------------------------
|
| Here you may configure the log settings for your application. Out of
| the box, Laravel uses the Monolog PHP logging library. This gives
| you a variety of powerful log handlers / formatters to utilize.
|
| Available Settings: "single", "daily", "syslog", "errorlog"
|
*/

'log' => env('APP_LOG', 'single'),

'log_level' => env('APP_LOG_LEVEL', 'debug'),

應用執行過程中,所有級別大於或等於 debug 的錯誤日誌都會被自動記錄到 storage/logs 目錄中。

也可以使用 Log 門面,手動記錄日誌資訊。

<?php

namespace App\Http\Controllers;

use App\User;
use Illuminate\Support\Facades\Log;
use App\Http\Controllers\Controller;

class UserController extends Controller
{
    /**
     * 顯示指定使用者的屬性
     *
     * @param  int  $id
     * @return Response
     */
    public function showProfile($id)
    {
        Log::info('Showing user profile for user: '.$id);
        return view('user.profile', ['user' => User::findOrFail($id)]);
    }
}

該日誌記錄器提供了 RFC 5424 中定義的八種日誌級別:emergency、alert、critical、error、warning、notice、info 和 debug。

  • Log::emergency($error);
  • Log::alert($error);
  • Log::critical($error);
  • Log::error($error);
  • Log::warning($error);
  • Log::notice($error);
  • Log::info($error);
  • Log::debug($error);

上下文資訊

上下文資料也會以陣列形式傳遞給日誌方法,然後和日誌訊息一起被格式化和顯示:

Log::info('User failed to login.', ['id' => $user->id]);

訪問底層 Monolog 例項

Monolog 有多個可用於日誌的處理器,如果需要的話,你可以訪問 Laravel 使用的底層 Monolog 例項:

$monolog = Log::getMonolog();