2011年10月9日 星期日

ASP.NET MVC + ELMAH 監控並記錄你的網站錯誤資訊 1

網站上線或是還在開發、測試階段時對於難以捉摸的錯誤總是讓開發人員頭痛不已,如果是在開發環境裡,還容易抓到錯誤,一旦上了測試機或是正式機之後,錯誤的發生就無法立即掌握,尤其是當客戶在測試的時候往往遇到錯誤就只會通知開發人員說「你的程式有 BUG!」,但是要問客戶說這些錯誤是怎麼發生的時候,客戶通常是說不清楚,甚至有的客戶明明操作錯誤或是輸入錯誤的資料而引發錯誤,但也不會老實跟開發人員說出實情,而如果可以任何時候都監控網站並且當錯誤發生的時候將當下的資訊給記錄下來的話,對於開發人員甚至是對於系統的功能完整、安全防護、減少 BUG 數等等都是一件好事。

網路上有很多種監控以及記錄的工具,然而眾多的工具、套件中最容易上手的應該就是 ELMAH 了,而且最重要的是,ELMAH 有提供 DashBoard 可以查看記錄的訊息,另外也可以透過遠端的方式去查看記錄,如此一來就可以更加容易掌握網站的一舉一動了(安全機制還是要先做好),就讓我們來看看如何在你的 ASP.NET MVC 3 網站中使用ELMAH 來監控並記錄錯誤訊息。


ELMAH

Error Logging Modules and Handlers

http://code.google.com/p/elmah/

ELMAH (Error Logging Modules and Handlers) is an application-wide error logging facility that is completely pluggable. It can be dynamically added to a running ASP.NET web application, or even all ASP.NET web applications on a machine, without any need for re-compilation or re-deployment.

