ExtAspNet应用技巧(八) - log4net配置与使用

时间:2022-08-02 02:51:13
前言

为了实践项目驱动的 ExtAspNet开发过程,最近我启动了另外一个开源项目 - AppBox
AppBox项目使用ExtAspNet作为前台展现层,SubSonic作为ORM层,SqlServer2005作为数据库,在Asp.Net2.0基础之上实现一个企业综合管理系统所必须的基础组件。
包括用户管理,菜单管理,权限管理,组织结构管理等各个部分,虽然AppBox不是给最终用户使用的,但是可以作为开发人员搭建网站的一个框架,同时在项目中遇到的控件会优先在ExtAspNet中实现。

由于在AppBox中使用了log4net作为日志记录组件,所以这篇文章就来分享一下log4net的配置和使用。

log4net配置

1. 首先到 http://logging.apache.org/ 下载最新的log4net v1.2.10。

2. 建立数据库表
    CREATE TABLE [dbo].[Log] (
[Id] [int] IDENTITY (1, 1) NOT NULL,
[Date] [datetime] NOT NULL,
[Thread] [varchar] (255) NOT NULL,
[Level] [varchar] (50) NOT NULL,
[Logger] [varchar] (255) NOT NULL,
[Message] [varchar] (4000) NOT NULL,
[Exception] [varchar] (2000) NULL
)


3. 在网站根目录添加log4net.config文件
    
<log4net>
<root>
<level value="ALL"/>
<appender-ref ref="AdoNetAppender"/>
<appender-ref ref="RollingFileAppender"/>
</root>
<appender name="RollingFileAppender" type="log4net.Appender.RollingFileAppender,log4net">
<param name="File" value="log\log.config"/>
<param name="AppendToFile" value="true"/>
<param name="MaxSizeRollBackups" value="10"/>
<param name="MaximumFileSize" value="5MB"/>
<param name="RollingStyle" value="Size"/>
<param name="StaticLogFileName" value="true"/>
<layout type="log4net.Layout.PatternLayout,log4net">
<param name="ConversionPattern" value="%d [%t] %-5p %c [%x] - %m%n"/>
</layout>
</appender>
<appender name="AdoNetAppender" type="log4net.Appender.AdoNetAppender">
<bufferSize value="0"/>
<connectionType value="System.Data.SqlClient.SqlConnection, System.Data, Version=2.0.0.0, Culture=neutral,PublicKeyToken=b77a5c561934e089"/>
<connectionString value="Password=sa;Persist Security Info=True;User ID=sa;Initial Catalog=AppBox;Data Source=."/>
<commandText value="insert into X_Log(DATETIME,THREAD,LOG_LEVEL,LOGGER,MESSAGE,EXCEPTION) values (@log_date,@thread,@log_level,@logger,@message,@exception)"/>
<parameter>
<parameterName value="@log_date"/>
<dbType value="DateTime"/>
<layout type="log4net.Layout.RawTimeStampLayout"/>
</parameter>
<parameter>
<parameterName value="@thread"/>
<dbType value="String"/>
<size value="255"/>
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%thread"/>
</layout>
</parameter>
<parameter>
<parameterName value="@log_level"/>
<dbType value="String"/>
<size value="50"/>
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%level"/>
</layout>
</parameter>
<parameter>
<parameterName value="@logger"/>
<dbType value="String"/>
<size value="255"/>
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%logger"/>
</layout>
</parameter>
<parameter>
<parameterName value="@message"/>
<dbType value="String"/>
<size value="4000"/>
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%message"/>
</layout>
</parameter>
<parameter>
<parameterName value="@exception"/>
<dbType value="String"/>
<size value="2000"/>
<layout type="log4net.Layout.ExceptionLayout"/>
</parameter>
</appender>
</log4net>


注:这里我们使用了两种类型的日志记录方式,文件和数据库。
文件保存在网站根目录下的 log\log.config ,必须保证Asp.Net服务进程对此文件夹有写权限,否则不能写入文件并且没有任何提示。
比如在WindowXP下需要设置 ASPNET (Windows2003不是这个名称,可以Google一下) 对此文件夹的写权限。
同时注意我们使用log.config而不是log.txt,是为了防止匿名用户非法下载系统日志。

