Better-express-error
在 express 開發時,如果遇到錯誤,通常是直接印在 error page 上,或者在 production 端直接導到 404, 500 page。
這雖然沒有什麼值得一提的是,不過說真的,看到這種頁面,你會覺得開心嗎?
熟悉 Ruby On Rails 開發的工程師,應該多少都用過 better_errors,或者 rails 本身內建的 error trace page 來 debug。
不過在 express 上,還沒有看到一個功能類似 better error 的 package,常常只能看這種醜不拉嘰的 error message 仰天長嘯。
於是,自己做了一個簡單的 middleware 處理這件事。其實本質上就是 bettor errors 的 express 實作啦
分析錯誤訊息
TypeError: range out of bound. Please check http://kjj6198.github.io for more information.
at app.get (/Users/kalan/code/express-error/server/app.js:17:9)
at Layer.handle [as handle_request] (/Users/kalan/code/express-error/node_modules/express/lib/router/layer.js:95:5)
at next (/Users/kalan/code/express-error/node_modules/express/lib/router/route.js:137:13)
at Route.dispatch (/Users/kalan/code/express-error/node_modules/express/lib/router/route.js:112:3)
at Layer.handle [as handle_request] (/Users/kalan/code/express-error/node_modules/express/lib/router/layer.js:95:5)
at /Users/kalan/code/express-error/node_modules/express/lib/router/index.js:281:22
at Function.process_params (/Users/kalan/code/express-error/node_modules/express/lib/router/index.js:335:12)
at next (/Users/kalan/code/express-error/node_modules/express/lib/router/index.js:275:10)
at jsonParser (/Users/kalan/code/express-error/node_modules/body-parser/lib/types/json.js:109:7)
at Layer.handle [as handle_request] (/Users/kalan/code/express-error/node_modules/express/lib/router/layer.js:95:5)
仔細觀察後可以發現,錯誤訊息的格式是相當整齊規律的。首先,第一行為錯誤名稱與訊息,這通常是最重要的資訊。第二行以後則是調用 stack。at … 是某個函數的呼叫,括號內則是檔名與行數、row 的訊息。
簡單分析完錯誤訊息後,我們可以把純文字轉為比較有用的資訊。因此透過 split('\n')
,再根據字串做簡單的正規表達式匹配,就能分成檔名與行數資訊了。
顯示 Error
對於第一行的錯誤訊息,通常是最重要的資訊,因為程式碼就是在那邊噴錯的,因此對於第一行錯誤訊息,我們將他放在最顯眼的地方並且 highlight。
而第二行之後的錯誤訊息,用顏色及大小來標示檔案名稱、行數以及調用的函式名稱。
比起前面一坨黑黑的文字,這樣簡單的整理已經可以讓開發者可以一目了然到底發生了什麼事。
顯示檔案內容
不過,除了顯示錯誤訊息之外,我們也希望能夠同時顯示對應的檔案內容,以及上下文有哪些。因此右半邊可以利用錯誤訊息中提供的檔案名稱與行數,顯示對應的檔案內容。
對於 nodejs 來說,只要透過 fs.readFileSync
就夠了。
function(filename, line, row) {
const content = fs.readFileSync(filename);
content.toString()
.split('\n')
.slice(line - 5, line + 5)
.map(content => `<span>${content}</span>`)
.join('\n');
}
這邊是透過很簡單的方式,直接印出前五行及後五行,更聰明的方式是透過像是 AST 之類的技巧,只印出對應到的 function 內容。不過現在我們直接印出前後五行的程式碼即可。
做一些調整與修改,大概長這樣:
透過簡單的 highlight,讓開發者馬上知道出錯的地方在哪邊。
REPL
除了顯示 error 外,我們還希望這個頁面可以輸入一些簡單的程式,確認問題點後,再實際修改程式碼。
在 nodejs 當中,有個 VM 的模組,能夠讓你使用 V8 的 Virtual Machine contexts 執行給定的 code。透過這個模組,我們就能夠實現類似 REPL 的功能了!
const debugContext = vm.createContext({
request: req,
response: res,
util: require("util"),
Buffer: require("buffer").Buffer,
stream: require("stream"),
console: {
log: util.format,
},
clear: "",
})
把想要暴露的變數傳入 context 當中,在透過 POST 的方式讀取前端的 code,就可以很方便地達到 debug 的效果。
(以上還需要做一些調整)
全部整合!
全部整合起來後,頁面大概會長這樣。
比起原本的純文字,雖然花了一些功調整頁面 style 與實現 REPL 的功能,不過讓 debug 的流程變得更加流暢了。
結論
詳細的實作在這個 repo 裏頭,最近如果比較有空會在將實作抽換為 middleware 的形式方便大家使用。也會逐漸優化整個 layout 與 code highlight 的部分,並且讓整個流程跟畫面更加流暢一些。雖然不知道會過多久就是了 XD
如果有各種建議也歡迎提出 issue。