Once ELMAH has been dropped into a running web application and configured appropriately, you get the following facilities without changing a single line of your code:

  • Logging of nearly all unhandled exceptions.
  • A web page to remotely view the entire log of recoded exceptions.
  • A web page to remotely view the full details of any one logged exception, including colored stack traces.
  • In many cases, you can review the original yellow screen of death that ASP.NET generated for a given exception, even with customErrors mode turned off.
  • An e-mail notification of each error at the time it occurs.
  • An RSS feed of the last 15 errors from the log.
  • 而官網的Wiki中則有一篇文件,說明如何在ASP.NET MVC網站中安裝並使用ELMAH,

    http://code.google.com/p/elmah/wiki/MVC

    其實整個安裝的步驟並不是很繁瑣,但這裡我則介紹如何在VS2010中使用NuGet安裝ELMAH。

     

    Step.1

    開啟你的ASP.NET MVC網站,並且開啟NuGet Packages Manager

    image

    於NuGet Packages Manager中去輸入ELMAH查詢相關套件

    image

    在這裡要安裝的有:

    • ELMAH
    • ELMAH Core Library(no config)
    • ELMAH.MVC
    • ELMAH on XML Log

    ELMAH可以將訊息給記錄到很多的裝置上,不過在這裡我則是要把訊息記錄到XML文字檔中,記錄在XML文字檔中的好處是可以避免資料庫的連線錯誤問題,無法連線時也是可以記錄錯誤,缺點是錯誤記錄無法篩選,而記錄在資料庫的話則是可以使用資料庫來查詢特定的錯誤記錄。

    安裝完成後,看看方案總管,增加了「~/App_Data/Elmah.Errors」「~/Areas/Admin/」

    image

     

    Step.2

    利用NuGet安裝ELMAH的好處是,它會自動去增修Web.Config的Section,我們就不必手動去增加那些Section,

    image

    image

    image

    我們可以直接輸入「http://your-web-Url/Admin/ELMAH」就可以按到ELMAH的DashBoard

    image

     

    Step.3

    接下來就是測試,輸入不存在的網頁或是執行有錯誤的Action等,看看是不是會把錯誤給記錄下來,

    輸入一個不存在的網址

    image

    沒有輸入Parameter

    image

    執行一個會有Exception的Action

    image

    來看看ELMAH DashBoard

    image

    上圖看來,剛剛那些的錯誤黃頁都有把資訊給記錄下來,而我們挑一個Error來看看它的Details,

    image
    image

    ELMAH都詳細了記錄錯誤發生的訊息以及當下執行的Server端與Client端的環境訊息,

     

    Step.4

    而使用了ELMAH後,我們可以記錄錯誤訊息,但是我們並不希望把錯誤黃頁就這樣直接顯示給使用者看到,除了說可以修改Web.Confg的customError的方式,將錯誤給導到特定頁面中,這邊我用別的方式,在Global.asax裡去增加Application_Error()方法,於發生錯誤時導到特定的網頁,

    /// <summary>
    
    /// Handles the Error event of the Application control.
    
    /// </summary>
    
    /// <param name="sender">The source of the event.</param>
    
    /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
    
    protected void Application_Error(object sender, EventArgs e)
    
    {
    
        var app = (MvcApplication)sender;
    
        var context = app.Context;
    
        var ex = app.Server.GetLastError();
    
        context.Response.Clear();
    
        context.ClearError();
    
        var httpException = ex as HttpException;
    
        var routeData = new RouteData();
    
        routeData.Values["controller"] = "Error";
    
        routeData.Values["exception"] = ex;
    
        routeData.Values["action"] = "Index";
    
        if (httpException != null)
    
        {
    
            switch (httpException.GetHttpCode())
    
            {
    
                case 403:
    
                    routeData.Values["action"] = "Forbidden";
    
                    break;
    
                case 404:
    
                    routeData.Values["action"] = "PageNotFound";
    
                    break;
    
                case 500:
    
                    routeData.Values["action"] = "InternalError";
    
                    break;
    
                default:
    
                    routeData.Values["action"] = "GenericError";
    
                    break;
    
            }
    
        }
    
        // Pass exception details to the target error View.
    
        routeData.Values.Add("Error", ex.Message);
    
        // Avoid IIS7 getting in the middle
    
        context.Response.TrySkipIisCustomErrors = true;
    
        IController controller = new ErrorController();
    
        controller.Execute(new RequestContext(new HttpContextWrapper(context), routeData));
    
    }

    我這邊只有針對403, 404, 500的三個錯誤去導到特定的錯誤顯示頁面,而其他的錯誤則導到統一的頁面中,接下來就是建立ErrorController以及相對應的Action、ViewPage,

    public class ErrorController : Controller
    
    {
    
        /// <summary>
    
        /// Indexes the specified error.
    
        /// </summary>
    
        /// <param name="error">The error.</param>
    
        /// <returns></returns>
    
        public ActionResult Index(string error)
    
        {
    
            ViewData["Title"] = "抱歉, 處理你的請求發生錯誤";
    
            ViewData["Description"] = error;
    
            return View();
    
        }
    
        /// <summary>
    
        /// Generics the error.
    
        /// </summary>
    
        /// <param name="error">The error.</param>
    
        /// <returns></returns>
    
        public ActionResult GenericError(string error)
    
        {
    
            ViewData["Title"] = "抱歉, 處理你的請求發生錯誤";
    
            ViewData["Description"] = error;
    
            return View();
    
        }
    
        /// <summary>
    
        /// Forbiddens the specified error.
    
        /// </summary>
    
        /// <param name="error">The error.</param>
    
        /// <returns></returns>
    
        public ActionResult Forbidden(string error)
    
        {
    
            return RedirectToAction("Index", "Home");
    
        }
    
        /// <summary>
    
        /// Pages the not found.
    
        /// </summary>
    
        /// <param name="error">The error.</param>
    
        /// <returns></returns>
    
        public ActionResult PageNotFound(string error)
    
        {
    
            ViewData["Title"] = "抱歉, 處理你的請求發生404錯誤";
    
            ViewData["Description"] = error;
    
            Response.StatusCode = 404;
    
            return View();
    
        }
    
        /// <summary>
    
        /// Internals the error.
    
        /// </summary>
    
        /// <param name="error">The error.</param>
    
        /// <returns></returns>
    
        public ActionResult InternalError(string error)
    
        {
    
            ViewData["Title"] = "抱歉, 處理你的請求發生500錯誤";
    
            ViewData["Description"] = error;
    
            Response.StatusCode = 500;
    
            return View();
    
        }
    
    }

    這樣一來這些錯誤就會導到特定的頁面中,而ELMAH依然持續記錄錯誤訊息,

    image

    image

    補充說明:

    在 ErrorController 的 InternalError, GenericError, Index 這幾個 Action 裡會將錯誤訊息給放到 ViewData 中,然後在前端頁面上顯示,這樣的作法只適合在練習的時候使用,而一般實際的專案並不會直接將這些錯誤訊息給直接揭露於前端使用者可以看到的頁面上,因為這麼做的話是有可能會把系統的內部細節給暴露在外,所以千萬不要將錯誤訊息直接的顯示在頁面上讓使用者看到喔!

     

    而我們來看看錯誤訊息是記錄的目錄「~/App_Data/Elmah.Errors」下的狀況,

    image

    ELMAH把每個錯誤記錄存成一個個的XML檔案。

     

    好的,我們完成了在ASP.NET MVC中使用NuGet來安裝ELMAH的步驟了,但是還是有一些地方還沒有完成,應該有些人會發覺到,為什麼直接輸入「http://your – WebSite – URL/Admin/ELMAH」就可以看到錯誤的紀錄呢?要是就這樣放到網路上還得了,而透過Nuget安裝ELMAH.MVC後,會預設建立在Areas下的Admin,萬一我不想放在Areas的Admin下呢?

    下一回再說…

     

    參考連結:

    elmah:Error Logging Modules and Handlers for ASP.NET

    Using ELMAH with ASP.NET MVC

    ELMAH: Error Logging Modules and Handlers for ASP.NET (and MVC too!)

    ELMAH - Error Loggin Module and Handler For unhandled errors in asp.net

     

    以上

    7 則留言:

    1. HI 版主你好,這個好的擴充,
      使用在 ASP.NET 也可以嗎?

      回覆刪除
      回覆
      1. Hello, Elmah當然也可以應用在ASP.NET WebForm上囉
        Elmah一開始就是提供給ASP.NET WebForm使用,早在2004年就已經發佈,
        後來又增加「Elmah.MVC」組件給ASP.NET MVC來使用

        http://code.google.com/p/elmah/

        http://aspnet2share.blogspot.com/2011/11/nuget-nuget-msdn-nuget-visual-studio.html

        http://msdn.microsoft.com/en-us/library/aa479332.aspx

        刪除
    2. 請問我用visual studio 2012 Nuget 產生Elmah為何沒有自動產生~/Areas/Admin/資料夾呢?

      回覆刪除
      回覆
      1. 單純安裝 Elmah 是不會另外建立~/Areas/Admin 目錄。

        上面的文章裡還包含要安裝「ELMAH.MVC」
        舊版的 ELMAH.MVC 經由 NuGet 安裝後會建立 ~/Areas/Admin 目錄,
        而新版的 ELMAH.MVC 已經改了方式,不會再建立 ~/Areas/Admin 目錄。

        刪除
    3. 想請教一下,我設定至此出現很有趣的現象
      只有404的時候頁面下面的畫面,整個error page的HTML被包在雙引號裡面呈現出來
      (其他錯誤都正常導至該去的頁面)
      http://imgur.com/a/Ccz1f

      回覆刪除
      回覆
      1. 連結為HTML的截圖請安心服用

        刪除
      2. 在擷圖裡有看到 browserLink 的內容,你要不要先把 BrowserLink 功能關閉呢
        或者你先將程式發佈到測試環境或預備環境裡看看

        刪除

    提醒

    千萬不要使用 Google Talk (Hangouts) 或 Facebook 及時通訊與我聯繫、提問,因為會掉訊息甚至我是過了好幾天之後才發現到你曾經傳給我訊息過,請多多使用「詢問與建議」(在左邊,就在左邊),另外比較深入的問題討論,或是有牽涉到你實作程式碼的內容,不適合在留言板裡留言討論,請務必使用「詢問與建議」功能(可以夾帶檔案),謝謝。