ELMAH在ASP.NET MVC中的使用

Google:http://code.google.com/p/elmah/
Demo:
http://code.google.com/p/elmah/wiki/DotNetSlackersArticle
Elman:
http://nuget.org/packages/elmah(Install-Package elmah)
Compact:
http://nuget.org/packages/elmah.sqlservercompact(Install-Package elmah.sqlservercompact)

安装

最简单的方式通过Nuget获得

image

我的环境是MVC4所以安装Elamh.MVC,因为这样会为我们默认配置好大部分的配置,当然也可以安装Elamh(如配置 Adding ELMAH to your ASP.NET Web Site),也可以安装XML,MongDB,MSSQL,MYSQL,Fiter等配置

Elmah.MVC 依赖与Elmah.corelibrary
"Could not load type 'Elmah.ErrorLogModule' from assembly 'Elmah'." 这个错误花了好长时间,开始我以为是IIS6与IIS7配置问题,最后发现是项目名与Elmah重复了
http://stackoverflow.com/questions/8409581/could-not-load-type-elmah-errorlogmodule-from-assembly-elmah

这里我故意写了句代码

       public ActionResult Index()
        {
            ViewBag.Message = "修改此模板以快速启动你的 ASP.NET MVC 应用程序。";
            int num = Convert.ToInt32("Irving");
            return View();
        });

访问:http://localhost:16899/ 显示错误

当然可以配置不显示错误

 <customErrors defaultRedirect="/Error" mode="RemoteOnly">
      <error redirect="/Error/NotFound" statusCode="404" />
      <error redirect="/Error/Error" statusCode="500" />
 <customErrors/>

或者将异常抛出去

       public ActionResult Index()
        {
            ViewBag.Message = "修改此模板以快速启动你的 ASP.NET MVC 应用程序。";
            try
            {
                int num = Convert.ToInt32("Irving");
            }
            catch (Exception e)
            {
                ErrorSignal.FromCurrentContext().Raise(e);
            }
            return View();
        }

访问:http://localhost:16899/elmah

image

可以查看详情,以XML 或者JSON方式显示

储存方式

Memory

<elmah>
    <errorLog type="Elmah.MemoryErrorLog, Elmah" size="100" />
</elmah>

这个没有什么好说的,正式环境不推荐使用

XML

<elmah>
  <errorLog type="Elmah.XmlFileErrorLog, Elmah" logPath="~/App_Data/ElmahXML_Logs" />
</elmah>

imageimage

Email

<errorMail from="sender@domain.com" 
             to="receiver@domain.com" 
             cc="copy@domain.com" 
             subject="Your Subject"
             async="true or false"
             smtpPort="25"
             smtpServer="smtp.domain.com" 
             userName="username"
             password="password" />
</elmah>

有关stmp协议看这里 http://help.163.com/09/1223/14/5R7P6CJ600753VB8.html?b08ene1

image

SQLite

<connectionStrings>
  <add name="ElmahDB" connectionString="data source=~/App_Data/Elmah.db" />
</connectionStrings>
<elmah>
  <errorLog type="Elmah.SQLiteErrorLog, Elmah" connectionStringName="ElmahDB" />
</elmah>

没有测试

MSSQL

  <elmah>
      <errorLog type="Elmah.SqlErrorLog, Elmah" connectionStringName="ElmahConnText" />
  </elmah>
  <connectionStrings>
    <add name="ElmahConnText" connectionString="Data Source=.;Initial Catalog=ElmahDB;User ID=sa;Password=123"/>
  </connectionStrings>

这里需要手动插件表,脚本如下 或者 Install-Package elmah.sqlservercompact

image

View Code
/*
  
   ELMAH - Error Logging Modules and Handlers for ASP.NET
   Copyright (c) 2004-9 Atif Aziz. All rights reserved.
  
    Author(s):
  
        Atif Aziz, http://www.raboof.com
        Phil Haacked, http://haacked.com
  
   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at
  
      http://www.apache.org/licenses/LICENSE-2.0
  
   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
  
*/

-- ELMAH DDL script for Microsoft SQL Server 2000 or later.

-- $Id: SQLServer.sql 677 2009-09-29 18:02:39Z azizatif $

DECLARE @DBCompatibilityLevel INT
DECLARE @DBCompatibilityLevelMajor INT
DECLARE @DBCompatibilityLevelMinor INT

SELECT 
    @DBCompatibilityLevel = cmptlevel 
