半熟前端

軟體工程師 / 台灣人 / 在前端的路上一邊探索其他領域的可能性

nodejs

Better Express error

Better-express-error

在 express 開發時,如果遇到錯誤,通常是直接印在 error page 上,或者在 production 端直接導到 404, 500 page。

這雖然沒有什麼值得一提的是,不過說真的,看到這種頁面,你會覺得開心嗎?

default error

熟悉 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。

better error(1)

而第二行之後的錯誤訊息,用顏色及大小來標示檔案名稱、行數以及調用的函式名稱。

better error(2)

比起前面一坨黑黑的文字,這樣簡單的整理已經可以讓開發者可以一目了然到底發生了什麼事。

顯示檔案內容

不過,除了顯示錯誤訊息之外,我們也希望能夠同時顯示對應的檔案內容,以及上下文有哪些。因此右半邊可以利用錯誤訊息中提供的檔案名稱與行數,顯示對應的檔案內容。

對於 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 內容。不過現在我們直接印出前後五行的程式碼即可。

做一些調整與修改,大概長這樣:

better error(3)

透過簡單的 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 的效果。

better error(4)

(以上還需要做一些調整)

全部整合!

better error(5)

全部整合起來後,頁面大概會長這樣。

比起原本的純文字,雖然花了一些功調整頁面 style 與實現 REPL 的功能,不過讓 debug 的流程變得更加流暢了。

結論

express-error

詳細的實作在這個 repo 裏頭,最近如果比較有空會在將實作抽換為 middleware 的形式方便大家使用。也會逐漸優化整個 layout 與 code highlight 的部分,並且讓整個流程跟畫面更加流暢一些。雖然不知道會過多久就是了 XD

如果有各種建議也歡迎提出 issue。