網頁

2012年6月11日 星期一

ASP.NET MVC 3 使用 ELMAH 無法記錄正確 Http Status Code ?

 

最近為了 twMVC攻略 ASP.NET MVC ─ 從無到有 與 偵測監控」其中的講題「ASP.NET MVC 偵測監控與 Log 記錄」而一直在做準備,

但是這兩天突然看到一個很奇怪的現象,讓我一直很納悶……

image

為什麼 ELMAH 沒有記錄到正確的 Http Status Code 呢?原本應該是 500 而 ELMAH 卻是記錄為 0 ……

這個問題一直困擾著我,所以就試著找出原因來。

 


先看一下在 ELMAH 官網上的一張圖片(來源:ELMAH Wiki  MVC - Using ELMAH with ASP.NET MVC

image

這裡看到的也是將原本 HttpStatusCode 應該是 500 的錯誤給記錄為 0,

而在 ASP.NET WebForms 中並不會有這樣的情況發生呀,來看看一個 ASP.NET WebForms 網站的 ELMAH 記錄,

image

在 ASP.NET WebForms 網站裡,ELMAH 是會記錄正確的 Http Status Code,

那為什麼 ASP.NET MVC 就不行呢?

於是我就開了一個測試方案,這方案中分別建立了 ASP.NET MVC 網站與 ASP.NET WebForms 網站,

兩個網站都透過 NuGet 增加 ELMAH Packages,為求有一致性的比較,ASP.NET MVC 網站不另外安裝 Elmah.MVC,

(Elmah.MVC 這個 Package 其實不影響 Elmah 的記錄,Elmah.MVC 是提供 MVC網站另一個瀏覽 Elmah 記錄的方式)

image

然後這兩個網站專案都執行同樣的錯誤,這個錯誤會觸發 Internal Error 並使 HttpStatusCode 為 500,

ASP.NET MVC

image

ASP.NET WebForms

image

 

不做任何改變的情況下,先觀察一下  exception 發生當下的情況:

ASP.NET WebForms

image

ASP.NET MVC

image

其實看內容還不出個所以然,因為怎麼看都差不多,差別就在於一個是一般的 System.Web 另一個是 System.Web.Mvc,

為了要更深入了解 ELMAH 的紀錄運作,所以就去下載 ELMAH 的原始檔,將 ELMAH 原始檔專案加入到測試專案中,

以方便偵測 ELMAH 的動作,

http://code.google.com/p/elmah/wiki/Downloads?tm=2

image

 

 

方案加入 Elmah 原始檔專案

image

而我們要加入的專案檔(*.csproj)記得要加入對應 Visual Studio 版本的專案檔,

我是使用 VS2010 建立專案的,所以我就是加入「/src/Solutions/2010」目錄下的 Elmah.csproj 檔案

image

加入了 Elamh 原始檔專案之後,接下來就是把  MVC 與 WebForms 網站專案原本由 NuGet 加入的 Elmah 給移除,

image

然後加入剛才的 Elmah 原始檔專案的參考

image

SNAGHTML153fa0d

上面是以 ASP.NET MVC 網站專案為例,記得 ASP.NET WebForms 網站專案也要做相同的操作,

做完以上的動作之後,記得要重新建置方案,並且執行幾個錯誤,以確定 Elmah 是可以正常運作的。

 

 

追查原因!

加入的 Elmah 原始檔專案後,我們就直搗黃龍來追查原因,不過看到了 Elmah 原始檔專案中的檔案有這麼多…

要從何追起呢?

image

直接追查 Error.cs 這個檔案就對了,

image

在 error.cs 中的 Error() 這個 method 下中斷點,然後我們先以 ASP.NET WebForms 來看 Elmah 進行錯誤記錄的運作,

以下可以看到 ASP.NET WebForms 觸發了一個 Expception,然後在 Elmah 就去取得 HttpException 的 statusCode,

就是 Line 116 這個地方讓 Elmah 可以記錄下正確的 Http Status Code,

image

image

 

接著我們來看看 MVC 是不是這樣呢?

image

是不是看出什麼端倪呢?

MVC中所以觸發的 Exception 類別並不是 HttpException,所以在進行類別轉換的時候就會是 NULL,

因為不是 HttpException 類別所以就會抓不到原本在 HttpExceotion 中的 HttpCode 值,

所以到最後 ELMAH 在 MVC 網站專案中所記錄的 StatusCode 就會是「0」

image

 

MVC 在 Controller 中所觸發的 Exception 其類別並不是 HttpException,

而 WebForms 在 Page Code-Behind 所觸發的 Exception 是 HttpException,

Elmah 中對於 StatusCode 的紀錄來源是取 HttpException,

所以「_statusCode = httpException.GetHttpCode();」才能取得 Http Status Code,

如此一來 MVC 就無法記錄到我們預期中的 StatusCode 值,

 

那到底 MVC 所觸發的 Exception 是什麼型別呢?

MVC 是直接把 Exception 給丟出來,沒有再另外包裝,

image

而 WebForms 的 Exception 型別則是 HttpUnhandledException……

image

 

 

解決!

原本想到的解決方式是另外建立一個 HandleErrorAttribute 類別,然後於 OnException 方法中再去處理 exception 的型別,

但是這樣的方式似乎並不是那麼好做,而且也不是那麼有效,而且對於網站本身的 Error Handler 影響比較大,

於是就用上最簡單的方式,就是直接改變 Elmah 原始檔 Error.cs 的方法內容,

其實也稱不上是修改,只是去增加個幾行程式,如下:

if (httpException == null)
{
    httpException = new HttpException(null, e);
}

因為 MVC 所產生的 Exception 並不是 HttpException 型別,所以我就增加個判斷,

當原本的 exception 轉換為 HttpException 為 null 時,就建立一個型別為 HttpException 的 instance,

然後把原本的 exception 就放到這個新 exception 的 InnerException 中,

如此一來,底下的程式就可以抓到正確的 StatusCode 了,

image

 

修改好程式之後,我們重新執行一次 MVC 網站中的錯誤,並且觀察修改過程式的 Elmah 能否抓到 StatusCode,

一開始的判斷, 轉換型別的 exception 是 null (基礎型別不同)

image

所以我們建立一個新的 HttpException instance,然後把原本 exception 給丟到 InnerException 中,

image

接下來程式中就可以抓到 StatusCode 了,

image\

最後看看 Elmah 是否真的有記錄正確的 Http StatusCode 呢?

image

 

 

最後

這篇文章中的作法是說明 ELMAH 於 ASP.NET MVC 網站無法記錄正確 StatusCode 的一種解決方式,

這樣的修改對於原本的網站程式並不會有任何的變動,而有變動的就是加入專案參考的 Elmah 原始檔專案,

所以在往後的專案元件管理,就必須要記得需要一併管理這有修改過的 Elmah 原始檔,

如果往後這個專案沒有使用修改過的 Elmah 而是使用原始的 Elmah,是不會對 MVC 網站程式有任何影響,

直接影響的就是不能夠正確的紀錄 Http StatusCode。

 

老實說,有沒有正確的紀錄 Http StatusCode 並沒有很大的需要,這是要看各位對於 Log 記錄完整度的要求,

有的人會希望這樣的錯誤記錄也能夠正確的紀錄任何的資訊,這樣在 Elmah Dashboard 瀏覽錯誤記錄時可以明顯判別,

image

 

 


這個問題困惑了幾天,基於有問題就要找出原因的堅持,就花了點時間來找出問題的所在,知道原因之後就豁然開朗了,

是不是這麼堅持一定要記錄正確的 Http StatusCode 呢?

對於解開疑惑之後,我反倒沒有那麼堅持了,知道原因對我來說比較重要。

 

如果堅持 Elmah 在 ASP.NET MVC 網站也必須要正確並完整地記錄所有資訊,那麼可以參考上面修改的方式,

如果對於記錄的正確性與完整度並不是那麼要求(因為會被 Elmah 所記錄下來的就是有錯誤的),那麼上面的作法就可以忽略。

 

以上

1 則留言:

  1. 此篇文章已被收錄在 elmah 官網的 Wiki 中,WebBase 單元內,
    https://code.google.com/p/elmah/wiki/WebBase

    回覆刪除