FROM 
    master.dbo.sysdatabases 
WHERE 
    name = DB_NAME()

IF @DBCompatibilityLevel <> 80
BEGIN

    SELECT @DBCompatibilityLevelMajor = @DBCompatibilityLevel / 10, 
           @DBCompatibilityLevelMinor = @DBCompatibilityLevel % 10
           
    PRINT N'
    ===========================================================================
    WARNING! 
    ---------------------------------------------------------------------------
    
    This script is designed for Microsoft SQL Server 2000 (8.0) but your 
    database is set up for compatibility with version ' 
    + CAST(@DBCompatibilityLevelMajor AS NVARCHAR(80)) 
    + N'.' 
    + CAST(@DBCompatibilityLevelMinor AS NVARCHAR(80)) 
    + N'. Although 
    the script should work with later versions of Microsoft SQL Server, 
    you can ensure compatibility by executing the following statement:
    
    ALTER DATABASE [' 
    + DB_NAME() 
    + N'] 
    SET COMPATIBILITY_LEVEL = 80

    If you are hosting ELMAH in the same database as your application 
    database and do not wish to change the compatibility option then you 
    should create a separate database to host ELMAH where you can set the 
    compatibility level more freely.
    
    If you continue with the current setup, please report any compatibility 
    issues you encounter over at:
    
    http://code.google.com/p/elmah/issues/list

    ===========================================================================
'
END
GO

/* ------------------------------------------------------------------------ 
        TABLES
   ------------------------------------------------------------------------ */

CREATE TABLE [dbo].[ELMAH_Error]
(
    [ErrorId]     UNIQUEIDENTIFIER NOT NULL,
    [Application] NVARCHAR(60)  COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL,
    [Host]        NVARCHAR(50)  COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL,
    [Type]        NVARCHAR(100) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL,
    [Source]      NVARCHAR(60)  COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL,
    [Message]     NVARCHAR(500) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL,
    [User]        NVARCHAR(50)  COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL,
    [StatusCode]  INT NOT NULL,
    [TimeUtc]     DATETIME NOT NULL,
    [Sequence]    INT IDENTITY (1, 1) NOT NULL,
    [AllXml]      NTEXT COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL 
) 
ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]

GO

ALTER TABLE [dbo].[ELMAH_Error] WITH NOCHECK ADD 
    CONSTRAINT [PK_ELMAH_Error] PRIMARY KEY NONCLUSTERED ([ErrorId]) ON [PRIMARY] 
GO

ALTER TABLE [dbo].[ELMAH_Error] ADD 
    CONSTRAINT [DF_ELMAH_Error_ErrorId] DEFAULT (NEWID()) FOR [ErrorId]
GO

CREATE NONCLUSTERED INDEX [IX_ELMAH_Error_App_Time_Seq] ON [dbo].[ELMAH_Error] 
(
    [Application]   ASC,
    [TimeUtc]       DESC,
    [Sequence]      DESC
) 
ON [PRIMARY]
GO

/* ------------------------------------------------------------------------ 
        STORED PROCEDURES                                                      
   ------------------------------------------------------------------------ */

SET QUOTED_IDENTIFIER ON 
GO
SET ANSI_NULLS ON 
GO

CREATE PROCEDURE [dbo].[ELMAH_GetErrorXml]
(
    @Application NVARCHAR(60),
    @ErrorId UNIQUEIDENTIFIER
)
AS

    SET NOCOUNT ON

    SELECT 
        [AllXml]
    FROM 
        [ELMAH_Error]
    WHERE
        [ErrorId] = @ErrorId
    AND
        [Application] = @Application

GO
SET QUOTED_IDENTIFIER OFF 
GO
SET ANSI_NULLS ON 
GO

SET QUOTED_IDENTIFIER ON 
GO
SET ANSI_NULLS ON 
GO

