log4net中提供了一些常用的pattern string,如:%m为输出日志信息,%n为输出一新行。
但有时我们也需要记录一些其它的常用信息,如用户的id、ip、浏览器信息、agent等,这个时候便可以通过继承log4net原生的PatternLayout来实现一些自定义的扩展。
以浏览器信息为例,以下我们扩展一个记录浏览器信息的PatternConverter:
public abstract class HttpRequestPatternConverter : PatternLayoutConverter
{
protected override void Convert(TextWriter writer, LoggingEvent loggingEvent)
{
try
{
if (HttpContext.Current.Session != null)
{
HttpConvert(writer, loggingEvent, HttpContext.Current);
}
else
{
writer.Write("-");
}
}
catch (HttpException) {}
}
protected abstract void HttpConvert(TextWriter writer, LoggingEvent loggingEvent, HttpContext currentContext);
}
2.新建一个BrowserPatternConverter,继承自HttpRequestPatternConverter,并改写方法HttpConvert,在该方法中记录浏览器信息
public class BrowserPatternConverter : HttpRequestPatternConverter
{
protected override void HttpConvert(TextWriter writer, LoggingEvent loggingEvent, HttpContext currentContext)
{
var request = currentContext.Request;
writer.Write(request.Browser.Browser + " " + request.Browser.Version);
}
}
在上面的实现中记录了浏览器的名称和版本号
3. 新建一个HttpPatternLayout,继承自log4net的PatternLayout,通过构造方法扩展上面实现的BrowserPatternConverter
public class HttpPatternLayout: PatternLayout
{
public HttpPatternLayout() : base()
{
//todo:: here extend pattern layout
AddConverter("browser", typeof(BrowserPatternConverter));
}
}
4.使用扩展的PatternLayout配置log4net输出浏览器信息
<log4net>
<root>
<level value="ALL" />
<appender-ref ref="AdoNetAppender" />
<appender-ref ref="TraceAppender" />
root>
<appender name="TraceAppender" type="log4net.Appender.TraceAppender">
<layout type="Common.Tool.Extend.Log4Net.PatternLayout.HttpPatternLayout">
<conversionPattern value="%n记录时间:%d 线程ID:[%t]%n日志级别:%-5p 记录类:%c%n浏览器 : %browser%nUrl Referrer : %url_referrer%nUrl : %url%n会话ID : %session_id%n日志信息:%m%n%n" />
layout>
appender>
log4net>
在这里我配置的是TraceAppender,输出日志到vs的输出窗口。
注意此时使用的layout type要使用扩展后的HttpPatternLayout,才能成功对扩展的pattern layout进行解析处理。
5. 查看结果
Q: PatternConverter是什么
A: 在log4net中,%m%n之类的占位符称之为pattern string,这些string在解析后都会有一个对应的PatternConverter进行处理,将之转换为所需的信息。
Q: 为什么需要基类HttpRequestPatternConverter
A: 因为在如果并非在外部请求的线程中调用log4net时(如在mvc的app_start中),获取HttpRequest会报一个异常,所以先判断HttpContext的Session是否为null,如果为null,则将pattern string统一转换为”-“;不为null,则执行子类实现的HttpConvert将pattern string转换为所需信息。如果这一判断放在子类中直接完成,势必造成很多重复的代码。
Q: 为什么在TacPatternLayout的构造函数中扩展PatternConverter,AddConverter方法又是哪里来的
A: log4net存储Converter定义了两个字段,分别是s_globalRulesRegistry和m_instanceRulesRegistry。前者是静态字段,用于存储log4net本身定义的一些全局PatternConverter,我们没有办法进行修改(除非改源码);后者是实例字段,是log4net提供给我们扩展PatternConverter的字段,AddConverter方法正是log4net提供给我们操作这个字段的接口。因此我们只需在TacPatternLayout的构造方法中调用AddConverter即可以扩展PatternConverter。
Q: Converter的name为browser,为什么使用时写的是%browser
A: 在log4net中,解析pattern string时都是以%[converter_name]的形式进行解析的,所以如果定义了Converter的name为browser,配置时便要写成%browser