在使用log4net写Web服务器端日志的时候,通常需要一些自定义的参数,比如请求的url,method,以及用户名等等,而log4net中默认的Log接口只提供了很少的参数。
在网上找的其他的解决方案,都是要重写很多地方,而且经常都是不全,让人很难理解,我反正是看不懂。。。
对俺们这些整天玩黑科技的人来说,自然要用一些不那么正常的方式来解决问题。
在这里,黑科技的意思是指绕过API或者直接修改API,其中带有一定的危险性的代码。
简单研究了一下log4net的源码(使用ILSpy反编译,野路子必备神器),发现,ILog接口的实现是log4net.Core.LogImpl,LogImpl中的Log实际方法使用了一个ILogger类型的属性Logger,这个ILogger的实现是在log4net.Repository.Hierarchy.Logger中。
在Logger类中,有一个方法,是Log,这个Log方法有两个版本,一个有三个参数,包含日志的多种参数,另一个,则是重点,它有一个参数,log4net.Core.LoggingEvent类型的参数。这里就到了关键点了。LoggingEvent中,有一个Properties属性,这个属性的内部包含了一个Hashtable。而直接使用this[key]索引器中的设置可以添加其他Key的元素。
接下来就是如何将这些预存到Properties中的数据取出来了。
log4net的基本配置:
1 <configuration> 2 <configSections> 3 <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net"/> 4 <configSections> 5 <log4net> 6 <appender name="ADONetAppender" type="log4net.Appender.ADONetAppender"> 7 <bufferSize value="1"/> 8 <connectionType value="System.Data.SqlClient.SqlConnection, System.Data, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"/> 9 <connectionString value="data source=.;initial catalog=LogDatabase;persist security info=True;user id=sa;password=******;App=Application.Logger"/> 10 <commandText value="INSERT INTO Log ([Date],[Level],[Url],[UserName],[Method],[Message],[Exception]) VALUES (@log_date, @log_level, @url,@username, @method, @message, @exception)"/> 11 <parameter> 12 <parameterName value="@log_date"/> 13 <dbType value="DateTime"/> 14 <layout type="log4net.Layout.RawTimeStampLayout"/> 15 </parameter> 16 <parameter> 17 <parameterName value="@log_level"/> 18 <dbType value="String"/> 19 <size value="50"/> 20 <layout type="log4net.Layout.PatternLayout"> 21 <conversionPattern value="%level"/> 22 </layout> 23 </parameter> 24 <parameter> 25 <parameterName value="@url"/> 26 <dbType value="String"/> 27 <size value="255"/> 28 <layout type="log4net.Layout.PatternLayout"> 29 <conversionPattern value="%property{Url}"/> 30 </layout> 31 </parameter> 32 <parameter> 33 <parameterName value="@username"/> 34 <dbType value="String"/> 35 <size value="50"/> 36 <layout type="log4net.Layout.PatternLayout"> 37 <conversionPattern value="%property{UserName}"/> 38 </layout> 39 </parameter> 40 <parameter> 41 <parameterName value="@method"/> 42 <dbType value="String"/> 43 <size value="50"/> 44 <layout type="log4net.Layout.PatternLayout"> 45 <conversionPattern value="%property{Method}"/> 46 </layout> 47 </parameter> 48 <parameter> 49 <parameterName value="@message"/> 50 <dbType value="String"/> 51 <size value="4000"/> 52 <layout type="log4net.Layout.PatternLayout"> 53 <conversionPattern value="%message"/> 54 </layout> 55 </parameter> 56 <parameter> 57 <parameterName value="@exception"/> 58 <dbType value="String"/> 59 <size value="2000"/> 60 <layout type="log4net.Layout.ExceptionLayout"/> 61 </parameter> 62 </appender> 63 <root> 64 <level value="INFO"/> 65 <appender-ref ref="ADONetAppender"/> 66 </root> 67 </log4net> 68 </configuration>
这里的%property{Method}会将Method这个Key中的数据赋值到这里。
数据库的数据表定义为:
1 CREATE TABLE [dbo].[Log] 2 ( 3 [Id] INT NOT NULL PRIMARY KEY Identity, 4 [Date] DATETIME NULL, 5 [Level] VARCHAR(50) NULL, 6 [Url] VARCHAR(255) NULL, 7 [UserName] VARCHAR(50) NULL, 8 [Method] VARCHAR(50) NULL, 9 [Message] VARCHAR(4000) NULL, 10 [Exception] VARCHAR(2000) NULL, 11 )
接下来就是程序中的代码了,在这里,我就不实现那么些类了,就写了一个WebLog静态类。
1 using log4net; 2 using log4net.Core; 3 using System; 4 5 namespace UI.Web.Log 6 { 7 public static class WebLogger 8 { 9 static ILog log = LogManager.GetLogger(typeof(WebLogger)); 10 11 static void Log(string url, string userName, string method, string message, Level level, Exception exception = null) 12 { 13 if (level == null) 14 level = Level.Debug; 15 var loggingEvent = new LoggingEvent(typeof(WebLogger), log.Logger.Repository, "WebLogger", level, message, exception); 16 loggingEvent.Properties["Url"] = url; 17 loggingEvent.Properties["UserName"] = userName; 18 loggingEvent.Properties["Method"] = method; 19 log.Logger.Log(loggingEvent); 20 } 21 22 public static void Debug(string url, string userName, string method, string message, Exception exception = null) 23 { 24 Log(url, userName, method, message, Level.Debug, exception); 25 } 26 27 public static void Info(string url, string userName, string method, string message, Exception exception = null) 28 { 29 Log(url, userName, method, message, Level.Info, exception); 30 } 31 32 public static void Warn(string url, string userName, string method, string message, Exception exception) 33 { 34 Log(url, userName, method, message, Level.Warn, exception); 35 } 36 37 public static void Error(string url, string userName, string method, string message, Exception exception) 38 { 39 Log(url, userName, method, message, Level.Error, exception); 40 } 41 42 public static void Fatal(string url, string userName, string method, string message, Exception exception) 43 { 44 Log(url, userName, method, message, Level.Fatal, exception); 45 } 46 } 47 }
在这里,就是将数据添加到loggingEvent对象中。
这样,就可以将自定义的数据填充到数据库中了。