CREATE PROCEDURE [dbo].[ELMAH_GetErrorsXml]
(
    @Application NVARCHAR(60),
    @PageIndex INT = 0,
    @PageSize INT = 15,
    @TotalCount INT OUTPUT
)
AS 

    SET NOCOUNT ON

    DECLARE @FirstTimeUTC DATETIME
    DECLARE @FirstSequence INT
    DECLARE @StartRow INT
    DECLARE @StartRowIndex INT

    SELECT 
        @TotalCount = COUNT(1) 
    FROM 
        [ELMAH_Error]
    WHERE 
        [Application] = @Application

    -- Get the ID of the first error for the requested page

    SET @StartRowIndex = @PageIndex * @PageSize + 1

    IF @StartRowIndex <= @TotalCount
    BEGIN

        SET ROWCOUNT @StartRowIndex

        SELECT  
            @FirstTimeUTC = [TimeUtc],
            @FirstSequence = [Sequence]
        FROM 
            [ELMAH_Error]
        WHERE   
            [Application] = @Application
        ORDER BY 
            [TimeUtc] DESC, 
            [Sequence] DESC

    END
    ELSE
    BEGIN

        SET @PageSize = 0

    END

    -- Now set the row count to the requested page size and get
    -- all records below it for the pertaining application.

    SET ROWCOUNT @PageSize

    SELECT 
        errorId     = [ErrorId], 
        application = [Application],
        host        = [Host], 
        type        = [Type],
        source      = [Source],
        message     = [Message],
        [user]      = [User],
        statusCode  = [StatusCode], 
        time        = CONVERT(VARCHAR(50), [TimeUtc], 126) + 'Z'
    FROM 
        [ELMAH_Error] error
    WHERE
        [Application] = @Application
    AND
        [TimeUtc] <= @FirstTimeUTC
    AND 
        [Sequence] <= @FirstSequence
    ORDER BY
        [TimeUtc] DESC, 
        [Sequence] DESC
    FOR
        XML AUTO

GO
SET QUOTED_IDENTIFIER OFF 
GO
SET ANSI_NULLS ON 
GO

SET QUOTED_IDENTIFIER ON 
GO
SET ANSI_NULLS ON 
GO

CREATE PROCEDURE [dbo].[ELMAH_LogError]
(
    @ErrorId UNIQUEIDENTIFIER,
    @Application NVARCHAR(60),
    @Host NVARCHAR(30),
    @Type NVARCHAR(100),
    @Source NVARCHAR(60),
    @Message NVARCHAR(500),
    @User NVARCHAR(50),
    @AllXml NTEXT,
    @StatusCode INT,
    @TimeUtc DATETIME
)
AS

    SET NOCOUNT ON

    INSERT
    INTO
        [ELMAH_Error]
        (
            [ErrorId],
            [Application],
            [Host],
            [Type],
            [Source],
            [Message],
            [User],
            [AllXml],
            [StatusCode],
            [TimeUtc]
        )
    VALUES
        (
            @ErrorId,
            @Application,
            @Host,
            @Type,
            @Source,
            @Message,
            @User,
            @AllXml,
            @StatusCode,
            @TimeUtc
        )

GO
SET QUOTED_IDENTIFIER OFF 
GO
SET ANSI_NULLS ON 
GO

ErrorFiltering

我们可以通过代码的方式过滤错误,如我们不想在应用程序发生404错误的时候记录日志,我们只需要在Global.asax文件中自定义

这里我启用XML与Email两种方式

<elmah>
    <security allowRemoteAccess="1" />
    <!--XML方式-->
    <errorLog type="Elmah.XmlFileErrorLog, Elmah" logPath="~/App_Data/ElmahXML_Logs"/>
    <!--Email方式-->
    <errorMail from="xxx@163.com" to="xxx@qq.com" subject="ERROR From Elmah:" async="true" smtpPort="25" smtpServer="smtp.163.com" userName="xxx.com" password="xxx>
    <!--内存方式-->
    <!--<errorLog type="Elmah.MemoryErrorLog, Elmah"/>-->
    <!--MSSQL方式-->
    <!--<errorLog type="Elmah.SqlErrorLog, Elmah" connectionStringName="ElmahConnText" />-->
  </elmah>

allowRemoteAccess 中0表示允许本地浏览,1表示允许本地与远程,其他方式没有试(RSS MYSQL ORACEL等)

