日志记录往往是软件开发周期中的重要组成部分。它具有以下几个优点:它可以提供应用程序运行时的精确环境,可供开发人员尽快找到应用程序中的Bug;一旦在程序中加入了Log 输出代码,程序运行过程中就能生成并输出日志信息而无需人工干预。另外,日志信息可以输出到不同的地方(控制台,文件等)以备以后研究之用。
- 从下载log4net的源代码。解压软件包后,在解压的src目录下将log4net.sln载入Visual Studio .NET,编译后可以得到log4net.dll。
或直接使用NuGet
log4net 有四个主要的组件,分别是Logger(记录器), Repository(库), Appender(附着器)以及 Layout(布局)
- 添加-新建配置文件Log4Net.config,并在文件属性中“复制到输出目录”选中“始终复制”,文件内容如下:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<!--Log日记配置, 申明一个名为“log4net“的自定义配置节-->
<configSections>
<section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net"/>
</configSections>
<log4net>
<!--程序中只实例化一个logger,名字为LogHelper-->
<logger name ="LogHelper">
<!--输出所有信息-->
<level value="ALL"/>
<!--介质名称,对应错误,信息和控制台-->
<appender-ref ref="ErrorRollingFileAppender" />
<appender-ref ref="InfoRollingFileAppender" />
</logger>
<!--Info介质设置-->
<appender name="InfoRollingFileAppender" type="log4net.Appender.RollingFileAppender">
<!--定义文件存放位置,Info和Error保存在不同的文件夹,方便理解-->
<appendToFile value="true"/>
<layout type="log4net.Layout.PatternLayout"> <!--输出格式-->
<!--样例:
记录时间:2016-10-11 14:03:00,540
线程ID:[1]
日志级别: INFO 错误内容-->
<conversionPattern value="记录时间:%date %n线程ID:[%thread] %n
日志级别: %level %message %newline "/>
</layout>
<!-- INFO输出的等级-->
<filter type="log4net.Filter.LevelRangeFilter">
<param name="LevelMin" value="INFO" />
<param name="LevelMax" value="INFO" />
</filter>
</appender>
<!--Error介质设置-->
<appender name="ErrorRollingFileAppender" type="log4net.Appender.RollingFileAppender">
<file value="Log\\Error\\"/>
<appendToFile value="true"/>
<rollingStyle value="Date"/>
<datePattern value="yyyyMMdd'.txt'"/>
<staticLogFileName value="false"/>
<param name="MaxSizeRollBackups" value="100"/>
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="记录时间:%date %n线程ID:[%thread] %n
日志级别: %level %message%newline "/>
</layout>
<!--设置输出的等级 ERROR-->
<filter type="log4net.Filter.LevelRangeFilter">
<param name="LevelMin" value="ERROR" />
<param name="LevelMax" value="ERROR" />
</filter>
</appender>
</log4net>
</configuration>
3、在AssemblyInfo.cs中添加代码,用于调用Log4Net.config:
[assembly: log4net.Config.XmlConfigurator(ConfigFile = "Log4Net.config", Watch = true)]
或者在程序中添加
log4net.Config.XmlConfigurator.Configure();
4、实现LogHelper,代码如下:
public class LogHelper
{
private static ILog log = log4net.LogManager.GetLogger("LogHelper");
/// <summary>
/// 整理异常信息
/// </summary>
/// <param name="error"></param>
/// <returns></returns>
private static string Msg(Exception error)
{
string str = "";
if (error != null)
{
str = string.Format("异常类型:{0}\r\n异常消息:{1}\r\n异常信息:{2}\r\n",
error.GetType().Name, error.Message, error.StackTrace);
}
return str;
}
/// <summary>
/// 输出异常信息
/// </summary>
/// <param name="t"></param>
/// <param name="ex"></param>
public static void LogError(Exception ex)
{
log.Error(Msg(ex));
}
/// <summary>
/// 输出日记信息
/// </summary>
/// <param name="str"></param>
public static void LogInfo(string str)
{
log.Info(str);
}
}
Log4net框架定义了一个叫做LogManager的类,用来管理所有的logger对象。它有一个GetLogger()静态方法,用我们提供的名字参数来检索已经存在的Logger对象。如果框架里不存在该Logger对象,它也会为我们创建一个Logger对象。LogManager.Exists(”name”)只获取而不进行创建,不存在则返回null;
ILog log = log4net.LogManager.GetLogger("logger-name");
Repository
Repository主要用于负责日志对象组织结构的维护。在log4net的以前版本中,框架仅支持分等级的组织结构(hierarchical organization)。这种等级结构本质上是库的一个实现,并且定义在log4net.Repository.Hierarchy 名字空间中。要实现一个Repository,需要实现log4net.Repository.ILoggerRepository 接口。但是通常并不是直接实现该接口,而是以log4net.Repository.LoggerRepositorySkeleton为基类继承。体系库 (hierarchical repository )则由log4net.Repository.Hierarchy.Hierarchy类实现。
如果你是个log4net框架的使用者,而非扩展者,那么你几乎不会在你的代码里用到Repository的类。相反的,你需要用到LogManager类来自动管理库和日志对象。
Appender
一个好的日志框架应该能够产生多目的地的输出。比如说输出到控制台或保存到一个日志文件。log4net 能够很好的满足这些要求。它使用一个叫做Appender的组件来定义输出介质。正如名字所示,这些组件把它们附加到Logger日志组件上并将输出传递到输出流中。你可以把多个Appender组件附加到一个日志对象上。 Log4net框架提供了几个Appender组件。关于log4net提供的Appender组件的完整列表可以在log4net框架的帮助手册中找到。有了这些现成的Appender组件,一般来说通过配置Filters和Layout来实现日志的过滤和输出格式。但是如果你愿意,可以从log4net.Appender.AppenderSkeleton类继承。
通过设置Type来配置输出方式:
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 系统。
UdpAppende将日志以无连接UDP数据报的形式送到远程宿主或用UdpClient的形式广播。
Appender Filters
一个Appender 对象缺省地将所有的日志事件传递到输出流。Appender的过滤器(Appender Filters) 可以按照不同的标准过滤日志事件。在log4net.Filter的名字空间下已经有几个预定义的过滤器。使用这些过滤器,你可以按照日志级别范围过滤日志事件,或者按照某个特殊的字符串进行过滤。你可以在API的帮助文件中发现更多关于过滤器的信息。
过滤器通常有以下几种:
DenyAllFilter 阻止所有的日志事件被记录
LevelMatchFilter 只有指定等级的日志事件才被记录
LevelRangeFilter 日志等级在指定范围内的事件才被记录
LoggerMatchFilter 与Logger名称匹配,才记录
PropertyFilter 消息匹配指定的属性值时才被记录
StringMathFilter 消息匹配指定的字符串才被记录
Layout
Layout 组件用于向用户显示最后经过格式化的输出信息。输出信息可以以多种格式显示,主要依赖于我们采用的Layout组件类型。可以是线性的或一个XML文件。Layout组件和一个Appender组件一起工作。API帮助手册中有关于不同Layout组件的列表。一个Appender对象,只能对应一个Layout对象。要实现你自己的Layout类,你需要从log4net.Layout.LayoutSkeleton类继承,它实现了ILayout接口。
Layout用于控制Appender的输出格式,可以是线性的也可以是XML。
最常用的Layout格式是PatternLayout,其次是SimpleLayout,RawTimeStampLayout和ExceptionLayout。然后还有IRawLayout,XMLLayout等几个,使用较少。Layout可以自己实现,需要从log4net.Layout.LayoutSkeleton类继承,来输出一些特殊需要的格式,在后面扩展时就重新实现了一个Layout。
SimpleLayout简单输出格式,只输出日志级别与消息内容。
RawTimeStampLayout 用来格式化时间,在向数据库输出时会用到。
样式如“yyyy-MM-dd HH:mm:ss“
ExceptionLayout需要给Logger的方法传入Exception对象作为参数才起作用,否则就什么也不输出。输出的时候会包含Message和Trace。
PatterLayout使用最多的一个Layout,能输出的信息很多,使用方式可参见上面例子中的配置文件。PatterLayout的格式化字符串见文后附注8.1。
Loggers
Logger是直接和应用程序交互的组件。Logger只是产生日志,然后由它引用的Appender记录到指定的媒介,并由Layout控制输出格式。
Logger提供了多种方式来记录一个日志消息,也可以有多个Logger同时存在。每个实例化的Logger对象对被log4net作为命名实体(Named Entity)来维护。log4net使用继承体系,也就是说假如存在两个Logger,名字分别为a.b.c和a.b。那么a.b就是a.b.c的祖先。每个Logger都继承了它祖先的属性。
所有的Logger都从Root继承,Root本身也是一个Logger。
日志的等级(level),它们由高到底分别为:
OFF > FATAL > ERROR > WARN > INFO > DEBUG > ALL
高于等级设定值方法(如何设置参见“配置文件详解”)都能写入日志,Off所有的写入方法都不写到日志里,ALL则相反。例如当我们设成Info时,logger.Debug就会被忽略而不写入文件,但是FATAL,ERROR,WARN,INFO会被写入,因为他们等级高于INFO。
在具体写日志时,一般可以这样理解日志等级:
FATAL(致命错误):记录系统中出现的能使用系统完全失去功能,服务停止,系统崩溃等使系统无法继续运行下去的错误。例如,数据库无法连接,系统出现死循环。
ERROR(一般错误):记录系统中出现的导致系统不稳定,部分功能出现混乱或部分功能失效一类的错误。例如,数据字段为空,数据操作不可完成,操作出现异常等。
WARN(警告):记录系统中不影响系统继续运行,但不符合系统运行正常条件,有可能引起系统错误的信息。例如,记录内容为空,数据内容不正确等。
INFO(一般信息):记录系统运行中应该让用户知道的基本信息。例如,服务开始运行,功能已经开户等。
DEBUG(调试信息):记录系统用于调试的一切信息,内容或一些关键数据内容的输出。
Logger实现的ILog接口,ILog定义了5个方法(Debug,Inof,Warn,Error,Fatal)分别对不同的日志等级记录日志。这5个方法还有5个重载。以Debug为例说明一下,其它的和它差不多。
ILog中对Debug方法的定义如下:
void Debug(object message);
void Debug(object message, Exception ex);
还有一个布尔属性:bool IsDebugEnabled { get; }
如果使用Debug(object message, Exception ex),则无论Layout中是否定义了%exception,默认配置下日志都会输出Exception。包括Exception的Message和Trace。如果使用Debug(object message),则日志是不会输出Exception。
Object Renders
它将告诉logger如何把一个对象转化为一个字符串记录到日志里。(ILog中定义的接口接收的参数是Object,而不是String。)
例如你想把Orange对象记录到日志中,但此时logger只会调用Orange默认的ToString方法而已。所以要定义一个OrangeRender类实现log4net.ObjectRender.IObjectRender接口,然后注册它(我们在本文中的扩展不使用这种方法,而是直接实现一个自定义的Layout)。这时logger就会知道如何把Orange记录到日志中了。
<log4net>
所有的配置都在<log4net>元素里定义。
支持的属性:
debug | 可选,取值是true或false,默认是false。设置为true,开启log4net的内部调试。 |
update | 可选,取值是Merge(合并)或Overwrite(覆盖),默认值是Merge。设置为Overwrite,在提交配置的时候会重置已经配置过的库。 |
threshold | 可选,取值是repository(库)中注册的level,默认值是ALL。 |
支持的子元素节点:
appender | 1或多个 |
logger | 0或多个 |
renderer | 0或多个 |
root | 最多一个 |
param | 0或多个 |
<root>
实际上就是一个根logger,所有其它logger都默认继承它,如果配置文件里没有显式定义,则框架使用根日志中定义的属性。root元素没有属性。
支持的子元素:
appender-ref | 0个或多个,要引用的appender的名字。 |
level | 最多一个。 只有在这个级别或之上的事件才会被记录。 |
param | 0个或多个, 设置一些参数。 |
<logger>
在框架的体系里,所有的日志对象<logger>都是根日志(root logger)的后代。 因此如果一个日志对象没有在配置文件里显式定义,则框架使用根日志中定义的属性。在<root>标签里,可以定义level级别值和Appender的列表。如果没有定义LEVEL的值,则缺省为DEBUG。可以通过<appender-ref>标签定义日志对象使用的Appender对象。<appender-ref>声明了在其他地方定义的Appender对象的一个引用。在一个logger对象中的设置会覆盖根日志的设置。而对Appender属性来说,子日志对象则会继承父日志对象的Appender列表。这种缺省的行为方式也可以通过显式地设定<logger>标签的additivity属性为false而改变。
<root>不显式申明时使用默认的配置。在使用时不定义<root>,自定义多个<logger>,在程序中记录日志时直接使用<logger>的name来查找相应的<logger>,这样更灵活。例如:
<!--同时写两个文件和数据库-->
<logger name="ReflectionLayout">
<level value="DEBUG"/>
<appender-ref ref="HashtableLayout"/>
<appender-ref ref="ReflectionLayout"/>
<appender-ref ref="ADONetAppender"/>
</logger>
支持的属性:
name | 必须的,logger的名称 |
additivity | 可选,取值是true或false,默认值是true。设置为false时将阻止父logger中的appender。 |
支持的子元素:
appender-ref | 0个或多个, 要引用的appender的名字。 |
level | 最多一个。 只有在这个级别或之上的事件才会被记录。 |
param | 0个或多个, 设置一些参数。 |
<appender>
日志输出方式,只能作为log4net的子元素。name属性必须唯一,type属性必须指定。
支持的属性:
name | 必须的, Appender对象的名称 |
type | 必须的, Appender对象的输出类型 |
支持的子元素:
appender-ref | 0个或多个 允许此appender引用其他appender,并不是所以appender类型都支持。 |
filter | 0个或多个 定义此app使用的过滤器。 |
layout | 最多一个 定义appender使用的输出格式。 |
param | 0个或多个 设置Appender类中对应的属性的值。 |
实际上<appender>所能包含的子元素远不止上面4个。
配置设置参考官方网站:
<layout>
布局,只能作为<appender>的子元素。
支持的属性:
type | 必须的,Layout的类型 |
支持的子元素:
conversionPattern | 0个或多个, 设置一些参数。 |
<filter>
过滤器,只能作为<appender>的子元素。
支持的属性:
type | 必须的,Filter的类型 |
支持的子元素:
param | 0个或多个, 设置一些参数。 |
<param>
<param>元素可以是任何元素的子元素。
支持的属性:
name | 必须的,取值是父对象的参数名。 |
value | 可选的,value和type中,必须有一个属性被指定。value是一个能被转化为参数值的字符串。 |
type | 可选的,value和type中,必须有一个属性被指定。type是一个类型名,如果type不是在log4net程序集中定义的,就需要使用全名。 |
支持的子元素:
param | 0个或多个, 设置一些参数。 |
示例——写入回滚文件:
<appender name="ReflectLayout" type="log4net.Appender.RollingFileAppender">
<!--日志文件路径,文件夹不存在则新建-->
<!--按文件大小方式输出时,在这里指定文件名,并且当天的日志在下一天时在文件名后自动追加当天日期形成新文件。--> 如<file value="Log\log.txt"/>
<!—按照日期形式输出时,直接连接元素DatePattern的value形成文件路径。此处使用这种方式 -->
<param name="File" value=" Log\\Info\\"/> 等价<file value="Log\\Info\\"/>
<!--是否追加到文件-->
<param name="AppendToFile" value="true"/>
<!--记录日志写入文件时,不锁定文本文件,防止多线程时不能写Log -->
<lockingModel type="log4net.Appender.FileAppender+MinimalLock"/>
<!—使用Unicode编码-->
<Encoding value="UTF-8" />
<!--最多产生的日志文件数,超过则只保留最新的n个。设定值value="-1"为不限文件数-->
<param name="MaxSizeRollBackups" value="10" />
<!--是否只写到一个文件中-->
<param name="StaticLogFileName" value="false" />
<!--按照何种方式产生多个日志文件(日期[Date],文件大小[Size],混合[Composite])-->
<param name="RollingStyle" value="Composite" />
<!--按日期产生文件夹和文件名[在日期方式与混合方式下使用]-->
<!—此处按日期产生文件夹,文件名固定。注意" 的位置-->
<param name="DatePattern" value="yyyy-MM-dd/"ReflectionLayout.log""/>
<!—这是按日期产生文件夹,并在文件名前也加上日期-->
<param name="DatePattern" value="yyyyMMdd/yyyyMMdd"-TimerServer.log"" />
<!—这是先按日期产生文件夹,再形成下一级固定的文件夹—>
<param name="DatePattern" value="yyyyMMdd/"TimerServer/TimerServer.log"" />
<!--每个文件的大小。只在混合方式与文件大小方式下使用。
超出大小后在所有文件名后自动增加正整数重新命名,数字最大的最早写入。
可用的单位:KB|MB|GB。不要使用小数,否则会一直写入当前日志-->
<param name="maximumFileSize" value="500KB" />
<!--计数类型为1,2,3…-->
<param name="CountDirection" value="1"/><!—过滤设置,LevelRangeFilter为使用的过滤器。 -->
<filter type="log4net.Filter.LevelRangeFilter">
<param name="LevelMin" value="DEBUG" />
<param name="LevelMax" value="WARN" />
</filter>
<!--记录的格式。一般用log4net.Layout.PatternLayout布局-->
<!—此处用继承了log4net.Layout.PatternLayout的自定义布局,TGLog.ExpandLayout2
为命名空间。%property{Operator}、%property{Action}是自定义的输出-->
<layout type="TGLog.ExpandLayout2.ReflectionLayout,TGLog">
<param name="ConversionPattern" value="记录时间:%date
线程ID:[%thread] 日志级别:%-5level
记录类:%logger
操作者ID:%property{Operator}
操作类型:%property{Action}%n
当前机器名:%property%n
当前机器名及登录用户:%username %n
记录位置:%location%n
消息描述:%property{Message}%n
异常:%exception%n
消息:%message%newline%n%n" />
</layout>
</appender>
写入SQL数据库
需要在相应的数据库中准备好一张表,创建语句如下:
CREATE TABLE [Log] (
[ID] [int] IDENTITY (1, 1) NOT NULL ,
[Date] [datetime] NOT NULL ,
[Thread] [varchar] (100) COLLATE Chinese_PRC_CI_AS NULL ,
[Level] [varchar] (100) COLLATE Chinese_PRC_CI_AS NULL ,
[Logger] [varchar] (200) COLLATE Chinese_PRC_CI_AS NULL ,
[Operator] [int] NULL ,
[Message] [text] COLLATE Chinese_PRC_CI_AS NULL ,
[ActionType] [int] NULL ,
[Operand] [varchar] (300) COLLATE Chinese_PRC_CI_AS NULL ,
[IP] [varchar] (20) COLLATE Chinese_PRC_CI_AS NULL ,
[MachineName] [varchar] (100) COLLATE Chinese_PRC_CI_AS NULL ,
[Browser] [varchar] (50) COLLATE Chinese_PRC_CI_AS NULL ,
[Location] [text] COLLATE Chinese_PRC_CI_AS NULL ,
[Exception] [text] COLLATE Chinese_PRC_CI_AS NULL)
<appender name="ADONetAppender" type="log4net.Appender.ADONetAppender">
<!--BufferSize为缓冲区大小,只有日志记录超设定值才会一块写入到数据库-->
<bufferSize value="10"/><!—或写为<param name="BufferSize" value="10"/>-->
<!--引用-->
<connectionType value="System.Data.SqlClient.SqlConnection, System.Data, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
<!--连接数据库字符串-->
<connectionString value="data source=.;initial catalog=Test;integrated security=false;persist security info=True;User ID=sa;Password=;" />
<!--插入到表Log-->
<commandText value="INSERT INTO Log ([Date],[Thread],[Level],[Logger],[Operator],[Message],[ActionType],[Operand],[IP],[MachineName],[Browser],[Location],[Exception]) VALUES (@log_date, @thread, @log_level,@logger,@operator,@message,@action_type,@operand,@ip,@machineName,@browser,@location,@exception)"/>
<!—日志记录时间,RawTimeStampLayout为默认的时间输出格式 -->
<parameter>
<parameterName value="@log_date" />
<dbType value="DateTime" />
<layout type="log4net.Layout.RawTimeStampLayout" />
</parameter>
<!--线程号-->
<parameter>
<parameterName value="@thread" />
<dbType value="String" />
<!—长度不可以省略,否则不会输出-->
<size value="100" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%thread" />
</layout>
</parameter>
<!--日志等级-->
<parameter>
<parameterName value="@log_level" />
<dbType value="String" />
<size value="100" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%level" />
</layout>
</parameter>
<!--日志记录类名称-->
<parameter>
<parameterName value="@logger" />
<dbType value="String" />
<size value="200" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%logger" />
</layout>
</parameter>
<!--操作者。自定义的输出字段,使用重新实现的布局器ReflectionLayout -->
<parameter>
<parameterName value="@operator" />
<!—设置为Int32时只有bufferSize的value<="1"才正确输出-->
<dbType value="Int16" />
<layout type="TGLog.ExpandLayout2.ReflectionLayout,TGLog">
<conversionPattern value="%property{Operator}" />
</layout>
</parameter>
<!--操作对象-->
<parameter>
<parameterName value="@operand" />
<dbType value="String" />
<size value="300" />
<layout type="TGLog.ExpandLayout2.ReflectionLayout,TGLog">
<conversionPattern value="%property{Operand}" />
</layout>
</parameter>
<!—IP地址-->
<parameter>
<parameterName value="@ip" />
<dbType value="String" />
<size value="20" />
<layout type="TGLog.ExpandLayout2.ReflectionLayout,TGLog">
<conversionPattern value="%property{IP}" />
</layout>
</parameter>
<!--机器名-->
<parameter>
<parameterName value="@machineName" />
<dbType value="String" />
<size value="100" />
<layout type="TGLog.ExpandLayout2.ReflectionLayout,TGLog">
<conversionPattern value="%property{MachineName}" />
</layout>
</parameter>
<!--浏览器-->
<parameter>
<parameterName value="@browser" />
<dbType value="String" />
<size value="50" />
<layout type="TGLog.ExpandLayout2.ReflectionLayout,TGLog">
<conversionPattern value="%property{Browser}" />
</layout>
</parameter>
<!—日志消息-->
<parameter>
<parameterName value="@message" />
<dbType value="String" />
<size value="3000" />
<layout type="TGLog.ExpandLayout2.ReflectionLayout,TGLog">
<conversionPattern value="%property{Message}" />
</layout>
</parameter>
<!--动作类型-->
<parameter>
<parameterName value="@action_type" />
<dbType value="Int16" />
<layout type="TGLog.ExpandLayout2.ReflectionLayout,TGLog">
<conversionPattern value="%property{ActionType}" />
</layout>
</parameter>
<!—记录日志的位置-->
<parameter>
<parameterName value="@location" />
<dbType value="String" />
<size value="2000" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%location" />
</layout>
</parameter>
<!—异常信息。ExceptionLayout 为异常输出的默认格式-->
<parameter>
<parameterName value="@exception" />
<dbType value="String" />
<size value="4000" />
<layout type="log4net.Layout.ExceptionLayout" />
</parameter>
</appender>
注意:
向表中输出的字段不能多于数据表本身字段,而反之则可以,但这些多余字段一定使其可以为空,否则便写不到数据库;
输出字段的类型一定是对应数据表字段数据类型可以隐式转换的,而且长度也不能超过,否则也不能写入;
数据表字段设置尽量可以为空,这样可以避免一条日志记录存在空数据导致后面的日志都记录不了。
关联配置文件
log4net默认关联的是应用程序的配置文件App.config(BS程序是Web.config),可以使用程序集自定义属性来进行设置。下面来介绍一下这个自定义属性:
log4net.Config.XmlConifguratorAttribute。
XmlConfiguratorAttribute有3个属性:
ConfigFile: 配置文件的名字,文件路径相对于应用程序目录
(AppDomain.CurrentDomain.BaseDirectory)。ConfigFile属性不能和ConfigFileExtension属性一起使用。
ConfigFileExtension: 配置文件的扩展名,文件路径相对于应用程序的目录。
Watch: 如果将Watch属性设置为true,就会监视配置文件。当配置文件发生变化的时候,就会重新加载。
如果ConfigFile和ConfigFileExtension都没有设置,则使用应用程序的配置文件App.config(Web.config)。
可以在项目的AssemblyInfo.cs文件里添加以下的语句:
//监视默认的配置文件,App.exe.config
[assembly:log4net.Config.XmlConfigurator(Watch =true)]
//监视配置文件,App.exe.log4net
[assembly:log4net.Config.XmlConfigurator(ConfigFileExtension ="log4net", Watch =true)]
//使用配置文件log4net.config,不监视改变。注意log4net.config文件的目录,BS程序在站点目录//下,CS则在应用程序启动目录下,如调试时在/bin/Debug下,一般将文件属性的文件输出目录调为//始终复制即可
[assembly:log4net.Config.XmlConfigurator(ConfigFile ="log4net.config")]
//使用配置文件log4net.config,不监视改变
[assembly:log4net.Config.XmlConfigurator()]
也可以在Global.asax的Application_Start里或者是Program.cs中的Main方法中添加,注意这里一定是绝对路径,如下所示:
//这是在BS程序下,使用自定义的配置文件log4net.xml,使用Server.MapPath("~") + //@"/log4net.xml来取得路径。/log4net.xml为相对于站点的路径
// ConfigureAndWatch()相当于Configure(Watch = true)
log4net.Config.XmlConfigurator.ConfigureAndWatch(
new System.IO.FileInfo(Server.MapPath("~") + @"/log4net.xml"));
//这是在CS程序下,可以用以下方法获得:
string assemblyFilePath = Assembly.GetExecutingAssembly().Location;
string assemblyDirPath = Path.GetDirectoryName(assemblyFilePath);
string configFilePath = assemblyDirPath + " //log4net.xml";
log4net.Config.XmlConfigurator.ConfigureAndWatch(
new FileInfo(configFilePath));
或直接使用绝对路径:
log4net.Config.XmlConfigurator.Configure(new System.IO.FileInfo(@"c:/log4net.config"));
Log4net的简单扩展
通过重写布局Layout输出传入的 message对象的属性
1.1重写Layout类
通过继承log4net.Layout.PatternLayout类,使用log4net.Core.LoggingEvent类的方法得到了要输出的message类的名称,然后通过反射得到各个属性的值,使用PatternLayout类AddConverter方法传入得到的值。
1.2配置相应的配置文件
配置文件其他地方不用改动,只是需要改动<appender>中的<layout>。例如:
<layout type="TGLog.ExpandLayout2.ReflectionLayout,TGLog">
<param name="ConversionPattern" value="记录时间:%date
操作者ID:%property{Operator}
操作类型:%property{Action}%n
消息描述:%property{Message}%n 异常:%exception%n " />
</layout>
其中<layout>的type由原来的log4net.Layout.PatternLayout换为自定义的TGLog.ExpandLayout2.ReflectionLayout(TGLog.ExpandLayout2为命名空间)。%property{Operator}输出的即为message类对象的属性Operator的值。数据库配置同样,相应的字段如果是自定义的,则输出选用自定义的<layout>。例:
<!--动作类型-->
<parameter>
<parameterName value="@action_type" />
<dbType value="Int16" />
<layout type="TGLog.ExpandLayout2.ReflectionLayout,TGLog">
<conversionPattern value="%property{ActionType}" />
</layout>
</parameter>
1.3程序中如何使用
和一般使用方法基本相同,只是传入的参数是一个自定义的类,类的属性和配置文件中<layout>所有的%property{属性}是一致的,即%property{属性}在输出的时候就查找传入message类中有无对应的属性,如果有就输出值,没有则输出null。例:
log4net.ILog log = log4net.LogManager.GetLogger("ReflectionLayout");
2通过重新实现ILog接口来增加输入的参数
2.1重写LogImpl,LogManager类及实现ILog接口
这种方式是通过构造一个名为IMyLog接口,是继承Ilog接口而来,然后分别在MyLogImpl,MyLogManager重新实现IMyLog接口,增加了每种方法的参数。MyLogImpl,MyLogManager分别继承LogImpl,LogManager而来。
2.2配置相应的配置文件
配置文件其他地方不用改动,只是需要改动<appender>中的<layout>元素name为ConversionPattern的value中输出格式。例如:
<layout type=" log4net.Layout.PatternLayout ">
<param name="ConversionPattern" value="记录时间:%date
操作者ID:%property{Operator} 操作类型:%property{Action}%n
消息描述:%property{Message}%n 异常:%exception%n " />
</layout>
%property{参数}中的参数在MyLogImpl类中定义,如语句:
loggingEvent.Properties["Operator"] = operatorID;
就定义了Operator输出参数,即%property{Operator}输出的即为IMyLog中的参数operatorID的值。
数据库配置同样。例:
<!--动作类型-->
<parameter>
<parameterName value="@action_type" />
<dbType value="Int16" />
<layout type=" log4net.Layout.PatternLayout ">
<conversionPattern value="%property{ActionType}" />
</layout>
</parameter>
2.3程序中如何使用
先引用IMyLog ,MyLogManager所在的命名空间,创建一个IMyLog对象,myLog的5 个方法,每个方法都有四个重载,增加了多参数的重载。例:
IMyLog myLog = MyLogManager.GetLogger("ExpandILog");
PatterLayout格式化字符表
转换字符 | 效果 |
a | 等价于appdomain |
appdomain | 引发日志事件的应用程序域的友好名称。(使用中一般是可执行文件的名字。) |
c | 等价于 logger |
C | 等价于 type |
class | 等价于 type |
d | 等价于 date |
date | 发生日志事件的本地时间。 使用 DE>%utcdate 输出UTC时间。date后面还可以跟一个日期格式,用大括号括起来。DE>例如:%date{HH:mm:ss,fff}或者%date{dd MMM yyyy HH:mm:ss,fff}。如果date后面什么也不跟,将使用ISO8601 格式 。 日期格式和.Net中DateTime类的ToString方法中使用的格式是一样。 另外log4net还有3个自己的格式Formatter。 它们是 "ABSOLUTE", "DATE"和"ISO8601"分别代表 AbsoluteTimeDateFormatter, DateTimeDateFormatter和Iso8601DateFormatter。例如:%date{ISO8601}或%date{ABSOLUTE}。 它们的性能要好于ToString。 |
exception | 异常信息 日志事件中必须存了一个异常对象,如果日志事件不包含没有异常对象,将什么也不输出。异常输出完毕后会跟一个换行。一般会在输出异常前加一个换行,并将异常放在最后。 |
F | 等价于 file |
file | 发生日志请求的源代码文件的名字。 警告:只在调试的时候有效。调用本地信息会影响性能。 |
identity | 当前活动用户的名字(Principal.Identity.Name). 警告:会影响性能。(我测试的时候%identity返回都是空的。) |
l | 等价于 location |
L | 等价于 line |
location | 引发日志事件的方法(包括命名空间和类名),以及所在的源文件和行号。 警告:会影响性能。没有pdb文件的话,只有方法名,没有源文件名和行号。 |
level | 日志事件等级 |
line | 引发日志事件的行号 警告:会影响性能。 |
logger | 记录日志事件的Logger对象的名字。 可以使用精度说明符控制Logger的名字的输出层级,默认输出全名。 注意,精度符的控制是从右开始的。例如:logger 名为 "a.b.c", 输出模型为%logger{2} ,将输出"b.c"。 |
m | 等价于 message |
M | 等价于 method |
message | 由应用程序提供给日志事件的消息。 |
mdc | MDC (旧为:ThreadContext.Properties) 现在是事件属性的一部分。 保留它是为了兼容性,它等价于 property。 |
method | 发生日志请求的方法名(只有方法名而已)。 警告:会影响性能。 |
n | 等价于 newline |
newline | 换行符 |
ndc | NDC (nested diagnostic context) |
p | 等价于 level |
P | 等价于 property |
properties | 等价于 property |
property | 输出事件的特殊属性。例如: %property{user} 输出user属性。属性是由loggers或appenders添加到时间中的。 有一个默认的属性"DE>log4net:HostName"总是会有。DE> %property将输出所有的属性 。 (扩展后可以使用)
|
r | 等价于 timestamp |
t | 等价于 thread |
timestamp | 从程序启动到事件发生所经过的毫秒数。 |
thread | 引发日志事件的线程,如果没有线程名就使用线程号。 |
type | 引发日志请求的类的全名。. 可以使用精度控制符。例如: 类名是 "log4net.Layout.PatternLayout", 格式模型是%type{1} 将输出"PatternLayout"。(也是从右开始的。) 警告:会影响性能。 |
u | 等价于 identity |
username | 当前用户的WindowsIdentity。(类似:HostName/Username) 警告:会影响性能。 |
utcdate | 发生日志事件的UTC时间。DE>后面还可以跟一个日期格式,用大括号括起来。DE>例如:%utcdate{HH:mm:ss,fff}或者%utcdate{dd MMM yyyy HH:mm:ss,fff}。如果utcdate后面什么也不跟,将使用ISO8601 格式 。 日期格式和.Net中DateTime类的ToString方法中使用的格式是一样。 另外log4net还有3个自己的格式Formatter。 它们是 "ABSOLUTE", "DATE"和"ISO8601"分别代表 AbsoluteTimeDateFormatter, DateTimeDateFormatter和Iso8601DateFormatter。例如:%date{ISO8601}或%date{ABSOLUTE}。 它们的性能要好于ToString。 |
w | 等价于 username |
x | 等价于 ndc |
X | 等价于 mdc |
% | %%输出一个百分号 |
关于调用本地信息(caller location information)的说明:
%type %file %line %method %location %class %C %F %L %l %M 都会调用本地信息。这样做会影响性能。本地信息使用System.Diagnostics.StackTrace得到。.Net 1.0 不支持System.Diagnostics.StackTrace 类。
本地信息在调试模式下可以正常获取,在非调试模式下可能获取不到,或只能获取一部分。(根据我的测试,其实是需要有一个程序数据库(.pdb)文件。)
%property属性要用代码来设置才能使用(也就是扩展一下),
默认属性log4net:HostName不用设置。
转义字符的修饰符:
Format modifier | left justify | minimum width | maximum width | comment |
%20logger | false | 20 | none | 如果logger名不足20个字符,就在左边补空格。 |
%-20logger | true | 20 | none | 如果logger名不足20个字符,就在右边补空格。 |
%.30logger | NA | none | 30 | 超过30个字符将截断。 |
%20.30logger | false | 20 | 30 | logger名要在20到30之间,少了在左边补空格,多了截断。 |
%-20.30logger | true | 20 | 30 | logger名要在20到30之间,少了在右边补空格,多了截断。 |
参考
2、
3、
4、
5、
6、
7、
8、