/** * 共用 logger:統一輸出格式為 [yyyy/MM/dd HH:mm:ss][階段][等級]: 訊息 * 無階段時為 [yyyy/MM/dd HH:mm:ss][等級]: 訊息;時間為台灣時區(Asia/Taipei)。 */ const TAIPEI_FORMATTER = new Intl.DateTimeFormat('en-US', { timeZone: 'Asia/Taipei', year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit', second: '2-digit', hour12: false, }); /** * 取得目前台灣時區時間字串(yyyy/MM/dd HH:mm:ss); * 以 Intl.DateTimeFormat 的 formatToParts 組字串,不受系統時區影響。 * @returns {string} 台灣時區(Asia/Taipei)的當下時間,固定 yyyy/MM/dd HH:mm:ss 格式 */ function timestamp() { const parts = {}; for (const { type, value } of TAIPEI_FORMATTER.formatToParts(new Date())) { parts[type] = value; } // hour12: false 在部分環境會把 00 顯示成 24,統一正規化 const hour = parts.hour === '24' ? '00' : parts.hour; return `${parts.year}/${parts.month}/${parts.day} ${hour}:${parts.minute}:${parts.second}`; } /** * 組合訊息前綴並輸出一行訊息。 * @param {string} level 等級(INF/WRN/ERR/TRC/DBG) * @param {string} message 訊息內容 * @param {string} [stage] 階段名稱;未提供時省略 [階段] 區塊 */ function write(level, message, stage) { const prefix = stage ? `[${timestamp()}][${stage}][${level}]` : `[${timestamp()}][${level}]`; const line = `${prefix}: ${message}`; if (level === 'ERR') { console.error(line); } else { console.log(line); } } module.exports = { /** * 輸出 INF(一般資訊)等級訊息至 stdout。 * @param {string} message 訊息內容 * @param {string} [stage] 階段名稱;未提供時省略 [階段] 區塊 */ inf: (message, stage) => write('INF', message, stage), /** * 輸出 WRN(警告)等級訊息至 stdout,用於可繼續執行的異常狀況。 * @param {string} message 訊息內容 * @param {string} [stage] 階段名稱;未提供時省略 [階段] 區塊 */ wrn: (message, stage) => write('WRN', message, stage), /** * 輸出 ERR(錯誤)等級訊息至 stderr(console.error)。 * @param {string} message 訊息內容 * @param {string} [stage] 階段名稱;未提供時省略 [階段] 區塊 */ err: (message, stage) => write('ERR', message, stage), /** * 輸出 TRC(細部追蹤)等級訊息至 stdout,用於記錄 API 呼叫等細節。 * @param {string} message 訊息內容 * @param {string} [stage] 階段名稱;未提供時省略 [階段] 區塊 */ trc: (message, stage) => write('TRC', message, stage), /** * 輸出 DBG(除錯)等級訊息至 stdout,用於開發除錯資訊。 * @param {string} message 訊息內容 * @param {string} [stage] 階段名稱;未提供時省略 [階段] 區塊 */ dbg: (message, stage) => write('DBG', message, stage), };