您的位置:首页 > 编程语言 > C#

C#组件系列——又一款日志组件:Elmah的学习和分享

2017-08-24 15:57 495 查看
阅读目录

一、组件介绍

二、组件安装使用

1、安装组件

2、配置组件

3、测试效果

三、功能介绍

1、将日志信息保存到数据库

2、程序“吃掉”异常

3、组件权限问题

四、总结

正文

前言:好久没动笔了,都有点生疏,12月都要接近尾声,可是这月连一篇的产出都没有,不能坏了“规矩”,今天还是来写一篇。最近个把月确实很忙,不过每天早上还是会抽空来园子里逛逛。一如既往,园子里每年这个时候都有大把的年终总结、回忆过去展望未来之类的文章。博主是没时间写总结了,要学的东西太多。关于Vue的系列一定要抽时间补上。最近刚用了一个日志组件Elmah,比较适合开发阶段异常信息的快速定位与追溯,有兴趣的跟着博主一起来看看吧。

本文原创地址:http://www.cnblogs.com/landeanfen/p/6221403.html

回到顶部

一、组件介绍

ELMAH的全称是The Error Logging Modules And Handlers,翻译过来是错误日志模块和处理。顾名思义,就是一个日志的拦截和处理组件,说到.net的日志组件,大家的第一反应该是Log4Net、NLog等这些东西,关于Log4Net和NLog,可以说是.net日志组件里面使用最为广泛的组件了,它们功能强大、使用方便。相比它们:

1、ELMAH的使用更加简单,它甚至不用写一句代码,只需要引入dll,然后在Web.config里面配置相应的节点即可;

2、按照网上的说法,ELMAH是一种“可拔插式”的组件,即在一个运行的项目里面我们可以随意轻松加入日志功能,或者移除日志功能;

3、ELMAH组件自带界面,不用写任何代码,即可查看异常日志的界面,轻松找到当前异常的详细信息;和web的结合更加紧密;

4、组件提供了一个用于集中记录和通知错误日志的机制,通过邮件的机制通知错误信息给相关人员。

回到顶部

二、组件安装使用

回到顶部

1、安装组件

Elmah的安装使用同样也很简单,我们万能的Nuget帮我们一键搞定。

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

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

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

SET @StartRowIndex = @PageIndex * @PageSize + 1
SET ROWCOUNT @StartRowIndex

SELECT
@FirstTimeUTC = TimeUTC,
@FirstSequence = Sequence
FROM
ELMAH_Error
WHERE
Application = @Application
ORDER BY
TimeUTC DESC,
Sequence DESC

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

SET ROWCOUNT @PageSize

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

SELECT
errorId,
application,
host,
type,
source,
message,
[user],
statusCode,
CONVERT(VARCHAR(50), TimeUtc, 126) + 'Z' time
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


CreateElmah.sql
做了以上三步之后,程序里面的异常就能记录到数据库里面,程序再次启动的时候会自动从数据库里面去取对应的信息。

回到顶部

2、程序“吃掉”异常

如果你反编译elmah组件,你会它的原理其实就是通过配置的方式通过HttpModule注册应用程序的Error事件,然后统一处理记录。既然是注册的Application_Error事件,那么肯定就存在某些情况会吃掉异常。

(1)在程序里面try...catch...

这种情况很好理解,如果你再代码里面显示的声明了try...catch那么异常肯定不会进到Application_Error事件里面去,组件也不会记录异常。

(2)在异常捕获器里面处理了异常

除了使用try吃掉异常之外,很多系统里面都会使用异常捕获器去统一捕获异常,如果再异常捕获器里面设置过异常已经处理,组件也不会记录异常。比如:

    protected override void OnException(ExceptionContext filterContext)
{
//如果加了这一句,表示异常已经处理,不会尽到应用程序的Error事件里面去
filterContext.ExceptionHandled = true;

//........

base.OnException(filterContext);
}


这就是异常捕获器统一处理异常,既然这里标识了异常已经处理过,那么组件肯定不会再次处理。可是有些情况下,我们需要处理某些自定义异常,而对于系统异常我们还是希望组件能够记录,这种情况下怎么办呢?其实很简单,这里只需要判断一下,如果是自定义异常信息,这里就加上 filterContext.ExceptionHandled = true; 这一句,而对于其他系统异常,则统一加上这一句即可。这里还是做一个简单的演示供需要的园友参考。

public class MyException : Exception
{
public MyException(string message) : base(message)
{ }
}


满足一定条件则抛出自定义异常

public JsonResult Get()
{
if (DateTime.Now > Convert.ToDateTime("2016-12-15 10:00:00"))
{
//如果满足某些条件则抛出异常
throw new MyException("当前时间已过期");
}

return Json("OK", JsonRequestBehavior.AllowGet);
}


然后再全局异常处理里面

protected override void OnException(ExceptionContext filterContext)
{
if (filterContext.Exception is MyException)
{
//如果加了这一句,表示异常已经处理,不会进到应用程序的Error事件里面去
filterContext.ExceptionHandled = true;
}

base.OnException(filterContext);
}


这样就能达到我们系统异常记录,自定义异常不记录的目的了。

回到顶部

3、组件权限问题

关于elmah组件,被人诟病的一个重要原因就是其安全问题,如果控制不好很容易招到他人入侵。大神汤姆大叔有篇文章记录这个,有兴趣可以看看。关于我们/elmah.axd路径,我们肯定是需要做一些限制,不能允许每个人都去查看,下面从以下几个方面来完善。

(1)拒绝远程访问

在web.config里面有一个节点配置不允许远程访问。

<elmah>
<security allowRemoteAccess="false" />
</elmah>


(2)拒绝匿名用户访问

对于没有登录到系统的用户,拒绝访问。这个可以在IIS上面配置,当然,我们在web.config里面也要做相应的配置

<location path="log.axd" inheritInChildApplications="false">
<system.web>
<authorization>
<deny users="?"/>
</authorization>
</system.web>
</location>


(3)指定角色或用户访问

可以在应用程序全局配置文件Global.asax里面的认证事件里面去做判断。

protected void Application_AuthenticateRequest(object sender, EventArgs e)
{
HttpApplication app = (HttpApplication)sender;
// 处理日志权限问题
if (Request.Url.ToString().Contains("elmah.axd"))
{
var user = app.Context.User;
if (user == null)
{
Response.Write("无权限访问");
Response.End();
}
var userData = (UserInfo)user;
//管理员角色才能查看
if (!userData.UserData.IsAdmin)
{
Response.Write("无权限访问");
Response.End();
}

//或者指定用户才能访问
//if (userData.UserData.UserName!="administrator")
//{
// Response.Write("无权限访问");
//Response.End();
//}
}
}


回到顶部

四、总结

以上总结了组件Elmah组件的使用和一些常见问题的处理。有兴趣的可以看看。欢迎推荐

本文原创出处:http://www.cnblogs.com/landeanfen/

欢迎各位转载,但是未经作者本人同意,转载文章之后必须在文章页面明显位置给出作者和原文连接,否则保留追究法律责任的权利
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