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_hk185Slack 發送者顯示名稱slackName,或 Slack webhook/app 設定
channel: FPS程式 log channelnew LogHelper('FPS')
level: WARNINGlog 等級$logHelper->warning(...)
message: hsbclog 訊息$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 仍會送 Slackmonolog_cron
websocket Node.js log不會直接寫 monolog_YYYYMM
fatal error shutdown handlerdirect 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.jsonautoload.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.php

Docker 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同左
channelFPSFPS
levelWARNINGWARNING
messagehsbchsbc_info
DB tablemonolog_warningmonolog_warning

PM 快速讀法

如果 Slack 出現告警,先看三個欄位:

先看用途
發送者名稱,例如 new_api_hk185判斷是哪個環境送出來
channel判斷是哪個功能或流程
message判斷實際事件

常見 channel 判讀:

channel大致意思
FPSFPS / HSBC 付款通知相關
QUERY_ERRORSQL statement error
DB_modifyDB update/modify 發生 exception
GLOBALPHP error handler 捕捉到的錯誤
Exception_錯誤碼建立 PRO360Exception 時指定要送 Slack
cronjob_*排程工作
websocketNode.js websocket 直接寫 DB,不送 Slack

查 DB 時先用這張表:

Slack 或 log 狀況優先查
level: ERRORmonolog_error
level: WARNINGmonolog_warning
channel 含 cronjob_monolog_cron
channel 是 websocket當月 monolog_YYYYMM
fatal error direct Slack不一定有對應 DB log

工程師快速判斷流程

要判斷一段程式會不會送 Slack 或寫 Monolog table:

  1. new LogHelper(...)
  2. 看第二個參數有沒有關 handler。
  3. 看呼叫 info()warning() 還是 error()
  4. 看 channel 是否含 cronjob_
  5. 看 channel 是否含 new/v1/tap_pay/
  6. 如果在 websocket/ 目錄,改看 Node.js 是否直接 insert DB。

handler 開關範例:

new LogHelper('DB Connection', ['PDOHandler' => false])

這代表不寫 DB,但 Slack 還是開著。只要後面打 warning()error(),仍會送 Slack。

核心檔案

用途檔案
設定 slackName、webhook、log levelLib/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 handlerLib/Common/FatalErrorHandler.php
指定 exception 送 SlackLib/Exception/PRO360Exception.php
FPS HSBC callback logEndpoint/V1/PayBank.php::hsbc()
websocket 直接寫月表websocket/server.jswebsocket/server_v2.jswebsocket/server_infobip.js

舊專案 Slack 規則

來源專案:

/Users/mattsu/Documents/Site/get-lancer-php56

legacy CakePHP / PHP 5.6 也有 LogHelperPDOHandlerMySlackHandler,但和 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 handlerapp/Config/core.php

舊專案和新 API 的主要差異

項目legacy get-lancer-php56new API pro360_api_82
Slack usernameMySlackHandler 內寫死,例如 Getlancer-Local-php56slackName 常數
webhook 來源MySlackHandler 內依 channel switch 硬編碼slackWebhook / slackWebhook2 常數
Slack handler 掛載時機EnvFactory::getEnv() == production 時才掛LogHelper 預設掛 Slack handler
Slack 等級MySlackHandler(Logger::ERROR),一般只有 error() 送 SlackMySlackHandler(Logger::WARNING)warning() / error() 送 Slack
DB handlerPDOHandler(Logger::INFO)PDOHandler,受 CurrentLoggerLevel 與 handler 設定影響
fatal errorapp/Config/core.phpfatalErrorHandler() 透過 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_RatingOperationzoho 有特殊目標。
  • webhook URL 是寫在 legacy code 裡的敏感資訊,文件只記來源,不貼值。

舊專案 DB table 分流

legacy PDOHandler 依 Monolog level 寫不同 table:

leveltable
ERRORmonolog_error
WARNINGmonolog_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 畫面
usernameslackName訊息上方發送者名稱
textchannel/level/message 組成訊息內文

3. Slack webhook 選擇

預設使用:

slackWebhook

如果 channel 包含:

new/v1/tap_pay/

改用:

slackWebhook2

webhook 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
1channel 包含 cronjob_monolog_cron
2level 是 ERRORmonolog_error
3level 是 WARNINGmonolog_warning
4其他 level,例如 INFOmonolog_YYYYMM

雖然程式碼先判斷 level,再判斷 cronjob_,但 cronjob_ 會覆蓋前面的 table,所以實際優先度最高。

3. 寫入欄位

欄位內容
channelnew LogHelper($channel) 傳入的名稱
levelERRORWARNINGINFO
messagelog message
contextcontext array 經 json_encode(..., 320)
extra使用者 IP
datetimelog 發生時間,格式 Y-m-d H:i:s

extra 的 IP 取值順序:

  1. HTTP_X_FORWARDED_FOR
  2. REMOTE_ADDR
  3. HTTP_VIA

特殊規則

Fatal error direct Slack

Lib/Common/FatalErrorHandler.php 註冊 shutdown handler。以下錯誤類型會直接送 Slack,channel 是 Uncaught Exception

  • E_ERROR
  • E_CORE_ERROR
  • E_COMPILE_ERROR
  • E_USER_ERROR
  • E_RECOVERABLE_ERROR
  • E_CORE_WARNING
  • E_COMPILE_WARNING
  • E_USER_WARNING
  • E_DEPRECATED
  • E_PARSE

這條路徑直接呼叫 MySlackHandler->writeSelf(...),不透過 LogHelper,所以本身不寫 monolog_*

PHP warning/error handler

同檔案的 errorHandler() 會用 LogHelper("GLOBAL")

錯誤情境行為
E_USER_ERRORerror(...),送 Slack,寫 DB
一般 E_WARNING / E_USER_WARNINGerror(...),送 Slack,寫 DB
warning 訊息包含 exif_read_datainfo(...),不送 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.phpcronjob_cancel
Endpoint/V1/ThirdPartyPayments.phpcronjob_export_instruct
Endpoint/V1/ThirdPartyPayments.phpcronjob_credit_card_refund
Endpoint/V1/ThirdPartyPayments.phpcronjob_cs_survey
Endpoint/V1/NshopOrders.phpcronjob_order_refund

容易誤會的地方:

  • cronjob_ 會進 monolog_cron
  • cron_ 不會。

例如 cron_wait_for_payment_notify 不含 cronjob_,所以仍依 level 進 monolog_errormonolog_warningmonolog_YYYYMM

Websocket 規則

websocket 目錄下的 Node.js 程式不走 PHP LogHelper,而是直接 insert DB。

相關檔案:

  • websocket/server.js
  • websocket/server_v2.js
  • websocket/server_infobip.js

SQL 型態:

insert into monolog_YYYYMM(channel, level, message, context, extra, datetime)
value('websocket','INFO',?,?,?,now())

固定規則:

欄位或條件
table執行當月 monolog_YYYYMM
channelwebsocket
levelINFO
寫入條件is_wss_closed 為 false
insert 失敗console.log(error),不送 Slack

會記錄的事件:

檔案事件
server.jsconnection、close、message received、action processed、remove unused bid
server_v2.jsconnection、JWT/action processed、close、remove unused bid
server_infobip.jsconnection、message received、message broadcast finished

常見情境速查

情境SlackDB
一般功能打 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 errormonolog_error
websocket 連線或訊息不送monolog_YYYYMM
fatal errordirect 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