一、数据准备

    在SQL Server中创建记录日志的数据表LogDetail:

CREATE TABLE [dbo].[LogDetail](    [LogID] [INT] IDENTITY(1,1) NOT NULL, --自增ID
    [LogDate] [DATETIME] NULL,            --日志时间
    [LogLevel] [NVARCHAR](10) NULL,       --日志级别
    [LogThread] [NVARCHAR](10) NULL,      --线程ID
    [Logger] [NVARCHAR](50) NULL,         --日志名称
    [LogMessage] [NVARCHAR](3000) NULL,   --日志内容
 CONSTRAINT [PK_LogDetail] PRIMARY KEY CLUSTERED (    [LogID] ASC)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]) ON [PRIMARY]

    在此表中,日志时间、日志级别、线程ID、日志名称都是可以通过配置文件从Log4Net库中取值的,需要重点处理的是日志内容字段。

    二、记录日志到数据库

    2.1、配置文件

    添加一个ConfigFile文件夹,然后在其下面新建一个Log4NetToDB.config的配置文件,接着在其属性的复制到输出目录项下选择始终复制。

<?xml version="1.0" encoding="utf-8" ?><configuration>
  <configSections>
    <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net" />
  configSections>
  <log4net debug="false">
    type:表示用哪种类型记录日志,log4net.Appender.ADONetAppender表示用数据库记录日志。-->
    <appender name="ADONetAppender" type="log4net.Appender.ADONetAppender">
      
      日志缓存写入条数,设置为0时只要有一条就立刻写到数据库。-->
      <bufferSize value="0" />

      数据库连接串-->
      C:\WINDOWS\Microsoft.NET\Framework\v4.0.30319\Config\machine.config-->
      <connectionType value="System.Data.SqlClient.SqlConnection, System.Data, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />

      数据库连接字符串-->
      <connectionString value="Server=.;Database=Test;Uid=sa;Pwd=********;" />
      
      数据库脚本-->
      <commandText value="INSERT INTO LogDetail (LogDate,LogLevel,LogThread,Logger,LogMessage) VALUES (@LogDate,@LogLevel,@LogThread,@Logger,@LogMessage)" />
      
      日志时间-->
      <parameter>
        <parameterName value="@LogDate" />
        <dbType value="DateTime" />
        <layout type="log4net.Layout.RawTimeStampLayout" />
      parameter>

      日志级别-->
      <parameter>
        <parameterName value="@LogLevel" />
        <dbType value="String" />
        <size value="10" />
        <layout type="log4net.Layout.PatternLayout">
          <conversionPattern value="%p" />
        layout>
      parameter>
      
      线程ID-->
      <parameter>
        <parameterName value="@LogThread" />
        <dbType value="String" />
        <size value="10" />
        <layout type="log4net.Layout.PatternLayout">
          <conversionPattern value="%t" />
        layout>
      parameter>

      日志名称-->
      <parameter>
        <parameterName value="@Logger" />
        <dbType value="String" />
        <size value="50" />
        <layout type="log4net.Layout.PatternLayout">
          <conversionPattern value="%logger" />
        layout>
      parameter>
      
      日志内容-->
      <parameter>
        <parameterName value="@LogMessage" />
        <dbType value="String" />
        <size value="3000" />
        <layout type="LinkTo.Test.ConsoleLog4Net.Utility.CustomLayout">
          <conversionPattern value="%property{LogMessage}" />
        layout>
      parameter>
    appender>

    <root>
      <priority value="ALL" />
      <level value="ALL" />
      <appender-ref ref="ADONetAppender" />
    root>
  log4net>configuration>

    2.2、日志内容处理过程

    注:日志内容处理涉及的4个类(含帮助类)都是存放在Utility文件夹下。

    从配置的

using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;using log4net.Layout;namespace LinkTo.Test.ConsoleLog4Net.Utility
{    public class CustomLayout : PatternLayout
    {        /// 
        /// 构造函数:把需要写入数据库的属性添加进来        /// 
        public CustomLayout()
        {
            AddConverter("property", typeof(CustomLayoutConverter));
        }
    }
}

    CustomLayout类添加属性时,类型来源于CustomLayoutConverter类:

using System;using System.Collections.Generic;using System.IO;using System.Linq;using System.Reflection;using System.Text;using System.Threading.Tasks;using log4net.Core;using log4net.Layout.Pattern;namespace LinkTo.Test.ConsoleLog4Net.Utility
{    public class CustomLayoutConverter : PatternLayoutConverter
    {        protected override void Convert(TextWriter writer, LoggingEvent loggingEvent)
        {            if (Option != null)
            {                //写入指定键的值                WriteObject(writer, loggingEvent.Repository, LookupProperty(Option, loggingEvent));
            }            else
            {                //Write all the key value pairs                WriteDictionary(writer, loggingEvent.Repository, loggingEvent.GetProperties());
            }
        }        /// 
        /// 通过反射获取传入的日志对象的某个属性的值        /// 
        /// 
        /// 
        /// 
        private object LookupProperty(string property, LoggingEvent loggingEvent)
        {            object propertyValue = string.Empty;
            PropertyInfo propertyInfo = loggingEvent.MessageObject.GetType().GetProperty(property);            if (propertyInfo != null)
                propertyValue = propertyInfo.GetValue(loggingEvent.MessageObject, null);            return propertyValue;
        }


    }
}

    从配置的

using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;namespace LinkTo.Test.ConsoleLog4Net.Utility
{    public class LogContent
    {        /// 
        /// 日志内容        /// 
        public string LogMessage { get; set; }        public LogContent(string logMessage)
        {
            LogMessage = logMessage;
        }
    }
}

    2.3、帮助类

    为了简化写日志的过程,封装了一个简单的帮助类LogHelper:

using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;using log4net;namespace LinkTo.Test.ConsoleLog4Net.Utility
{    public class LogHelper
    {        public static readonly ILog logger = LogManager.GetLogger("LinkTo.Test.ConsoleLog4Net");    //这里的参数不能使用Type类型

        public static void Fatal(LogContent content)
        {
            logger.Fatal(content);
        }        public static void Error(LogContent content)
        {
            logger.Error(content);
        }        public static void Warn(LogContent content)
        {
            logger.Warn(content);
        }        public static void Info(LogContent content)
        {
            logger.Info(content);
        }        public static void Debug(LogContent content)
        {
            logger.Debug(content);
        }
    }
}

    2.4、测试代码

    class Program
    {        static void Main(string[] args)
        {
            XmlConfigurator.Configure(new FileInfo(Path.Combine(AppDomain.CurrentDomain.BaseDirectory + "ConfigFile\\Log4NetToDB.config")));
            LogHelper.Fatal(new LogContent("This is fatal message."));
            LogHelper.Error(new LogContent("This is error message."));
            LogHelper.Warn(new LogContent("This is warn message."));
            LogHelper.Info(new LogContent("This is info message."));
            LogHelper.Debug(new LogContent("This is debug message."));

            Console.Read();
        }
    }

    2.5、运行结果

C# Log4Net学习笔记:记录日志到数据库_C# Log4Net

    2.6、一点优化

    每次写日志时,都需要进行Log4Net文件配置的话,肯定是没有必要的:

XmlConfigurator.Configure(new FileInfo(Path.Combine(AppDomain.CurrentDomain.BaseDirectory + "ConfigFile\\Log4NetToDB.config")));

    可以在项目的Properties\AssemblyInfo.cs最下面加上下面这一句,进行全局的统一配置:

[assembly: log4net.Config.XmlConfigurator(ConfigFile = "ConfigFile\\Log4NetToDB.config")]