系統日誌輸出規範

「to後端」在業務運營過程中,我們希望能夠主動的瞭解應用系統存在的bug和錯誤,以便進行迭代優化,提升系統的健壯性。在系統運維過程中,日誌的管理非常重要,需要進行規範化的日誌管理,以便進行故障分析、錯誤排查和問題回溯。本文針對laravel框架,介紹日誌管理規範。

系統日誌輸出規範

一、設計思路

以接口調用為例進行說明,每次用戶請求,分配請求流水,存入session,在本次請求過程中輸出的日誌都加入請求流水標識,在工具類中封裝日誌打印方法,從請求進入到報文返回,均帶流水號,以便進行日誌查詢。

系統日誌輸出規範

日誌記錄過程

二、實例展示

以下為測試環境中的實例展示:

系統日誌輸出規範

日誌實例

紅色框:LOG類型(Request:請求接口;Process:過程接口;Error:代碼錯誤;Back:返回接口);

黃色框:流水號;

藍色框:過程接口中的過程描述;

綠色框:動作控制器;

紅線:數據;

白框:錯誤返回信息

三、實現方法

以laravel為例,實現方案如下:

  • Utils文件添加以下方法:
/**
 * 請求接口LOG
 * @param string $logPath 請求接口
 * @param string $logIp IP地址
 * @param array $logData 請求參數
 */
 public static function requestLog($logPath="",$logIp="",$logData=[]){
 $LOGO_NO = 'LOG'.date('Ymdhis',time()).rand(1000000,10000000);
 Session::put('LOGO_NO', $LOGO_NO);
 Log::info('[Request] '.$LOGO_NO.' '. $logPath . "(" . $logIp . ") " .json_encode($logData));
 }
 /**
 * 過程中接口LOG
 * @param string $logModular 模塊
 * @param string/array $logData 數據
 * @param string $logContent 備註
 */
 public static function processLog($logModular="", $logContent="", $logData=""){
 $LOGO_NO = Session::get("LOGO_NO");
 if(is_array($logData)){
 $logData = json_encode($logData,true);
 }
 if($logContent){
 Log::info('[Process] '.$LOGO_NO.' '.$logContent.' '.$logModular .' ' . $logData );
 }
 else{
 Log::info('[Process] '.$LOGO_NO.' '.$logModular .' ' . $logData );
 }
 }
 /**
 * 過程報錯接口LOG
 * @param string/array $logData 數據
 */
 public static function errorLog($logData=""){
 $LOGO_NO = Session::get("LOGO_NO");
 if(!$LOGO_NO){
 $LOGO_NO = 'LOG'.date('Ymdhis',time()).rand(1000000,10000000);
 Session::put('LOGO_NO', $LOGO_NO);
 }
 if(is_array($logData)){
 $logData = json_encode($logData,true);
 }
 Log::info('[Error] '.$LOGO_NO.' '. $logData );
 Session::remove("LOGO_NO");
 }
 /**
 * 返回接口LOG
 * @param string $logModular 模塊
 * @param string/array $logData 數據
 */
 public static function backLog($logModular="", $logData=[]){
 $LOGO_NO = Session::get("LOGO_NO");
 $log = array(
 'code' => $logData['code'],
 'result' => $logData['result'],
 'message' => $logData['message'],
 );
 if(array_key_exists('ret',$logData)){
 $log['ret'] = $logData['ret'];
 }
 Log::info('[Back] '.$LOGO_NO.' '. $logModular .' ' .json_encode($log,true));
 Session::remove("LOGO_NO");
 }
 /**
 * 自定義LOG
 * @param string $label log標籤
 * @param string $logContent 備註
 * @param string/array $logData 數據
 */
 public static function customLog($label="DEBUG", $logContent="", $logData=""){
 $LOGO_NO = Session::get("LOGO_NO");
 if(!$LOGO_NO){
 $LOGO_NO = 'LOG'.date('Ymdhis',time()).rand(1000000,10000000);
 Session::put('LOGO_NO', $LOGO_NO);
 }
 if(is_array($logData)){
 // 將數組轉為字符串
 $logDataArray = $logData;
 $logData = '';
 foreach ($logDataArray as $key=>$logDataRow){
 if(is_array($logDataRow)){
 $logDataRow = json_encode($logDataRow,true);
 }
 $str=$key.":".$logDataRow;
 $logData.=$str.' ';
 }
 }
 if($logContent){
 Log::info('['.$label.'] '.$LOGO_NO.' '.$logContent.' '. $logData );
 }
 else{
 Log::info('['.$label.'] '.$LOGO_NO.' '. $logData );
 }
 Session::remove("LOGO_NO");
 }
  • 調用:
  1. 入參時調用(中間件BeforeRequest)
public function handle($request, Closure $next)
 {
 Utils::requestLog($request->getPathInfo(), $request->getClientIp(), $request->all());
 return $next($request);
 }
  1. 過程中調用,例如:
public function recommendUsers(Request $request)
 {
 Utils::processLog(__METHOD__, '測試'); // 1
 $data = Utils::requestParameter($request);
 $user_id = $data['user_id'];
 $language = $data['language'];
 Utils::processLog(__METHOD__, '',$language); // 2
 Utils::processLog(__METHOD__, '',[$language,$user_id]); // 3
 Utils::processLog(__METHOD__,'我是測試', [$language,$user_id]); // 4
 //推薦騎士
 $users = self::getUsersWithOutNotice($user_id, $language);
 return ApiResponse::makeResponse(true, $users, ApiResponse::SUCCESS_CODE, $data['language']);
 }
  1. 代碼錯誤時(/app/Exceptions/Handler.php)
public function report(Exception $exception)
 {
 Utils::errorLog($exception); //添加此行代碼
 parent::report($exception);
 }
 
  1. 返回時調用(ApiResponse):
//以上為省略代碼
Utils::backLog(__METHOD__, $rsp);
return response()->json($rsp);
  1. 5.自定義LOG,以檢測到錯誤路由為例:
  2. (/vendor/laravel/framework/src/Illuminate/Routing/RouteCollection.php)
public function match(Request $request)
 {
 $routes = $this->get($request->getMethod());
 // First, we will see if we can find a matching route for this current request
 // method. If we can, great, we can just return it so that it can be called
 // by the consumer. Otherwise we will check for routes with another verb.
 $route = $this->matchAgainstRoutes($routes, $request);
 if (! is_null($route)) {
 return $route->bind($request);
 }
 /////////////LOG-START////////////////////
 $logData=array(
 'route'=>$request->getPathInfo(),
 'method'=>$request->getMethod(),
 'Info'=>$request
 );
 Utils::customLog("ErrorNotFoundHttpException","路由錯誤",$logData);
 /////////////LOG-END////////////////////
 // If no route was found we will now check if a matching route is specified by
 // another HTTP verb. If it is we will need to throw a MethodNotAllowed and
 // inform the user agent of which HTTP verb it should use for this route.
 $others = $this->checkForAlternateVerbs($request);
 if (count($others) > 0) {
 return $this->getRouteForMethods($request, $others);
 }
 throw new NotFoundHttpException;
 }

四、後記

應用日誌是排查故障、發展隱患、回溯問題的重要工具,本次主要講解應用系統的日誌輸出方案,每天應該進行日誌巡檢(tail -f -n 100000 laravel.log | grep Error),檢索錯誤信息,及時優化業務系統,提升穩定性和健壯性。


分享到:


相關文章: