trd-rdm / stack-logger
A composite logger which can log multiple channels simultaneously.
Installs: 2 287
Dependents: 0
Suggesters: 0
Security: 0
Stars: 1
Watchers: 2
Forks: 0
Open Issues: 0
Type:package
Requires
- php: >=7.1.0|^8.0
- google/cloud: ^0.158
- nunomaduro/collision: >=2.0
README
Introduction
使用 Laravel 框架的 logging 與 facades 功能組成的複合型工具, 可以使用 facade 在程式中任何一處呼叫, 方便開發使用
Table of contents
1. 安裝步驟 [返回目錄]
(1) 執行安裝指令:
composer require trd-rdm/stack-logger --with-all-dependencies
(2) 註冊 Service Provider 以使用 Log Facade:
打開 config/app.php
在 providers 陣列底下添加 RDM\StackLogger\StackLoggerServiceProvider
註冊 facade
'providers' => [ /* * Package Service Providers... */ RDM\StackLogger\StackLoggerServiceProvider::class, ]
(3) 添加註冊多種 loggers 以支援 facade 功能:
打開 config/logging.php
添加以下 logger 到 channels 陣列中,
根據需求調整 stack_logger 中 channels 的內容, 但不建議添加 storage。
return [ 'channels' => [ /** * Log Facade * 主要 logger , 以 stack driver 結合多個 custom log 同時呼叫 * 加入 channels 的 driver 可以在 GCPLog 中呼叫 */ 'stack_logger' => [ 'driver' => 'stack', 'channels' => ['console', 'file'], // 指定多種 log 頻道, 可以自由添加移除 (可添加選項 : ['console', 'file', 'storage', 'stackdriver', 'telegram']) 'ignore_exceptions' => false, ], /** * 1. 螢幕輸出: 彩色字體 */ 'console' => [ 'driver' => 'monolog', 'level' => env('LOG_LEVEL', 'debug'), // The minimum logging level at which this handler will be triggered 'handler' => RDM\StackLogger\Handlers\ConsoleLogger::class, 'formatter' => env('LOG_STDERR_FORMATTER'), 'with' => [ ], ], /** * 2. 文件檔案 : 需要指定寫入檔案路徑 */ 'file' => [ 'driver' => 'monolog', 'level' => env('LOG_LEVEL', 'debug'), 'handler' => RDM\StackLogger\Handlers\FileLogger::class, 'formatter' => env('LOG_STDERR_FORMATTER'), 'with' => [ // handler constructor 額外參數 'default_path' => storage_path('default.log'), // 預設檔案路徑 'permission' => 0775, // The log file's permissions 'locking' => false, // Attempt to lock the log file before writing to it ], ], /** * 3. GCP Storage 儲存空間 : 需要指定本地上傳檔案路徑 與 storage 路徑 * * 結果請至 GCP Cloud Storage - Browser查看 * * @link https://console.cloud.google.com/storage/browser */ 'storage' => [ 'driver' => 'monolog', 'level' => env('LOG_LEVEL', 'debug'), 'handler' => RDM\StackLogger\Handlers\GcpStorageLogger::class, 'formatter' => env('LOG_STDERR_FORMATTER'), 'with' => [ 'gcp_project_id' => 'your-gcp-project', // GCP Project 'credential_path' => 'your-local-credential-file-path', // 憑證路徑 (專案根目錄為 root) 'bucket_id' => 'your-storage-bucket-id', // GCP Storage bucket 名稱 'bucket_folder' => 'bucket-folder1/folder2', // 此 logger 在 bucket id 底下的 root 路徑 'bucket_path' => '', // bucket_folder 底下的檔案路徑, 需要使用者用 setPath() 設置後才能寫入 'ignore_exceptions' => false, // 上傳失敗時不要拋出例外 ], ], /** * 4. GCP Stackdriver : 需要指定 log 名稱 * * 輸出結果請至 Google Console 查詢 Log * * @link https://console.cloud.google.com/logs */ 'stackdriver' => [ 'driver' => 'monolog', 'level' => env('LOG_LEVEL', 'debug'), 'handler' => RDM\StackLogger\Handlers\StackdriverLogger::class, 'formatter' => env('LOG_STDERR_FORMATTER'), 'with' => [ 'name' => 'your-stackdriver-logging-name', // log 名稱 (在 google console 查詢過濾條件 logName 的值) 'gcp_project_id' => 'rdm-bbos', 'credential_path' => '/rdm-bbos.json', // 憑證路徑 (專案根目錄為 root) 'batch_enabled' => true, // 允許批次送出多筆 log 紀錄, 避免每次送出產生高延遲 ], ], /** * 5. Telegram 通知 : 需要指定 接收者 chat_id 與 api_key 機器人群組 */ 'telegram' => [ 'driver' => 'monolog', 'level' => env('LOG_LEVEL', 'debug'), // The minimum logging level at which this handler will be triggered 'handler' => RDM\StackLogger\Handlers\TelegramLogger::class, 'formatter' => env('LOG_STDOUT_FORMATTER'), 'with' => [ 'chat_ids' => ['1378441015'], // 收信者 'disable_notification' => false, // 關閉通知提示音 'api_key' => (env('APP_ENV') === 'prod') ? env('TELEGRAM_BOT_TOKEN', 'xxxxx') // 正式 Bot : env('TELEGRAM_BOT_TOKEN', 'xxxxx'), // 測試 Bot ], ], ] ];
調整 stack_logger 的 channels 以選擇你需要的 channels, 並且針對下面的五種 log 設置參數。
這五種 log 都是 monolog log, 關於這些參數定義可以參考
Laravel Logging - Creating Monolog Handler Channel
2. Logger 各別介紹與 config 設置 [返回目錄]
打開 config/logging.php
中可以看到新添加入的 logger 底下將各別作介紹。
-
stack_logger
首先 stack_logger 即 GCPLog Facade 功能, 他是一種 Laravel Log stack 類型,
需要設置的參數有:
- channels: 添加或移除你需要的 log 名稱, 可以開啟或關閉指定 log 功能, 讓你在呼叫此 log 時可以一次執行多種 channels 中的 log, 而不用各別呼叫。
舉例來說若是 channel 中有包含 'console', 'file', 'storage', 'stackdriver', 'telegram'。
則呼叫以下這一行
Log::channel('stack_logger')->info('info message');
相當於執行以下這幾行
Log::channel('console')->info('info message'); Log::channel('file')->info('info message'); Log::channel('storage')->info('info message'); Log::channel('stackdriver')->info('info message'); Log::channel('telegram')->info('info message');
-
console
螢幕輸出, 即傳統意義上的 print, 只是依據 log 等級 會有不同的文字色彩, 依據訊息重要程度增加辨識度。
需要設置的參數有:
- level : 什麼等級(含)以上的 log 才需要被執行, 例如將 level 設置為 'info', 則 debug 等級的 log 不會印到螢幕上
-
file
寫到本地檔案。
需要設置的參數有:
- default_path: 指定預設寫入的 log 檔案路徑
-
storage
上傳檔案到 Google Cloud Storage, 使用此 log 的話每寫入一行 log 就會, 同步上傳 file logger 設置的檔案內容到你指定的 Storage Bucket 檔案之中。
* 請注意兩次呼叫 storage logger (上傳) 的時間間隔過短, 會使得 Google API 上傳拋出例外, 如果要忽略例外的話, 請將
ignore_exceptions
參數設成true
。需要設置的參數有:
- gcp_project_id :GCP 專案名稱
- credential_path: GCP API 憑證檔案, 請設置本地相對於專案根目錄的檔案路徑
- bucket_id: Storage Bucket 名稱
- bucket_folder: Storage 檔案在 bucket 底下的資料夾, 通常執行時不變動
- bucket_path: Storage 檔案在 bucket_folder 底下的路徑
- ignore_exceptions: 寫 log (上傳檔案) 失敗時不要拋出例外
-
stackdriver
寫入的 log 會透過 GCP API 傳送到 Google Stackdriver 儲存, 可以透過 GCP Log 記錄檔探索工具 查看, 查詢時的過濾條件就可以帶入 logName="your-stackdriver-logging-name", 以查到執行結果, 目前此 stackdriver log 紀錄是 30 日後會自動消失。
需要設置的參數有:
- gcp_project_id: GCP 專案名稱
- credential_path: GCP API 憑證檔案, 請設置本地相對於專案根目錄的檔案路徑
- name: log 名稱, 在 google console 查詢過濾條件 logName 的值
- [batch_enabled: 允許批次送出多筆 log 紀錄, 避免每次送出 API 產生高延遲, 影響程式效率
-
telegram
透過 telegram bot 通知傳送訊息給指定一位或多位收訊者。
需要設置的參數有:
- chat_ids: 收信者的 telegram user id
- disable_notification: 關閉通知提示音
- api_key: 註冊機器人時 Telegram 官方提供的 Telegram Bot API Token
3. Logger 使用方法 [返回目錄]
你可以使用整合進 stack_logger driver 的 GCPLog Facade (以下簡稱 GCPLog)
也可以使用 laravel 原生的 Log Facade (以下簡稱 Log) 執行 logging。
但是 Log 僅支援 PSR-3 標準的介面函式,
你沒有辦法在程式執行期動態修改 config/logging.php
中的參數,
以修改 logger 參數, 比如設置檔案路徑等,
執行期針對 config 的異動不會影響到已經建立靜態 (static) 實例的 Log。
而 GCPLog 提供一些介面讓你能在執行期設置參數, 以及整合部分 logging 以外的除錯函式方便程式人員開發。
以下為 GCPLog 支援的函式介紹
PHP PSR-3 介面, 所有 logger 共同支援 | 範例1 |
---|---|
static void emergency(string $message, array $context = []) |
系統不可用 |
static void alert(string $message, array $context = []) |
必須馬上採取行動 |
static void critical(string $message, array $context = []) |
緊急狀況 |
static void error(string $message, array $context = []) |
運行時出現的錯誤,不須要馬上採起行動,但必須記錄下來以備檢測 |
static void warning(string $message, array $context = []) |
出現非錯誤性的異常 |
static void notice(string $message, array $context = []) |
通常性重要的事件 |
static void info(string $message, array $context = []) |
重要事件 |
static void debug(string $message, array $context = []) |
debug 詳情 |
static void log($level, string $message, array $context = []) |
任意等級的日誌記錄, $level 僅限連結中任一值 |
其他相容 Laravel Console 介面 | |
---|---|
static void line(string $out, array $context = []) |
同 debug() |
static void comment(string $out, array $context = []) |
同 debug() |
static void warn(string $out, array $context = []) |
同 warning() |
static string ask(string $out) |
詢問問題並等待使用者輸入, 回傳使用者答案 |
本地 Log 檔案相關 - file logger | 範例2 |
---|---|
static self setLocalLogPath(string $path) |
設置 log 檔案路徑 |
static self setCustomLogPath(string $path) |
同 setLocalLogPath() |
static string getLocalLogPath() |
取得 log 檔案路徑 |
static self clearLogFile() |
清除 log 檔案內容 |
static self deleteLogFile() |
刪除 log 檔案 |
GCP Storage 檔案處理相關 - storage logger | |
---|---|
static self setStorageBucketFolder(string $path) |
設置 Bucket id 底下的跟資料夾路徑 |
static self setStorageBucketPath(string $path) |
設置 BucketFolder 底下的上傳物件路徑 |
static string|null getStorageObjectPath() |
取得 Storage 上傳物件的完整路徑 (bucket folder + bucket path) |
static string|null getStorageLogPath() |
同 getStorageObjectPath() |
static string|null getStoragePublicUrl() |
取得 Storage object 的公開網址 |
static self setStorageUploadFilePath(string $path) |
設置 Storage 上傳本地檔案的路徑 |
static string|null getStorageUploadFilePath() |
取得 Storage 被上傳本地檔案的路徑 |
GCP StackDriver 相關 - stackdriver logger | 範例2 |
---|---|
static self setStackDriverLogName(string $name) |
設置 logName 名稱, 作為 Google console 頁面查詢條件 |
static string|null getStackDriverLink() |
取得 Google 記錄檔探索工具 瀏覽頁面的 log 連結 |
Telegram 相關 - telegram logger | 範例3 |
---|---|
static self setTelegramChatIds(array $chat_ids) |
設置接收通知者,呼叫此函式會覆蓋 config/logging.php telegram.chat_ids 中的設置 |
static self addTelegramChatId(string $chat_id) |
添加一位通知者 (傳入 user id) |
static self removeTelegramChatId(string $chat_id) |
移除一位通知者 (傳入 user id) |
static self disableTelegramNotification(string $chat_id) |
關閉通知提示音 |
static self enableTelegramNotification(string $chat_id) |
開啟通知提示音 |
例外處理 | 範例1 |
---|---|
static void renderException(\Exception $e, $render_type = 'editor', $verbosity = true) |
渲染 exception 成漂亮可讀訊息 (human-readable message), $render_type 可以代入 editor, trace, default 三種參數, 會有不同印出格式 |
static void dumpExceptionPrettyPage(\Exception $e, string $export_html_path) |
匯出 exception 成一個 html 檔案, 需要指定匯出 html 的完整檔案路徑 |
static void failIf($condition, $error_message, $success_message = '', $throw = false) |
如果 condition 為假, log $error_message, 否則 log $success_message, 如果 $throw 為真會拋出例外 |
static void failUnless($condition, $error_message, $success_message = '', $throw = false) |
如果 condition 為真, log $error_message, 否則 log $success_message, 如果 $throw 為真會拋出例外 |
計時器功能 | 範例4, 範例5 |
---|---|
static void watch() |
開始計時, 在第一次呼叫 timing 前呼叫 |
static string timing(string $out, bool $return = false) |
印出附帶距離上次呼叫 timing 或 watch 的時間間隔, $return 為 true 表示只回傳字串而不會 logging |
static Timer createTimer() |
創立一個計時器 |
static Timer[] createTimers(int $count_of_timers = 1) |
創立多個計時器 |
MySQL Log 相關 | 範例6 |
---|---|
static self enableGeneralLog($connection = null) |
啟用 General log, 每一項查詢將會被記錄到表 mysql.general_log |
static self disableGeneralLog($connection = null) |
停用 General log |
static self clearGeneralLog($connection = null) |
清除 mysql.general_log |
static self enableSlowQueryLog($long_query_time = 1, $connection = null) |
啟用 Slow log, 每一項超過 $long_query_time 秒的查詢, 將會被記錄到表 mysql.slow_log |
static self disableSlowQueryLog($connection = null) |
停用 Slow log |
static self clearSlowQueryLog($connection = null) |
清除 mysql.slow_log |
其他 | 範例7 |
---|---|
static void destroy() |
Facade 摧毀自身實例, 可以用在當想要清除設定值的時候, 例如在 laravel queue 的 job 結尾呼叫, 避免設定值影響到下一個 job |
static mixed console() |
取得 console logger 實例 |
static mixed file() |
取得 file logger 實例 |
static mixed storage() |
取得 storage logger 實例 |
static mixed stackdriver() |
取得 stackdriver logger 實例 |
static mixed telegram() |
取得 telegram logger 實例 |
程式碼範例 [返回目錄]
-
Log 成功與錯誤訊息 跳至函式
use RDM\StackLogger\Facades\GCPLog; use \Psr\Log\LogLevel; // 成功訊息 GCPLog::info('success'); // 同上成功訊息 GCPLog::log(LogLevel::INFO, 'success', ['success' => true, 'result' => []]); try { // ... } catch (\Exception $exception) { // 錯誤訊息 GCPLog::error('something wrong'); // 印出漂亮格式的錯誤訊息 GCPLog::renderException($exception); // 匯出例外訊息成 HTML 檔 GCPLog:dumpExceptionPrettyPage($exception, storage_path('exception.html')); }
-
設置 Log 路徑 跳至函式
use RDM\StackLogger\Facades\GCPLog; // 設置路徑參數 GCPLog::setLocalLogPath(storage_path('logs/err.log')) // 設置 log 檔案參數 ->setStackDriverLogName('stack-driver-log-name'); // 設置 stackdriver log 名稱 GCPLog::info("message"); // 讀取路徑參數 fprintf("All messages are written at file %s", GCPLog::getLocalLogPath()); // 取得 log 檔案路徑 fprintf("All messages are export to stackdriver: %s", GCPLog::getStackDriverLink()); // 取得 stackdriver log 存取連結
-
telegram 跳至函式
use RDM\StackLogger\Facades\GCPLog; try { $john_user_id = '12345'; GCPLog::setTelegramChatIds([$john_user_id]) // 設置收訊者 ->disableTelegramNotification(); // 關閉通知聲響 GCPLog::info('<b>TITLE</b> message'); // 送出通知 (支援 html 格式,若要避免請使用 `htmlspecialchars()` 跳脫或是 <pre> 包覆) } catch (\Throwable $e) { // html tag 限制 https://core.telegram.org/api/entities#allowed-entities // 最大文字長度 : 4096 字元 dump($e); }
若是 config/logging.php 預設呼叫頻道 channels.stack_logger.channels 中沒有
telegram
則應使用 呼叫內部 logger 函式GCPLog::telegram()->info('<b>TITLE</b> message');
-
計時器 1 跳至函式
use RDM\StackLogger\Facades\GCPLog; GCPLog::watch(); // 開始計時 /* * Code Block 1 */ GCPLog::timing('執行 Code Block 1'); /* * Code Block 2 */ GCPLog::timing('執行 Code Block 2'); /** 範例輸出結果: 執行 Code Block 1 1.500 sec 執行 Code Block 2 1.500 sec */
-
計時器 2 跳至函式
use RDM\StackLogger\Facades\GCPLog; $timer = GCPLog::createTimer(); $timer->setDescription('執行 Code Block 1'); for($i = 5; $i > 0; $i--) { $timer->start(); sleep(1); $timer->stop(); sleep(2); } echo (string)$timer; /** 範例輸出結果: 執行 Code Block 1 花費 : 5.01 秒 */
-
SQL Log 跳至函式
use RDM\StackLogger\Facades\GCPLog; use Illuminate\Support\Facades\DB; $connection = 'mysql'; GCPLog::enableMysqlGeneralLog($connection); // 啟用 mysql general log DB::connection($connection)->table('users')->get(); // send query GCPLog::disableMysqlGeneralLog($connection); // 停用 mysql general log
-
呼叫內部 logger : 函式 config/logging.php 預設呼叫頻道 channels.stack_logger.channels 中沒有包含的頻道,可以用此方法單獨呼叫。 跳至函式
use RDM\StackLogger\Facades\GCPLog; use Illuminate\Support\Facades\DB; // 選取 GCPLog 中的特定 logger, 執行其內部函式 GCPLog::file()->setPath(storage_path('laravel.log')); GCPLog::console()->info('message'); GCPLog::telegram()->info('<b>TITLE</b> message');