在数据库配置上也有个小技巧,我们设置了 bufferSize value="0",也就是说产生一条日志就写到数据库。
我刚开始也是在这个地方遇到麻烦,设置bufferSize为10,刚开始怎么也观察不到日志插入数据库,后来才知道被缓存了。


不要在多处定义数据库连接字符串

因为我们已经在Web.config中定义了数据库连接字符串:
    <connectionStrings>
<clear/>
<add name="Default" connectionString="Password=sa;Persist Security Info=True;User ID=sa;Initial Catalog=AppBox;Data Source=."/>
</connectionStrings>

因此如果在log4net.config中再定义数据库连接字符串,总觉得不爽。

经过在网上一番搜索,居然发现log4net v1.2.10不支持这个Asp.Net2.0的特性,不过 这篇文章给出了一个解决方法。
我们需要在AppBox中添加一个CS文件:
    using System;
using System.Collections.Generic;
using System.Web;
using log4net;
using log4net.Appender;
using System.Configuration;

namespace AppBox
{
/// <summary>
/// http://issues.apache.org/jira/browse/LOG4NET-88
/// An appender for Log4Net that uses a database based on the connection string name.
/// </summary>
public class Log4NetConnectionStringNameAdoNetAppender : AdoNetAppender
{
private static ILog _Log;

/// <summary>
/// Gets the log.
/// </summary>
/// <value>The log.</value>
protected static ILog Log
{
get
{
if (_Log == null)
_Log = LogManager.GetLogger(typeof(Log4NetConnectionStringNameAdoNetAppender));
return _Log;
}
}

private string _ConnectionStringName;

/// <summary>
/// Initialize the appender based on the options set
/// </summary>
/// <remarks>
/// <para>
/// This is part of the <see cref="T:log4net.Core.IOptionHandler"/> delayed object
/// activation scheme. The <see cref="M:log4net.Appender.AdoNetAppender.ActivateOptions"/> method must
/// be called on this object after the configuration properties have
/// been set. Until <see cref="M:log4net.Appender.AdoNetAppender.ActivateOptions"/> is called this
/// object is in an undefined state and must not be used.
/// </para>
/// <para>
/// If any of the configuration properties are modified then
/// <see cref="M:log4net.Appender.AdoNetAppender.ActivateOptions"/> must be called again.
/// </para>
/// </remarks>
public override void ActivateOptions()
{
PopulateConnectionString();
base.ActivateOptions();
}

/// <summary>
/// Populates the connection string.
/// </summary>
private void PopulateConnectionString()
{
// if connection string already defined, do nothing
if (!String.IsNullOrEmpty(ConnectionString)) return;

// if connection string name is not available, do nothing
if (String.IsNullOrEmpty(ConnectionStringName)) return;

// grab connection string settings
ConnectionStringSettings settings = ConfigurationManager
.ConnectionStrings[ConnectionStringName];

// if connection string name was not found in settings
if (settings == null)
{
// log error
if (Log.IsErrorEnabled)
Log.ErrorFormat("Connection String Name not found in Configuration: {0}",
ConnectionStringName);
// do nothing more
return;
}

// retrieve connection string from the name
ConnectionString = settings.ConnectionString;
}

/// <summary>
/// Gets or sets the name of the connection string.
/// </summary>
/// <value>The name of the connection string.</value>
public string ConnectionStringName
{
get { return _ConnectionStringName; }
set { _ConnectionStringName = value; }
}
}

}


然后修改log4net.config文件:
    <appender name="AdoNetAppender" type="AppBox.Log4NetConnectionStringNameAdoNetAppender">
<bufferSize value="0"/>
<connectionType value="System.Data.SqlClient.SqlConnection, System.Data, Version=2.0.0.0, Culture=neutral,PublicKeyToken=b77a5c561934e089"/>
<connectionStringName value="Default"></connectionStringName>
........
........
</appender>


注意,在log4net.config中我们指定使用名为 Default 的连接字符串。

使用log4net

调用方法倒很简单,比如在登录页面:
    private static readonly log4net.ILog logger = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
protected void btnSubmit_Click(object sender, EventArgs e)
{
// ....
logger.Info(String.Format("用户 - {0} - 登录成功", tbxUserName.Text));
}

生成log记录类似:
    2009-08-19 18:05:37,932 [11] INFO  AppBox._default [(null)] - 用户 - admin - 登录成功