C# Log4Net學習筆記:記錄日志到數據庫


    一、數據准備

    在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文件夾下。

    從配置的<layout type="LinkTo.Test.ConsoleLog4Net.Utility.CustomLayout">可以看出,日志內容的取值來源於一個自定義的Layout類CustomLayout:

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
    {
        /// <summary>
        /// 構造函數:把需要寫入數據庫的屬性添加進來
        /// </summary>
        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());
            }
        }

        /// <summary>
        /// 通過反射獲取傳入的日志對象的某個屬性的值
        /// </summary>
        /// <param name="property"></param>
        /// <param name="loggingEvent"></param>
        /// <returns></returns>
        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;
        }


    }
}

    從配置的<conversionPattern value="%property{LogMessage}" />可以看出,日志內容的取值來源於屬性LogMessage,而這個LogMessage,統一來源於一個實體類LogContent:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace LinkTo.Test.ConsoleLog4Net.Utility
{
    public class LogContent
    {
        /// <summary>
        /// 日志內容
        /// </summary>
        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、運行結果

    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")]

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM