使用log4net完成程序异常日志记录(使用SQLite数据库记录和普通文本记录)

简介:

     在前端时间开发的时候由于需要将异常保存到数据库中,所以就到网上搜了下专门的日志记录工具,一搜果然很多,比如:log4net,NLog,EntLib Logging等等,但是还是log4net名气最大,所以就下载下来试用了一番,果然很方便,其涵盖了所有常用的日志记录方式具体的可以看下表:

AdoNetAppender 将日志记录到数据库中。可以采用SQL和存储过程两种方式。

AnsiColorTerminalAppender 将日志高亮输出到ANSI终端。

AspNetTraceAppender 能用asp.net中Trace的方式查看记录的日志。

BufferingForwardingAppender 在输出到子Appenders之前先缓存日志事件。

ConsoleAppender 将日志输出到应用程序控制台。

EventLogAppender 将日志写到Windows Event Log。

FileAppender 将日志输出到文件。

ForwardingAppender 发送日志事件到子Appenders。

LocalSyslogAppender 将日志写到local syslog service (仅用于UNIX环境下)。

MemoryAppender 将日志存到内存缓冲区。

NetSendAppender 将日志输出到Windows Messenger service.这些日志信息将在用户终端的对话框中显示。

OutputDebugStringAppender 将日志输出到Debuger,如果程序没有Debuger,就输出到系统Debuger。如果系统Debuger也不可用,将忽略消息。

RemoteSyslogAppender 通过UDP网络协议将日志写到Remote syslog service。

RemotingAppender 通过.NET Remoting将日志写到远程接收端。

RollingFileAppender 将日志以回滚文件的形式写到文件中。

SmtpAppender 将日志写到邮件中。

SmtpPickupDirAppender 将消息以文件的方式放入一个目录中,像IIS SMTP agent这样的SMTP代理就可以阅读或发送它们。

TelnetAppender 客户端通过Telnet来接受日志事件。

TraceAppender 将日志写到.NET trace 系统。

UdpAppender 将日志以无连接UDP数据报的形式送到远程宿主或用UdpClient的形式广播。

怎么样?看了是不是很心动?如果想学习log4net的话可以看下园子里这位兄弟写的文章:如何使用Log4net创建日志及简单扩展,里面详细介绍了如何使用log4net记录日志,本文呢仅仅就对异常出现后如何记录下来进行讨论,同时也是做个记录方便日后写微软企业库 学习之路有所引用。

在日常的项目开发过程中总会碰到各种各样的异常,我们总是希望异常能第1时间捕获,同时能清楚的知道异常信息、异常发生的时间、发生异常的位置,这样我们好立刻追踪到其发生点来具体解决问题,log4net就很好的帮我们解决了这个问题

现在我们就开始对如何进行异常日志的记录做个分析:

首先是具体的config配置文件,主要分为2个日志记录器,一个是LogToSqlite(记录到SQLite数据库),另外一个是LogToFile(记录到相应的文件),如果要更换到其他数据库也是一样的,值需要更改connectionTypeconnectionString就可以了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
   <configSections>
     <section name="log4net" type="System.Configuration.IgnoreSectionHandler, log4net" />
   </configSections>
   <log4net>
     <appender name="ADONetAppender" type="log4net.Appender.ADONetAppender,log4net">
       <!--BufferSize为缓冲区大小-->
       <bufferSize value="100" />
 
       <!--<param name="BufferSize" value="2" />-->
       <!--引用-->
       <connectionType value="System.Data.SQLite.SQLiteConnection, System.Data.SQLite, Version=1.0.65.0, Culture=neutral, PublicKeyToken=db937bc2d44ff139" />
       <!--连接字符串-->
       <connectionString value="data source=F:\\项目开发\\log4netDemo\\WebApplication1\\bin\\log.db3" />
       <!--插入语句-->
       <commandText value="insert into [Log2] ([Author],[LogDate],[Log_Level],[Message],[Location],[Exception]) Values(@Author,@Date,@Level,@Message,@Location,@Exception);" />
       <commandType value="Text"/>
       <!--操作者,暂时没用到-->
       <parameter>
         <parameterName value="@Author" />
         <dbType value="String" />
         <layout type="log4net.Layout.PatternLayout">
           <conversionPattern value="%property{Operator}" />
         </layout>
       </parameter>
       <!--记录时间-->
       <parameter>
         <parameterName value="@Date" />
         <dbType value="DateTime" />
         <layout type="log4net.Layout.RawTimeStampLayout" />
       </parameter>
       <!--日志等级-->
       <parameter>
         <parameterName value="@Level" />
         <dbType value="String" />
         <layout type="log4net.Layout.PatternLayout">
           <conversionPattern value="%level" />
         </layout>
       </parameter>
       <!--异常消息-->
       <parameter>
         <parameterName value="@Message" />
         <dbType value="String" />
         <layout type="log4net.Layout.PatternLayout">
           <conversionPattern value="%message" />
         </layout>
       </parameter>
       <!--异常位置-->
       <parameter>
         <parameterName value="@Location" />
         <dbType value="String" />
         <layout type="log4net.Layout.PatternLayout">
           <conversionPattern value="%location" />
         </layout>
       </parameter>
       <!--错误-->
       <parameter>
         <parameterName value="@Exception" />
         <dbType value="String" />
         <layout type="log4net.Layout.PatternLayout">
           <conversionPattern value="%exception" />
         </layout>
       </parameter>
     </appender>
     <appender name="LogAllToFile" type="log4net.Appender.RollingFileAppender,log4net">
       <!--输出格式
                      每种转换符号都以%开始,后面跟着一个格式符号和换符号。
                      %-数字 :该项的最小长度,小于最小长度的用空格填充
                      %m(message):输出的日志消息
                      %n(new line):换行
                      %d(datetime):输出当前语句运行的时刻
                      %r(run time):输出程序从运行到执行到当前语句时消耗的毫秒数
                      %t(thread id):当前语句所在的线程ID
                      %p(priority): 日志的当前优先级别,即DEBUG、INFO、WARN…等
                      %c(class):当前日志对象的名称,
                      %L(line ):输出语句所在的行号
                      %F(file name):输出语句所在的文件名
                      %logger 日志名称
                  -->
       <param name="File" value="log\"/>
       <param name="AppendToFile" value="true"/>
       <param name="MaxSizeRollBackups" value="100"/>
       <param name="MaximumFileSize" value="1KB"/>
       <param name="StaticLogFileName" value="false"/>
       <param name="DatePattern" value="yyyyMMdd&quot;.log&quot;"/>
       <param name="RollingStyle" value="Date"/>
       <layout type="log4net.Layout.PatternLayout">
         <param name="ConversionPattern" value="记录时间:%date 线程ID:[%thread] 日志级别:%-5level 记录类:%logger 操作者ID:%property{Operator} 操作类型:%property{ActionType}%n当前机器名:%property%n当前机器名及登录用户:%username %n记录位置:%location%n消息描述:%property{Message}%n异常:%exception%n消息:%message%newline%n%n" />
       </layout>
     </appender>
     <logger name="LogToSqlite">
       <level value="ERROR"/>
       <appender-ref ref="ADONetAppender"/>
     </logger>
     <!--<logger name="LogToFile">
       <level value="ALL"/>
       <appender-ref ref="LogAllToFile"/>
     </logger>-->
     <!--所有logger的基础,root的设置在所有logger中都起作用。
         当在root和logger中重复设定相同的appender时,你会发现同一日志信息将被记录两次。-->
     <!--<root>
       <level value="ERROR"/>
       ALL, DEBUG, INFO, WARN, ERROR, FATAL, OFF
       <appender-ref ref="LogAllToFile"/>
       <appender-ref ref="ADONetAppender"/>
     </root>-->
   </log4net>
</configuration>

在普通的C/S系统下都会有个程序入口,我们可以在这个入口点做文章,在程序启动时添加2个事件来监听异常:

1
2
Application.ThreadException += new  System.Threading.ThreadExceptionEventHandler(Application_ThreadException);
AppDomain.CurrentDomain.UnhandledException += new  UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);

这2个事件是当执行Application.Run方法的线程发生未捕获异常时,触发 Application.ThreadException和如果 handler 抛出异常,或者不在 UI 线程中发生异常,将触发 
AppDomain.UnhandledException事件。

其入口点代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
static  class  Program
     {
         /// <summary>
         /// 应用程序的主入口点。
         /// </summary>
         [STAThread]
         static  void  Main()
         {
             //若不在AssemblyInfo.cs配置[assembly: log4net.Config.XmlConfigurator(ConfigFile = "WindowsFormsApplication1.exe.config", Watch = true)]
             //可使用一下代码来读取log4net配置文件
             string  assemblyFilePath = Assembly.GetExecutingAssembly().Location;
             string  assemblyDirPath = Path.GetDirectoryName(assemblyFilePath);
             string  configFilePath = assemblyDirPath + " \\log4net.xml" ;
             log4net.Config.XmlConfigurator.ConfigureAndWatch( new  FileInfo(configFilePath));
 
             //异常
             Application.ThreadException += new  System.Threading.ThreadExceptionEventHandler(Application_ThreadException);
             AppDomain.CurrentDomain.UnhandledException += new  UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);
             Application.EnableVisualStyles();
             Application.SetCompatibleTextRenderingDefault( false );
             Application.Run( new  Form1());
         }
 
         static  void  CurrentDomain_UnhandledException( object  sender, UnhandledExceptionEventArgs e)
         {
             if  (e.ExceptionObject is  System.Exception)
             {
                 HandleException((System.Exception)e.ExceptionObject);
             }
         }
 
         static  void  Application_ThreadException( object  sender, System.Threading.ThreadExceptionEventArgs e)
         {
             HandleException(e.Exception);
         }
 
         public  static  void  HandleException(Exception ex)
         {
             log4net.ILog log = (log4net.ILog)log4net.LogManager.GetLogger( "LogToSqlite" );
             log.Error(ex.Message, ex);
         }
     }

而在B/S程序下面我们则可以利用页面级别的Page_Error事件或者整个应用程序级别的Application_Error事件(本例子中采用Page_Error处理),同时我们还要做一步处理就是在Global.asax中的Application_Start添加log4net的配置读取,或者也可以在AssemblyInfo.cs添加和WinFrom一样的配置,不过如果仅限于WebForm,而Web网站就只能在Global中配置,所以这边我们就采用通用配置:

1
2
3
4
protected  void  Application_Start( object  sender, EventArgs e)
         {
             log4net.Config.XmlConfigurator.ConfigureAndWatch( new  System.IO.FileInfo(Server.MapPath( "~" ) + @"\log4net.xml" ));
         }

为了方便每个页面进行异常捕获我们建立一个PageBase,让所有的页面都继承这个PageBase,这样只需在PageBase中写一个Page_Error就可以了,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
public  class  PageBase : System.Web.UI.Page
     {
         protected  void  Page_Error( object  sender, EventArgs args)
         {
             //获取最新的异常信息
             var  ex = Server.GetLastError();
             //记录异常信息
             log4net.ILog log = (log4net.ILog)log4net.LogManager.GetLogger( "LogToSqlite" );
             log.Error(ex.Message, ex);
             //清空异常信息
             Server.ClearError();
         }
     }

这样在页面中就可以进行异常捕获及记录了。具体的代码可以下载查看。

下载地址:点我下载



本文转自kyo-yo博客园博客,原文链接:http://www.cnblogs.com/kyo-yo/archive/2010/06/11/Use-Log4net-To-Log-Exception.html,如需转载请自行联系原作者


