C#组件系列———又一款日志组件:Elmah的学习和分享
前言:好久没动笔了,都有点生疏,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帮我们一键搞定。
安装如上图的两个组件即可在MVC项目里面应用起来。注意这里有一个依赖关系Elmah.MVC依赖Elmah.corelibrary组件。
安装成功后,项目中会添加如下两个dll的引用。
2、配置组件
组件安装成功之后,会自动在我们MVC项目的Web.config里面添加如下节点:
- <sectionGroup name="elmah">
- <section name="security" requirePermission="false" type="Elmah.SecuritySectionHandler, Elmah" />
- <section name="errorLog" requirePermission="false" type="Elmah.ErrorLogSectionHandler, Elmah" />
- <section name="errorMail" requirePermission="false" type="Elmah.ErrorMailSectionHandler, Elmah" />
- <section name="errorFilter" requirePermission="false" type="Elmah.ErrorFilterSectionHandler, Elmah" />
- </sectionGroup>
- ......
- <appSettings>
- <add key="elmah.mvc.disableHandler" value="false" />
- <add key="elmah.mvc.disableHandleErrorFilter" value="false" />
- <add key="elmah.mvc.requiresAuthentication" value="false" />
- <add key="elmah.mvc.IgnoreDefaultRoute" value="false" />
- <add key="elmah.mvc.allowedRoles" value="*" />
- <add key="elmah.mvc.allowedUsers" value="*" />
- <add key="elmah.mvc.route" value="elmah" />
- <add key="elmah.mvc.UserAuthCaseSensitive" value="true" />
</appSettings>- ......
- <httpModules>
- <add name="ErrorLog" type="Elmah.ErrorLogModule, Elmah" />
- <add name="ErrorMail" type="Elmah.ErrorMailModule, Elmah" />
- <add name="ErrorFilter" type="Elmah.ErrorFilterModule, Elmah" />
- </httpModules>
- ......
- <system.webServer>
- <modules>
- <add name="ErrorLog" type="Elmah.ErrorLogModule, Elmah" preCondition="managedHandler" />
- <add name="ErrorMail" type="Elmah.ErrorMailModule, Elmah" preCondition="managedHandler" />
- <add name="ErrorFilter" type="Elmah.ErrorFilterModule, Elmah" preCondition="managedHandler" />
- </modules>
- </system.webServer>
- ......
- <elmah>
- </elmah>
除此之外,还需要手动在Web.config里面加入如下配置节点:
- <elmah>
- <security allowRemoteAccess="false" />
- <!--三种存储方式:内存、xml、数据库。存储到xml里面格式如下行-->
- <!--<errorLog type="Elmah.XmlFileErrorLog, Elmah" logPath="~/Static/Log/" />-->
- <!--<errorLog type="Elmah.SqlErrorLog, Elmah" connectionStringName="ElmahConn" />-->
- </elmah>
- <location path="elmah.axd" inheritInChildApplications="false">
- <system.web>
- <httpHandlers>
- <add verb="POST,GET,HEAD" path="elmah.axd" type="Elmah.ErrorLogPageFactory, Elmah" />
- </httpHandlers>
- </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>
需要说明的是在elmah节点里面可以配置组件支持的三种日志存储方式:
- 如果elmah节点不配置任何任何东西,表示默认组件是将日志信息保存到内存中,这种方式的弊端在于一旦系统重启,所有的异常信息都会丢失;
- <errorLog type="Elmah.XmlFileErrorLog, Elmah" logPath="~/Static/Log/" /> 表示日志保存到xml文件里面,第二个属性配置保存xml的路径;
- <errorLog type="Elmah.SqlErrorLog, Elmah" connectionStringName="ElmahConn" /> 表示日志保存到数据库里面,第二个参数对应数据库的链接字符串的name,也就是说如果配置保存到数据库,则必须要在web.config里面配置连接字符串,这里的connectionStringName就对应我们配置的连接字符串的name,并且这个时候还需要使用脚本在数据库里面新建需要的表和存储过程,这个放在后面说。
3、测试效果
通过以上安装和配置,我们即可记录异常。首先我们采用内存的方式来记录异常,先来看看效果:
- public class HomeController : Controller
- {
- public ActionResult Index()
- {
- return View();
- }
- //测试异常
- public JsonResult Get()
- {
- var a = ;
- var b = ;
- var c = b / a;
- return Json(a, JsonRequestBehavior.AllowGet);
- }
- }
然后我们前端ajax来调用
- <html>
- <head>
- <title>Index</title>
- <script src="~/Scripts/jquery-1.9.1.min.js"></script>
- <script type="text/javascript">
- $(function () {
- $("#btn").click(function () {
- $.ajax({
- type: 'get',
- url: '/Home/Get',
- data: {},
- });
- });
- });
- </script>
- </head>
- <body>
- <h1>首页</h1>
- <div>
- <button type="button" id="btn">测试异常</button>
- </div>
- </body>
- </html>
点击按钮出现异常,然后我们通过地址http://localhost:51230/elmah.axd来查看异常信息。
点击展开详细信息如下
到这一步,我们的组件就可以生效了。但是看到这个elmah.axd这个路径太恶心了,我们想要改一下,用我们自己的路径,呵呵,这个确实是可以配置的。我们再来看看web.config里面的location节点,在location节点里面其实就配置一个东西——HttpHandler,虽然有 system.web 和 system.webServer 两个节点,如果你仔细观察,其实它们是一个东西,只不过是为了兼容不同的IIS版本而写了两个配置,这一点和我们HttpHandler的配置是相同的,对于这个配置不熟悉的,可以看看博主之前的文章http://www.cnblogs.com/landeanfen/p/6000978.html。我们将location的节点改成这样:
- <location path="log.axd" inheritInChildApplications="false">
- <!--<system.web>
- <httpHandlers>
- <add verb="POST,GET,HEAD" path="log.axd" type="Elmah.ErrorLogPageFactory, Elmah" />
- </httpHandlers>
- </system.web>-->
- <system.webServer>
- <handlers>
- <add name="ELMAH" verb="POST,GET,HEAD" path="log.axd" type="Elmah.ErrorLogPageFactory, Elmah" preCondition="integratedMode" />
- </handlers>
- </system.webServer>
- </location>
然后我们通过http://localhost:51230/log.axd这个地址来访问,能达到同样的效果。博主本地使用的是IIS经典模式,所以使用的是system.webServer里面的配置,注释掉或者删掉system.web节点都不会有任何问题。
三、功能介绍
1、将日志信息保存到数据库
上述使用在内存中保存日志信息的方式,在实际项目中基本上很少会用到。除此之外,还有保存到xml和数据库两种方式。保存到xml文件这个没什么好说的,就是配置一下保存路径即可。下面就以保存到数据库的方式来看看想想介绍下。
首先我们在web.config里面加入连接字符串节点。
- <connectionStrings>
- <add name="ElmahConn" connectionString="Data source=127.0.0.1;Initial Catalog=PowerMangent;Persist Security Info=True;User ID=sa;Password=123456" providerName="System.Data.EntityClient" />
- <add name="ElmahContainer" connectionString="metadata=res://*/Elmah.csdl|res://*/Elmah.ssdl|res://*/Elmah.msl;provider=System.Data.SqlClient;provider connection string="data source=127.0.0.1;initial catalog=PowerMangent;user id=sa;password=123456;MultipleActiveResultSets=True;App=EntityFramework"" providerName="System.Data.EntityClient" />
- </connectionStrings>
然后配置elmah节点
- <elmah>
- <security allowRemoteAccess="false" />
- <errorLog type="Elmah.SqlErrorLog, Elmah" connectionStringName="ElmahConn" />
- </elmah>
最后就是在数据库创建需要的表和存储过程,官方提供了脚本,这个不用我们担心,脚本如下
- 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/
欢迎各位转载,但是未经作者本人同意,转载文章之后必须在文章页面明显位置给出作者和原文连接,否则保留追究法律责任的权利
C#组件系列———又一款日志组件:Elmah的学习和分享的更多相关文章
- C#组件系列——又一款日志组件:Elmah的学习和分享
前言:好久没动笔了,都有点生疏,12月都要接近尾声,可是这月连一篇的产出都没有,不能坏了“规矩”,今天还是来写一篇.最近个把月确实很忙,不过每天早上还是会抽空来园子里逛逛.一如既往,园子里每年这个时候 ...
- JS组件系列——又一款MVVM组件:Vue(二:构建自己的Vue组件)
前言:转眼距离上篇 JS组件系列——又一款MVVM组件:Vue(一:30分钟搞定前端增删改查) 已有好几个月了,今天打算将它捡起来,发现好久不用,Vue相关技术点都生疏不少.经过这几个月的时间,Vue ...
- JS组件系列——又一款MVVM组件:Vue(一:30分钟搞定前端增删改查)
前言:关于Vue框架,好几个月之前就听说过,了解一项新技术之后,总是处于观望状态,一直在犹豫要不要系统学习下.正好最近有点空,就去官网了解了下,看上去还不错的一个组件,就抽空研究了下.最近园子里vue ...
- C#组件系列——又一款Excel处理神器Spire.XLS,你值得拥有(二)
前言:上篇 C#组件系列——又一款Excel处理神器Spire.XLS,你值得拥有 介绍了下组件的两个功能,说不上特色,但确实能解决我们项目中的一些实际问题,这两天继续研究了下这个组件,觉得有些功能用 ...
- C#组件系列——又一款Excel处理神器Spire.XLS,你值得拥有
前言:最近项目里面有一些对Excel操作的需求,博主想都没想,NPOI呗,简单.开源.免费,大家都喜欢!确实,对于一些简单的Excel导入.导出.合并单元格等,它都没啥太大的问题,但是这次的需求有两点 ...
- JS组件系列——两种bootstrap multiselect组件大比拼
前言:今天继续来看看bootstrap的另一个组件:multiselect.记得在项目开始之前,博主项目组几个同事就使用哪些js组件展开过讨论,其中就说到了select组件,由于项目的整体风格使用的b ...
- JS组件系列——Bootstrap文件上传组件:bootstrap fileinput
前言:之前的三篇介绍了下bootstrap table的一些常见用法,发现博主对这种扁平化的风格有点着迷了.前两天做一个excel导入的功能,前端使用原始的input type='file'这种标签, ...
- JS组件系列——封装自己的JS组件,你也可以
前言:之前分享了那么多bootstrap组件的使用经验,这篇博主打算研究下JS组件的扩展和封装,我们来感受下JQuery为我们提供$.Extend的神奇,看看我们怎么自定义自己的组件,比如我们想扩展一 ...
- JS组件系列——封装自己的JS组件
前言:之前分享了那么多bootstrap组件的使用经验,这篇博主打算研究下JS组件的扩展和封装,我们来感受下JQuery为我们提供$.Extend的神奇,看看我们怎么自定义自己的组件,比如我们想扩展一 ...
随机推荐
- Taurus.MVC 2.0 开源发布:WebAPI开发教程
背景: 有用户反映,Tausus.MVC 能写WebAPI么? 能! 教程呢? 嗯,木有! 好吧,刚好2.0出来,就带上WEBAPI教程了! 开源地址: https://github.com/cyq1 ...
- favicon.ioc使用以及注意事项
1.效果 2.使用引入方法 2.1 注意事项:(把图标命名为favicon.ico,并且放在根目录下,同时使用Link标签,多重保险) 浏览器默认使用根目录下的favicon.ico 图标(如果你并没 ...
- 设置tomcat远程debug
查看端口占用情况命令: netstat -tunlp |grep 8000 tomcat 启动远程debug: startup.sh 中的最后一行 exec "$PRGDIR"/& ...
- 用javascript写星际飞机大战游戏
在github里看到了个不错的脚本游戏,决定亲自动手来写,效果如下 下面是代码的思路分享 把整个代码理解消化确实不容易,但是如果你坚持看完相信你一定会有收获 如果没兴趣可以直接点击下面的链接 复制代码 ...
- 让 asp.net 在 mac 上飞
.NET 不跨平台一直饱受争议,虽然微软前端时间放出些消息,要支持.NET跨平台的发展,但是微软一直坚持着不主动.不拒绝.不负责的三不态度,仍然用一种软件帝国的心态,折腾着一些毫无新意的东西.微软想要 ...
- C#跨平台手机应用开发工具Xamarin尝试 与Eclipse简单对比
Xamarin 支持使用C#开发基于Android.IOS.WindowsPhone应用开发,最大特点C#+跨平台,详细说明问度娘. 安装 研究 想体验研究的点击查看页面 Xamarin For Vi ...
- ASP.NET 5运行时升级到Beta5
在Visual Studio 2015 RTM和Windows 10正式发布之前,微软把开源.NET升级到了beta5,带来了一些增强和改变.和Visual Studio 2015 RC一起安装的AS ...
- ENode框架单台机器在处理Command时的设计思路
设计目标 尽量快的处理命令和事件,保证吞吐量: 处理完一个命令后不需要等待命令产生的事件持久化完成就能处理下一个命令,从而保证领域内的业务逻辑处理不依赖于持久化IO,实现真正的in-memory: 保 ...
- 【UWP开源】图片编辑器,带贴图、滤镜、涂鸦等功能
目录 说明 功能 实现原理 使用方法 效果截图 说明 最近空余时间研究了一下Win2D,它能为我们在UWP中提供一种类似GDI那样的绘图方法.就像传统Winform.MFC中那样重写OnPaint相关 ...
- 【初探Spring】------Spring IOC(二):初始化过程---简介
首先我们先来看看如下一段代码 ClassPathResource resource = new ClassPathResource("bean.xml"); DefaultList ...