protected void ErrorLog_Filtering(object sender, ExceptionFilterEventArgs e)
        {
            FilterError404Exception(e);
            FilterErrorValidationException(e);
            FilterErrorNotFileException(e);
        }

        protected void ErrorMail_Filtering(object sender, ExceptionFilterEventArgs e)
        {
            FilterError404Exception(e);
        }
        /// <summary>
        /// 处理404
        /// </summary>
        /// <param name="e"></param>
        void FilterError404Exception(ExceptionFilterEventArgs e)
        {
            if (e.Exception.GetBaseException() is HttpException)
            {
                HttpException ex = (HttpException)e.Exception.GetBaseException();

                if (ex.GetHttpCode() == 404)
                {
                    e.Dismiss();
                }
            }
        }
        /// <summary>
        /// 处理 HttpRequestValidationException
        /// </summary>
        /// <param name="args"></param>
        void FilterErrorValidationException(ExceptionFilterEventArgs args)
        {
            if (args.Exception.GetBaseException() is HttpRequestValidationException)
            {
                args.Dismiss();
            }
        }
        void FilterErrorNotFileException(ExceptionFilterEventArgs e)
        {
            if (e.Exception.GetBaseException() is FileNotFoundException)
            {
                e.Dismiss();
            }
        }

IIS配置

II6与IIS7+异同,这个貌似跟集成模式与经典模式有关系,MVC中如果是用Nuget安装的话,应该不用考虑这个问题,如果有问题参考下面链接

安全性

可以配置<security>节点设置allowRemoteAccess为0,这样就不能够通过远程的方式进行访问,如果想既支持远程查看日志又能够保证安全性,首先设置allowRemoteAccess为1然后需要我们对elmah.axd路径进行权限控制,可以在<loaction>节点下进行相关配置(Form验证)。

<location path="elmah.axd">
    <system.web>
      <httpHandlers>
        <add verb="POST,GET,HEAD" path="elmah.axd" type="Elmah.ErrorLogPageFactory, Elmah" />
      </httpHandlers>
      <authorization>
        <allow roles="Irving" />
        <deny users="*" />
      </authorization>
    </system.web>
    <system.webServer>
      <handlers>
        <add name="ELMAH" verb="POST,GET,HEAD" path="elmah.axd" type="Elmah.ErrorLogPageFactory, Elmah" preCondition="integratedMode" />
      </handlers>
    </system.webServer>
  </location>

http://www.troyhunt.com/2012/01/aspnet-session-hijacking-with-google.html

这篇文章非常好,之前有一篇文章讨论 From验证安全性 machineconfig中的key不被篡改的话,能够确保安全性,链接找不到了

ElmahR

ElmahR是一个集成了SignalR技术,可以把错误的消息推送到浏览器上,可以在这里看DEMO :http://elmahr.apphb.com/?log=1
ElmahR = ELMAH + SignalR (1.0.0 released!)
http://www.codeproject.com/Articles/377394/ElmahR-equals-ELMAH-plus-SignalR-released
Streaming logs with SignalR 2.0
http://www.codeproject.com/Articles/758633/Streaming-logs-with-SignalR

更新:新版本已支持APS.NET MVC 性能路由的调试

Refer:

ASP.NET MVC
http://www.asp.net/web-forms/tutorials/deployment/deploying-web-site-projects/logging-error-details-with-elmah-cs
http://www.codeproject.com/Articles/26468/ELMAH-Error-Loggin-Module-and-Handler-For-unhandle
http://volaresystems.com/Blog/post/2009/08/23/Handling-Exceptions-in-ASPNET-MVC.aspx
http://www.hanselman.com/blog/NuGetPackageOfTheWeek7ELMAHErrorLoggingModulesAndHandlersWithSQLServerCompact.aspx
http://www.codecapers.com/post/Error-Handling-in-MVC-with-ELMAH.aspx
http://www.cnblogs.com/lerit/archive/2011/03/29/1998396.html

ASP.NET WEBFROM
http://www.asp.net/web-forms/tutorials/deployment/deploying-web-site-projects/logging-error-details-with-asp-net-health-monitoring-cs
http://msdn.microsoft.com/en-us/library/aa479332.aspx
http://www.itscodingtime.com/itscodingtime/post/Adding-ELMAH-to-your-ASPNET-Web-Site.aspx

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

Securing
http://code.google.com/p/elmah/wiki/SecuringErrorLogPages
http://www.troyhunt.com/2012/01/aspnet-session-hijacking-with-google.html
http://www.beletsky.net/2011/03/integrating-elmah-to-aspnet-mvc-in.html
http://stackoverflow.com/questions/1245364/securing-elmah-in-asp-net-website  (备注)

IIS
http://www.asp.net/mvc/tutorials/older-versions/deployment/using-asp-net-mvc-with-different-versions-of-iis-cs
http://www.cnblogs.com/apsnet/archive/2012/04/28/2474730.html

原文地址:https://www.cnblogs.com/Irving/p/2858601.html