- 作為一個PHPer一直思考PHP怎麼做高效能(個人基礎比較弱),怎麼微服務,之前真不知道,不是概念而是怎麼落地,談概念有用但是不落地有點扯
- Swoole沒有實際專案用過,swoole相關框架也沒了解過也不知道,獲取知道了可能對於之前的問題就可能有一些答案了
- Hyperf的出現簡直就是太及時了,文件清晰,框架靈活等等等,簡直不要太好
- 雖然Hyperf已經這麼好了,但是還是希望把它稍微按著自己喜歡的規範改造一下,這裡就把多有對它的改造都定義為自己的開發規範,如果大家覺得有道理可以沿用
- 感謝swoole團隊(韓老師等……),感謝Hyperf團隊(黃老師等……)
- 看完文章是否可以點個贊!!!!
工欲善其事,必先利其器,它有個名字
HyperfCMS
- 學習一個框架,日誌處理是一個很很很重要的事情,個人部落格、入口網站、商城網站、公司內部管理系統等等,無論大小都應該需要日誌處理,但是系統的複雜度應該對應有靈活的日誌處理方案。談一下個人的看法:
- 預設肯定支援檔案日誌,有業務需求可以在對檔案日誌進行收集,比如ELK,給公司寫個門戶一上來你就搞一套完整(複雜)的日誌機制感覺沒有真正的必要。如果不二次處理檔案日誌檢視起來就沒然後了……
- DB日誌,用資料庫儲存日誌,如果網站沒有太多的請求,是沒啥問題的。但是請求多了有可能資料表驟增至1個億……
- 通過API的方式對接第三方日誌系統(這裡只是對接了AliyunSLS),注意是通過API方式也就是同步,在每次請求或者認為的位置呼叫API,當然這個會對業務有一些可以忽略不計的影響,如果遇到不可以忽略的影響的時候,針對業務的完整的日誌替代方案也應該早就有了……
- 雖然logger配置很靈活,但是我只用處理handlers來實現修改,增加驅動判斷:
$driver = env('LOG_DRIVER', 'file'); $handlers = []; if ($driver == 'file') { $handlers = [ // info、waring、notice日誌等 [ 'class' => App\Core\Handler\LogFileHandler::class, 'constructor' => [ 'stream' => BASE_PATH . '/runtime/logs/hyperf.log', 'level' => Monolog\Logger::INFO, ], 'formatter' => [ 'class' => Monolog\Formatter\LineFormatter::class, 'constructor' => [ 'format' => "%datetime%||%channel%||%level_name%||%message%||%context%||%extra%\n", 'dateFormat' => null, 'allowInlineLineBreaks' => true, ], ] ], // debug日誌 [ 'class' => App\Core\Handler\LogFileHandler::class, 'constructor' => [ 'stream' => BASE_PATH . '/runtime/logs/hyperf-debug.log', 'level' => Monolog\Logger::DEBUG, ], 'formatter' => [ 'class' => Monolog\Formatter\LineFormatter::class, 'constructor' => [ 'format' => "%datetime%||%channel%||%level_name%||%message%||%context%||%extra%\n", 'dateFormat' => null, 'allowInlineLineBreaks' => true, ], ] ], // error日誌 [ 'class' => App\Core\Handler\LogFileHandler::class, 'constructor' => [ 'stream' => BASE_PATH . '/runtime/logs/hyperf-error.log', 'level' => Monolog\Logger::ERROR, ], 'formatter' => [ 'class' => Monolog\Formatter\LineFormatter::class, 'constructor' => [ 'format' => "%datetime%||%channel%||%level_name%||%message%||%context%||%extra%\n", 'dateFormat' => null, 'allowInlineLineBreaks' => true, ], ] ], ]; } if ($driver == 'db') { $handlers = [ // 資料庫日誌儲存 [ 'class' => App\Core\Handler\LogDbHandler::class, 'formatter' => [ 'class' => Monolog\Formatter\LineFormatter::class, 'constructor' => [ 'format' => "%datetime%||%channel%||%level_name%||%message%||%context%||%extra%\n", 'dateFormat' => null, 'allowInlineLineBreaks' => true, ], ] ], ]; } if ($driver == 'sls') { $handlers = [ // 資料庫日誌儲存 [ 'class' => App\Core\Handler\LogSlsHandler::class, 'formatter' => [ 'class' => Monolog\Formatter\LineFormatter::class, 'constructor' => [ 'format' => "%datetime%||%channel%||%level_name%||%message%||%context%||%extra%\n", 'dateFormat' => null, 'allowInlineLineBreaks' => true, ], ] ], ]; } return [ 'default' => [ // 配置多個hander,根據每個handel產生日誌 'handlers' => $handlers ], ];
-
修改filelog的規則,將debug、error、其它型別,分別用三個不同檔案儲存。
namespace App\Core\Handler; use Monolog\Handler\StreamHandler; use Monolog\Logger; /** * LogFileHandler * 日誌處理,儲存檔案 * 將info、warning、notic等型別儲存一個檔案,debug型別儲存一個檔案,error型別儲存一個檔案 * @package App\Core\Handler * User:YM * Date:2019/11/29 * Time:下午6:39 */ class LogFileHandler extends StreamHandler { /** * handle * 改寫父類方法,增加判斷日誌輸出,框架日誌 * User:YM * Date:2019/12/21 * Time:下午7:16 * @param array $record * @return bool */ public function handle(array $record) { if (!$this->isHandling($record)) { return false; } $record = $this->processRecord($record); // 判斷系統允許日誌型別 if ( ! isStdoutLog($record['level_name']) ) { return false; } // 判斷是否處理框架日誌 if ( ! env('HF_LOG', false) && $record['channel'] == 'hyperf' ) { return false; } $record['formatted'] = $this->getFormatter()->format($record); $this->write($record); return false === $this->bubble; } /** * isHandling * 重寫該方法,作用改變日誌的儲存檔案的方式。 * 將debug,error,單獨儲存,其它的按著原來規則 * User:YM * Date:2019/11/29 * Time:下午6:49 * @param array $record * @return bool */ public function isHandling(array $record) { switch ($record['level']) { case Logger::DEBUG: return $record['level'] == $this->level; break; case $record['level'] == Logger::ERROR || $record['level'] == Logger::CRITICAL || $record['level'] == Logger::ALERT || $record['level'] == Logger::EMERGENCY: return Logger::ERROR <= $this->level && Logger::EMERGENCY >= $this->level; break; default: return Logger::INFO <= $this->level && Logger::WARNING >= $this->level; } } }
-
增加dblog的支援
namespace App\Core\Handler; use Monolog\Handler\StreamHandler; use Monolog\Logger; /** * LogFileHandler * 日誌處理,儲存檔案 * 將info、warning、notic等型別儲存一個檔案,debug型別儲存一個檔案,error型別儲存一個檔案 * @package App\Core\Handler * User:YM * Date:2019/11/29 * Time:下午6:39 */ class LogFileHandler extends StreamHandler { /** * handle * 改寫父類方法,增加判斷日誌輸出,框架日誌 * User:YM * Date:2019/12/21 * Time:下午7:16 * @param array $record * @return bool */ public function handle(array $record) { if (!$this->isHandling($record)) { return false; } $record = $this->processRecord($record); // 判斷系統允許日誌型別 if ( ! isStdoutLog($record['level_name']) ) { return false; } // 判斷是否處理框架日誌 if ( ! env('HF_LOG', false) && $record['channel'] == 'hyperf' ) { return false; } $record['formatted'] = $this->getFormatter()->format($record); $this->write($record); return false === $this->bubble; } /** * isHandling * 重寫該方法,作用改變日誌的儲存檔案的方式。 * 將debug,error,單獨儲存,其它的按著原來規則 * User:YM * Date:2019/11/29 * Time:下午6:49 * @param array $record * @return bool */ public function isHandling(array $record) { switch ($record['level']) { case Logger::DEBUG: return $record['level'] == $this->level; break; case $record['level'] == Logger::ERROR || $record['level'] == Logger::CRITICAL || $record['level'] == Logger::ALERT || $record['level'] == Logger::EMERGENCY: return Logger::ERROR <= $this->level && Logger::EMERGENCY >= $this->level; break; default: return Logger::INFO <= $this->level && Logger::WARNING >= $this->level; } } }
-
增加阿里雲slslog的支援,需要使用適配hyperf框架的sdk支援,點這裡!!!,需要注意:儲存欄位不能有空值,如果有自己處理一下0代替
namespace App\Core\Handler; use Hyperf\Di\Annotation\Inject; use Monolog\Handler\AbstractProcessingHandler; use Ym\AliyunSls\ClientInterface; /** * LogSlsHandler * 阿里雲sls日誌處理 * @package App\Core\LogHandler * User:YM * Date:2019/12/31 * Time:下午3:17 */ class LogSlsHandler extends AbstractProcessingHandler { /** * @Inject * @var ClientInterface */ protected $sls; /** * write * 記錄日誌 * User:YM * Date:2019/12/21 * Time:下午4:15 * @param array $record * @return bool|void */ public function write(array $record) { // 判斷系統允許日誌型別 if ( ! isStdoutLog($record['level_name']) ) { return false; } // 判斷是否處理框架日誌 if ( ! env('HF_LOG', false) && $record['channel'] == 'hyperf' ) { return false; } $saveData = $record['context']; $saveData['channel'] = $record['channel']; $saveData['message'] = is_array($record['message'])?json_encode($record['message']):$record['message']; $saveData['level_name'] = $record['level_name']; // 阿里雲日誌不能有空欄位 foreach ($saveData as &$v) { if (!$v) { $v = 0; } } unset($v); $this->sls->putLogs($saveData); } }
- hyperf框架預設日誌時列印到控制檯的使用(StdoutLoggerInterface),使用monolog需要使用(LoggerFactory),但是如果統一一下(然而我就這麼做了),遇到一些問題以及解決
- 給dependencies.php檔案增加
\Hyperf\Contract\StdoutLoggerInterface::class => \App\Core\HF\StdoutLoggerFactory::class
改變依賴,且預設channel為"hyperf",後期用這個做了一些事情,工廠類實現了什麼,可以看看hyperf官方文件,基本差不多 - db日誌實現的時候,由於框架核心輸出是
StdoutLoggerInterface
實現,而且已經被修改依賴,將不在控制檯列印而是通過日誌驅動,由於系統框架有資料庫監聽日誌,所以死迴圈了……,當然已經處理了,看日誌handler程式碼就好了 - 三個驅動handler已經對,config.php的
StdoutLoggerInterface::class
提供了適配
- 給dependencies.php檔案增加
本作品採用《CC 協議》,轉載必須註明作者和本文連結