Artisan 疑難排解
每一種 bridge 失敗都會以具型別的例外呈現。請將例外與訊息對照下方表格。每一列都指向確切的原始檢查,讓你修正成因,而不是症狀。
依例外診斷
標題為「依例外診斷」的區段ChromeNotAvailableException
標題為「ChromeNotAvailableException」的區段
chrome-php/chrome is not installed. Install it via: composer require chrome-php/chrome:^1.15
CDP 用戶端函式庫不在 autoloader 中。BrowserPool::getBrowser() 會在任何一次 Chrome 啟動之前就拋出這個例外。請執行安裝指令。這與缺少執行檔不同:函式庫檢查與執行檔檢查是兩個彼此獨立的階段。
ChromeRenderException — 啟動或逾時
標題為「ChromeRenderException — 啟動或逾時」的區段
Chrome renderer failed: <cause>
Chrome 無法啟動、逾時或當機。前一個例外會保留原始成因。常見成因:
- **找不到執行檔/無法執行。**請以
chromium --headless --dump-dom about:blank驗證。將chrome_binary設為絕對路徑。 - **Sandbox 無法初始化(容器環境)。**成因會提到 sandbox 或 namespace。佈建一個支援 sandbox 的容器(建議做法),或閱讀 /integrations/artisan/security-and-operations/. 一節後設定
no_sandbox: true。 - **逾時。**沉重的文件超過了
render_timeout。請為該工作負載調高逾時,或縮減文件;在面向使用者的路徑上,必須權衡 denial-of-service 的取捨。 - **缺少共用函式庫。**Chrome 會立即結束。請安裝該發行版的 Chrome 相依套件集。
ChromeRenderException — 空白輸出
標題為「ChromeRenderException — 空白輸出」的區段
Chrome printToPDF returned empty data
Chrome 已啟動,但 printToPDF 回傳了零位元組。常見成因是輸入未產生任何可繪製的方塊(body 為空、所有內容皆為 display:none),或 Chrome 在列印途中當機。請確認該 HTML 會繪製出可見方塊,並檢查主機的記憶體壓力。
RuntimeException — 在 Chrome 之前就拒絕輸入
標題為「RuntimeException — 在 Chrome 之前就拒絕輸入」的區段| 訊息包含 | 成因 | 修正方式 |
|---|---|---|
exceeds maximum allowed size | HTML 超過 maxHtmlSize | 縮減輸入,或調高 max_html_size(會擴大資源耗盡的攻擊面) |
oversized base64 data URI | 內嵌的 base64 ≥ 13 MB | 縮小內嵌資產;改為引用較小的影像 |
forbidden meta refresh redirect | <meta http-equiv="refresh"> 存在 | 移除該標籤;這是 SSRF 導向的導覽向量,一律會被拒絕 |
這些都來自 ChromeSecurityPolicy::validate(),並且會在接觸 Chrome 之前觸發,所以測試時可以快速且低成本地命中。
PdfParseException
標題為「PdfParseException」的區段
Page <n> has no content stream
Chrome 產生的 PDF 讓 parser 無法從中取出頁面。這種情況很罕見,通常代表 Chrome 輸出有缺陷,或某個 Chrome 版本產生了非預期的結構。請擷取 Chrome 版本與該輸入,並確認該執行檔是受支援的 Chrome/Chromium 組建。
遠端資產繪製出空白
標題為「遠端資產繪製出空白」的區段這不是 bug。bridge 會封鎖每一次子資源擷取(CSP default-src 'none' 再加上一道 CDP setBlockedURLs('*') 封鎖)。遠端的 <img>、樣式表、字型、指令碼與 iframe 都不會載入。請將資產以 data: URI 內嵌,並透過 defaultCss 或 <style> 內嵌 CSS。請參閱 /integrations/artisan/security-and-operations/. 一節中的網路模型。
內容在底部被裁切
標題為「內容在底部被裁切」的區段文件溢出到 Chrome 的第二頁,而 bridge 只匯入第 0 頁。原因不是自動調整高度的緩衝對異常高的回流而言太小,就是明確指定的高度太小。請提供依內容大小設定的明確高度,或移除明確高度,改用帶有安全緩衝的自動調整高度。請參閱 /integrations/artisan/production-usage/. 一節中的高度處理。
約每 100 次繪製出現一次延遲尖峰
標題為「約每 100 次繪製出現一次延遲尖峰」的區段這是預期行為。BrowserPool 每 100 次繪製就會重啟 Chrome 一次,以限制記憶體用量。一筆 notice 等級的日誌會記錄這次重啟及繪製次數。請在 SLO 中將它視為已知的週期性成本,而不是異常事件。重啟頻率高於每 100 次繪製一次,代表文件比預期更沉重。
記憶體在長批次處理中逐漸增長
標題為「記憶體在長批次處理中逐漸增長」的區段BrowserPool 透過每 100 次繪製重啟一次來限制增長,但存活時間很長的 worker 仍可能累積記憶體。請在大批次之間呼叫 close() 以提早回收 Chrome 行程,並在主機的記憶體限制內執行該 worker。
診斷檢查清單
標題為「診斷檢查清單」的區段- 用一段最小的可信 HTML 片段重現問題,以便隔離輸入與環境。
- 以 worker 使用者的身分,在主機上執行
chromium --headless --dump-dom about:blank。 - 注入 PSR-3 logger,並讀取
info/notice等級的日誌(執行檔路徑、重啟次數)。 - 確認
chrome-php/chrome已安裝:若BrowserPool::getBrowser()不會拋出ChromeNotAvailableException,即代表已安裝。 - 檢查該例外的前一個例外,找出底層的 Chrome 成因。
另請參閱
標題為「另請參閱」的區段- 安裝指南:/integrations/artisan/install/
- 組態設定:/integrations/artisan/configuration/
- 安全性與維運:/integrations/artisan/security-and-operations/
- Chrome 繪製器設定:/integrations/artisan/chrome-renderer-setup/
- 生產環境使用:/integrations/artisan/production-usage/