相关实践学习
通过日志服务实现云资源OSS的安全审计
本实验介绍如何通过日志服务实现云资源OSS的安全审计。
目录
相关文章
|
5月前
|
SQL 存储 监控
SQL日志优化策略:提升数据库日志记录效率
通过以上方法结合起来运行调整方案, 可以显著地提升SQL环境下面向各种搜索引擎服务平台所需要满足标准条件下之数据库登记作业流程综合表现; 同时还能确保系统稳健运行并满越用户体验预期目标.
324 6
|
6月前
|
缓存 Java 应用服务中间件
Spring Boot配置优化:Tomcat+数据库+缓存+日志,全场景教程
本文详解Spring Boot十大核心配置优化技巧,涵盖Tomcat连接池、数据库连接池、Jackson时区、日志管理、缓存策略、异步线程池等关键配置,结合代码示例与通俗解释,助你轻松掌握高并发场景下的性能调优方法,适用于实际项目落地。
1106 5
|
12月前
|
存储 缓存 监控
【YashanDB数据库】数据库运行正常,日志出现大量错误metadata changed
数据库运行正常,日志出现大量错误metadata changed
|
7月前
|
存储 关系型数据库 数据库
【赵渝强老师】PostgreSQL数据库的WAL日志与数据写入的过程
PostgreSQL中的WAL(预写日志)是保证数据完整性的关键技术。在数据修改前,系统会先将日志写入WAL,确保宕机时可通过日志恢复数据。它减少了磁盘I/O,提升了性能,并支持手动切换日志文件。WAL文件默认存储在pg_wal目录下,采用16进制命名规则。此外,PostgreSQL提供pg_waldump工具解析日志内容。
670 0
|
存储 消息中间件 Kafka
聊一聊日志背后的抽象
本文从思考日志的本质开始,一览业界对日志使用的最佳实践,然后尝试给出分布式存储场景下对日志模块的需求抽象,最后是技术探索路上个人的一点点感悟。
712 81
|
存储 SQL 关系型数据库
MySQL日志详解——日志分类、二进制日志bin log、回滚日志undo log、重做日志redo log
MySQL日志详解——日志分类、二进制日志bin log、回滚日志undo log、重做日志redo log、原理、写入过程;binlog与redolog区别、update语句的执行流程、两阶段提交、主从复制、三种日志的使用场景;查询日志、慢查询日志、错误日志等其他几类日志
1007 35
MySQL日志详解——日志分类、二进制日志bin log、回滚日志undo log、重做日志redo log
|
存储 缓存 关系型数据库
图解MySQL【日志】——Redo Log
Redo Log(重做日志)是数据库中用于记录数据页修改的物理日志,确保事务的持久性和一致性。其主要作用包括崩溃恢复、提高性能和保证事务一致性。Redo Log 通过先写日志的方式,在内存中缓存修改操作,并在适当时候刷入磁盘,减少随机写入带来的性能损耗。WAL(Write-Ahead Logging)技术的核心思想是先将修改操作记录到日志文件中,再择机写入磁盘,从而实现高效且安全的数据持久化。Redo Log 的持久化过程涉及 Redo Log Buffer 和不同刷盘时机的控制参数(如 `innodb_flush_log_at_trx_commit`),以平衡性能与数据安全性。
729 5
图解MySQL【日志】——Redo Log
|
12月前
|
监控 Java 应用服务中间件
Tomcat log日志解析
理解和解析Tomcat日志文件对于诊断和解决Web应用中的问题至关重要。通过分析 `catalina.out`、`localhost.log`、`localhost_access_log.*.txt`、`manager.log`和 `host-manager.log`等日志文件,可以快速定位和解决问题,确保Tomcat服务器的稳定运行。掌握这些日志解析技巧,可以显著提高运维和开发效率。
1353 13
|
SQL 关系型数据库 MySQL
MySQL事务日志-Undo Log工作原理分析
事务的持久性是交由Redo Log来保证,原子性则是交由Undo Log来保证。如果事务中的SQL执行到一半出现错误,需要把前面已经执行过的SQL撤销以达到原子性的目的,这个过程也叫做"回滚",所以Undo Log也叫回滚日志。
785 7
MySQL事务日志-Undo Log工作原理分析
|
开发框架 安全 .NET
【Azure Developer】.NET Aspire 项目本地调试遇 Grpc.Core.RpcException 异常( Error starting gRPC call ... )
Error starting gRPC call. HttpRequestException: The SSL connection could not be established, see inner exception. AuthenticationException: The remote certificate is invalid because of errors in the certificate chain: UntrustedRoot
410 12