Slack 與 Monolog 寫入規則
來源專案:
/Users/mattsu/Documents/Site/pro360_api_82
/Users/mattsu/Documents/Site/get-lancer-php56掃描日期:2026-05-05
先看結論:new API
看到 Slack 這種訊息時:
new_api_hk185 [晚上 11:48]
channel: FPS
level: WARNING
message: hsbc可以先這樣拆:
| 畫面內容 | 意思 | 由哪裡決定 |
|---|---|---|
new_api_hk185 | Slack 發送者顯示名稱 | slackName,或 Slack webhook/app 設定 |
channel: FPS | 程式 log channel | new LogHelper('FPS') |
level: WARNING | log 等級 | $logHelper->warning(...) |
message: hsbc | log 訊息 | $logHelper->warning('hsbc', ...) |
new API 最重要的規則:
| 程式怎麼寫 | 會不會送 Slack | 通常寫到哪張 DB table |
|---|---|---|
$log->info(...) | 不會 | monolog_YYYYMM,例如 monolog_202605 |
$log->warning(...) | 會 | monolog_warning |
$log->error(...) | 會 | monolog_error |
channel 含 cronjob_ | warning/error 仍會送 Slack | monolog_cron |
| websocket Node.js log | 不會 | 直接寫 monolog_YYYYMM |
| fatal error shutdown handler | 會 | direct Slack,本身不寫 monolog_* |
補充:info() 是否真的寫 DB,還要看 CurrentLoggerLevel。目前本 repo 設定是 Monolog\Logger::INFO。
legacy CakePHP / PHP 5.6 的 Slack 規則不同:production 才掛 Slack handler,且一般 handler level 是 ERROR。不要把 new API 的 warning() 會送 Slack 規則直接套到舊專案。
new_api_hk185 是哪裡決定的
Slack 發送者顯示名稱不是 channel。它主要由 slackName 決定。
發送 Slack 的程式在:
Lib/Common/MySlackHandler.php關鍵程式:
$payload = ['username' => slackName, 'text' => $msg];所以 Slack 畫面上方的 new_api_hk185,通常就是該執行環境裡的 slackName。
本 repo 目前看到的設定
autoload 會載入:
Lib/Common/Configuration.php這是由 composer.json 的 autoload.files 指定:
"files": ["Lib/Common/Configuration.php", "Lib/Common/FatalErrorHandler.php"]目前本機 repo 的 Lib/Common/Configuration.php 有:
define("CurrentLoggerLevel", Monolog\Logger::INFO);
define("slackName", "new_api_local1-1");
define("slackWebhook", "..."); // webhook URL,文件不展開
define("slackWebhook2", "..."); // webhook URL,文件不展開另外 Docker staging build 會把:
docker/staging/Configuration.php複製成:
/project-data/Lib/Common/Configuration.phpDocker staging 版本裡的 slackName 是:
define("slackName", "new_api_local1");為什麼 Slack 看到的是 new_api_hk185
new_api_hk185 沒有出現在目前掃描到的 pro360_api_82 repo 檔案裡。
因此比較合理的判斷是:
- 實際發送 Slack 的 HK 環境有另一份
Configuration.php。 - 或部署時用其他方式替換了
Lib/Common/Configuration.php。 - 或 Slack incoming webhook/app 設定覆蓋了 payload username。
也就是說:本機 repo 看到 new_api_local1-1,Docker staging 看到 new_api_local1,但你貼的 Slack 來自 new_api_hk185 這個執行環境。
FPS / hsbc / hsbc_info 來源
你貼的訊息:
channel: FPS
level: WARNING
message: hsbc
channel: FPS
level: WARNING
message: hsbc_info來源在:
Endpoint/V1/PayBank.php::hsbc()關鍵程式:
$logHelper = new LogHelper('FPS');
$body = file_get_contents('php://input');
$logHelper->warning('hsbc', [$body, $_POST, $_GET]);
$fpsUtil = new FpsUtil();
$info = $fpsUtil->decrypt_jwe($body);
$logHelper->warning('hsbc_info', [$info]);所以它會產生兩筆 Slack:
| Slack 欄位 | 第一筆 | 第二筆 |
|---|---|---|
| 發送者名稱 | 由 slackName 決定,例如 new_api_hk185 | 同左 |
| channel | FPS | FPS |
| level | WARNING | WARNING |
| message | hsbc | hsbc_info |
| DB table | monolog_warning | monolog_warning |
PM 快速讀法
如果 Slack 出現告警,先看三個欄位:
| 先看 | 用途 |
|---|---|
發送者名稱,例如 new_api_hk185 | 判斷是哪個環境送出來 |
channel | 判斷是哪個功能或流程 |
message | 判斷實際事件 |
常見 channel 判讀:
| channel | 大致意思 |
|---|---|
FPS | FPS / HSBC 付款通知相關 |
QUERY_ERROR | SQL statement error |
DB_modify | DB update/modify 發生 exception |
GLOBAL | PHP error handler 捕捉到的錯誤 |
Exception_錯誤碼 | 建立 PRO360Exception 時指定要送 Slack |
cronjob_* | 排程工作 |
websocket | Node.js websocket 直接寫 DB,不送 Slack |
查 DB 時先用這張表:
| Slack 或 log 狀況 | 優先查 |
|---|---|
level: ERROR | monolog_error |
level: WARNING | monolog_warning |
channel 含 cronjob_ | monolog_cron |
channel 是 websocket | 當月 monolog_YYYYMM |
| fatal error direct Slack | 不一定有對應 DB log |
工程師快速判斷流程
要判斷一段程式會不會送 Slack 或寫 Monolog table:
- 找
new LogHelper(...)。 - 看第二個參數有沒有關 handler。
- 看呼叫
info()、warning()還是error()。 - 看 channel 是否含
cronjob_。 - 看 channel 是否含
new/v1/tap_pay/。 - 如果在
websocket/目錄,改看 Node.js 是否直接 insert DB。
handler 開關範例:
new LogHelper('DB Connection', ['PDOHandler' => false])這代表不寫 DB,但 Slack 還是開著。只要後面打 warning() 或 error(),仍會送 Slack。
核心檔案
| 用途 | 檔案 |
|---|---|
設定 slackName、webhook、log level | Lib/Common/Configuration.php |
| Docker staging 會覆蓋的設定檔 | docker/staging/Configuration.php |
| Composer autoload 載入設定 | composer.json |
| 統一 log 入口 | Lib/Common/LogHelper.php |
| Slack 發送實作 | Lib/Common/MySlackHandler.php |
| DB 寫入與 table 分流 | Lib/Common/PDOHandler.php |
| fatal error shutdown handler | Lib/Common/FatalErrorHandler.php |
| 指定 exception 送 Slack | Lib/Exception/PRO360Exception.php |
| FPS HSBC callback log | Endpoint/V1/PayBank.php::hsbc() |
| websocket 直接寫月表 | websocket/server.js、websocket/server_v2.js、websocket/server_infobip.js |
舊專案 Slack 規則
來源專案:
/Users/mattsu/Documents/Site/get-lancer-php56legacy CakePHP / PHP 5.6 也有 LogHelper、PDOHandler、MySlackHandler,但和 pro360_api_82 的設定方式不同。
核心檔案:
| 用途 | 檔案 |
|---|---|
| 統一 log 入口 | app/Lib/LogHelper.php |
| Slack 發送實作 | app/Lib/MySlackHandler.php |
| DB 寫入與 table 分流 | app/Lib/PDOHandler.php |
| 環境判斷 | app/Lib/EnvFactory.php |
| fatal error shutdown handler | app/Config/core.php |
舊專案和新 API 的主要差異
| 項目 | legacy get-lancer-php56 | new API pro360_api_82 |
|---|---|---|
| Slack username | MySlackHandler 內寫死,例如 Getlancer-Local-php56 | slackName 常數 |
| webhook 來源 | MySlackHandler 內依 channel switch 硬編碼 | slackWebhook / slackWebhook2 常數 |
| Slack handler 掛載時機 | EnvFactory::getEnv() == production 時才掛 | LogHelper 預設掛 Slack handler |
| Slack 等級 | MySlackHandler(Logger::ERROR),一般只有 error() 送 Slack | MySlackHandler(Logger::WARNING),warning() / error() 送 Slack |
| DB handler | PDOHandler(Logger::INFO) | PDOHandler,受 CurrentLoggerLevel 與 handler 設定影響 |
| fatal error | app/Config/core.php 的 fatalErrorHandler() 透過 LogHelper("Uncaught Exception")->error(...) | Lib/Common/FatalErrorHandler.php 可 direct Slack |
舊專案 LogHelper 主線:
new LogHelper($channel)
-> Monolog Logger($channel)
-> push PDOHandler(INFO)
-> production 時 push MySlackHandler(ERROR)因此判讀 legacy Slack 時要注意:
warning()通常會寫monolog_warning,但不一定送 Slack。error()在 production 會寫monolog_error並送 Slack。- 非 production 環境若沒有掛
MySlackHandler,就算呼叫error()也可能只寫 DB。 MySlackHandler會依 channel 切換 webhook,例如QuoteBids_Rating、Operation、zoho有特殊目標。- webhook URL 是寫在 legacy code 裡的敏感資訊,文件只記來源,不貼值。
舊專案 DB table 分流
legacy PDOHandler 依 Monolog level 寫不同 table:
| level | table |
|---|---|
ERROR | monolog_error |
WARNING | monolog_warning |
| 其他 | monolog_YYYYMM |
TimeCostUtil 另有直接寫入:
monolog_cron舊專案 fatal error
legacy app/Config/core.php 註冊:
register_shutdown_function("fatalErrorHandler")遇到 fatal 類錯誤時會:
LogHelper("Uncaught Exception")->error(...)所以 production 上可能同時看到:
channel: Uncaught Exception
level: ERROR並寫入 monolog_error。
Slack 規則
1. LogHelper 預設會掛 Slack handler
LogHelper 預設設定:
[
'PDOHandler' => true,
'FileLogHandler' => false,
'SlackHandler' => true,
]建立 LogHelper 時,會掛上:
new MySlackHandler(Logger::WARNING)因此:
| 呼叫 | Slack |
|---|---|
info() | 不送 |
warning() | 送 |
error() | 送 |
2. Slack 發送內容
MySlackHandler 會把 Monolog record 組成文字:
channel: {channel}
level: {level}
message: {message}然後用 payload 送出:
$payload = ['username' => slackName, 'text' => $msg];其中:
| payload 欄位 | 來源 | Slack 畫面 |
|---|---|---|
username | slackName | 訊息上方發送者名稱 |
text | channel/level/message 組成 | 訊息內文 |
3. Slack webhook 選擇
預設使用:
slackWebhook如果 channel 包含:
new/v1/tap_pay/改用:
slackWebhook2webhook URL 定義在 Configuration.php,文件不展開 URL,避免把 webhook secret 寫進文件。
Monolog DB 規則
1. 寫 DB 的入口
LogHelper 預設會掛:
new PDOHandler(CurrentLoggerLevel)目前 Lib/Common/Configuration.php 設定:
define("CurrentLoggerLevel", Monolog\Logger::INFO);代表 INFO 以上的 log 都會進 DB handler。
2. Table 分流
PDOHandler 實際 table 規則:
| 優先順序 | 條件 | 寫入 table |
|---|---|---|
| 1 | channel 包含 cronjob_ | monolog_cron |
| 2 | level 是 ERROR | monolog_error |
| 3 | level 是 WARNING | monolog_warning |
| 4 | 其他 level,例如 INFO | monolog_YYYYMM |
雖然程式碼先判斷 level,再判斷 cronjob_,但 cronjob_ 會覆蓋前面的 table,所以實際優先度最高。
3. 寫入欄位
| 欄位 | 內容 |
|---|---|
channel | new LogHelper($channel) 傳入的名稱 |
level | ERROR、WARNING、INFO 等 |
message | log message |
context | context array 經 json_encode(..., 320) |
extra | 使用者 IP |
datetime | log 發生時間,格式 Y-m-d H:i:s |
extra 的 IP 取值順序:
HTTP_X_FORWARDED_FORREMOTE_ADDRHTTP_VIA
特殊規則
Fatal error direct Slack
Lib/Common/FatalErrorHandler.php 註冊 shutdown handler。以下錯誤類型會直接送 Slack,channel 是 Uncaught Exception:
E_ERRORE_CORE_ERRORE_COMPILE_ERRORE_USER_ERRORE_RECOVERABLE_ERRORE_CORE_WARNINGE_COMPILE_WARNINGE_USER_WARNINGE_DEPRECATEDE_PARSE
這條路徑直接呼叫 MySlackHandler->writeSelf(...),不透過 LogHelper,所以本身不寫 monolog_*。
PHP warning/error handler
同檔案的 errorHandler() 會用 LogHelper("GLOBAL"):
| 錯誤情境 | 行為 |
|---|---|
E_USER_ERROR | error(...),送 Slack,寫 DB |
一般 E_WARNING / E_USER_WARNING | error(...),送 Slack,寫 DB |
warning 訊息包含 exif_read_data | info(...),不送 Slack |
PRO360Exception 指定送 Slack
PRO360\Exception\PRO360Exception 建構時,如果參數包含:
PRO360Exception::P_IS_SLACK => true或:
'is_slack' => true會執行:
new LogHelper('Exception_' . $code);
$logHelper->warning($message, $args);結果:
- 會送 Slack。
- 會寫
monolog_warning。
DB connection error
Lib/Common/DBConfiguration.php::getConnection() 使用:
new LogHelper("DB Connection", ['PDOHandler' => false])結果:
- 會送 Slack。
- 不寫
monolog_*。
原因是只關閉 PDOHandler,沒有關閉 SlackHandler。
SQL statement error
Lib/Common/DBConfigurationArray.php::onError() 在 SQL statement error 且 $isThrowException 為 true 時:
new LogHelper('QUERY_ERROR');
$log->error($query, $stmt->errorInfo());結果:
- 會送 Slack。
- 會寫
monolog_error。
DB modify exception
Lib/Common/DBConfiguration.php::modify() catch PDOException 時:
new LogHelper("DB_modify");
$log->error(...);結果:
- 會送 Slack。
- 會寫
monolog_error。
Cronjob 規則
只要 channel 字串包含 cronjob_,DB 會寫 monolog_cron。
明確例子:
| 檔案 | channel |
|---|---|
Endpoint/V1/ThirdPartyPayments.php | cronjob_cancel |
Endpoint/V1/ThirdPartyPayments.php | cronjob_export_instruct |
Endpoint/V1/ThirdPartyPayments.php | cronjob_credit_card_refund |
Endpoint/V1/ThirdPartyPayments.php | cronjob_cs_survey |
Endpoint/V1/NshopOrders.php | cronjob_order_refund |
容易誤會的地方:
cronjob_會進monolog_cron。cron_不會。
例如 cron_wait_for_payment_notify 不含 cronjob_,所以仍依 level 進 monolog_error、monolog_warning 或 monolog_YYYYMM。
Websocket 規則
websocket 目錄下的 Node.js 程式不走 PHP LogHelper,而是直接 insert DB。
相關檔案:
websocket/server.jswebsocket/server_v2.jswebsocket/server_infobip.js
SQL 型態:
insert into monolog_YYYYMM(channel, level, message, context, extra, datetime)
value('websocket','INFO',?,?,?,now())固定規則:
| 欄位或條件 | 值 |
|---|---|
| table | 執行當月 monolog_YYYYMM |
| channel | websocket |
| level | INFO |
| 寫入條件 | is_wss_closed 為 false |
| insert 失敗 | 只 console.log(error),不送 Slack |
會記錄的事件:
| 檔案 | 事件 |
|---|---|
server.js | connection、close、message received、action processed、remove unused bid |
server_v2.js | connection、JWT/action processed、close、remove unused bid |
server_infobip.js | connection、message received、message broadcast finished |
常見情境速查
| 情境 | Slack | DB |
|---|---|---|
一般功能打 info() | 不送 | monolog_YYYYMM,若達 CurrentLoggerLevel |
一般功能打 warning() | 送 | monolog_warning |
一般功能打 error() | 送 | monolog_error |
channel 是 cronjob_* | warning/error 送 | monolog_cron |
channel 是 cron_* | warning/error 送 | 依 level 分流,不一定是 monolog_cron |
| DB connection 失敗 | 送 | 不寫 |
| SQL statement error | 送 | monolog_error |
| websocket 連線或訊息 | 不送 | monolog_YYYYMM |
| fatal error | 送 | direct Slack 本身不寫 |
查證指令摘要
本文件主要依以下搜尋結果整理:
rg -n "define\\(\"slackName\"|define\\(\"slackWebhook\"|define\\(\"slackWebhook2\"|define\\(\"CurrentLoggerLevel\"" /Users/mattsu/Documents/Site/pro360_api_82/Lib/Common/Configuration.php
rg -n "Configuration\\.php|Common/Configuration|require.*Configuration|include.*Configuration" /Users/mattsu/Documents/Site/pro360_api_82
rg -n "MySlackHandler|payload|username|slackName" /Users/mattsu/Documents/Site/pro360_api_82/Lib/Common/MySlackHandler.php
rg -n "LogHelper\\('FPS'|LogHelper\\(\"FPS\"|hsbc|hsbc_info|FPS" /Users/mattsu/Documents/Site/pro360_api_82/Endpoint /Users/mattsu/Documents/Site/pro360_api_82/Lib
rg -n -i "monolog|monolog_|Monolog|Logger\\(" /Users/mattsu/Documents/Site/pro360_api_82
rg -n "cronjob_" /Users/mattsu/Documents/Site/pro360_api_82/Endpoint /Users/mattsu/Documents/Site/pro360_api_82/Lib /Users/mattsu/Documents/Site/pro360_api_82/CLI
rg -n "getInsertQuery|pool\\.query|query\\(" /Users/mattsu/Documents/Site/pro360_api_82/websocket/server.js /Users/mattsu/Documents/Site/pro360_api_82/websocket/server_v2.js /Users/mattsu/Documents/Site/pro360_api_82/websocket/server_infobip.js