序言
衆所周知,大多數情況下,業務需要記錄的并不是簡單的系統時間%date,級别%level,資訊%message等字段,而是需要自定義的業務字段。以便後續的資料挖掘和鑽取。
逐漸研究發現Log4Net記錄日志的info,error,debug等方法可以傳入object參數:log.info(object message)。
下面記錄一下,傳一個自定義的業務日志對象給info方法,它自動幫我得到該業務對象的字段的值,然後再寫入到資料庫背景。
解決方案
1、建立資料庫和資料表
- 資料庫:Test,使用者名:sa,密碼:sa
- 資料表:SysLogs
USE [Test] GO /****** Object: Table [dbo].[SysLogs] Script Date: 2020-05-02 22:07:49 ******/ SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO CREATE TABLE [dbo].[SysLogs]( [ID] [int] IDENTITY(1,1) NOT NULL, [LogDate] [datetime] NULL, [Msg] [nvarchar](max) NULL, [UserName] [nvarchar](200) NULL, [ThreadName] [nvarchar](200) NULL, [Level] [nvarchar](200) NULL ) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY] GO
SysLogs生成腳本
2、建立解決方案
- 添加控制台應用程式 Log4NetDBDemo
- 添加MyMsgPatternConverter類,啟用反射方式建立業務類屬性
- 添加MyLayout類,在該類的構造方法中将自定義的Converter加進去,以便于處理property{}中的自定義字段)和一個PatternConverter
- 封裝一個消息類LogContent,包含需要操作的業務屬性字段
- 配置App.config檔案
3、核心代碼
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Log4NetDBDemo { class Program { static void Main(string[] args) { log4net.Config.XmlConfigurator.Configure(); log4net.ILog log = log4net.LogManager.GetLogger(typeof(Program)); Console.WriteLine("開始寫日志_" + DateTime.Now.ToLongDateString()); log.Info(new LogContent { Msg = "測試Log4Net日志存入資料庫", ThreadName = "控制台測試子產品", UserName = "sysman" }); Console.WriteLine("日志寫入成功_" + DateTime.Now.ToLongDateString()); Console.ReadKey(); } } }
Program.cs
using log4net.Layout.Pattern; using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Text; using System.Threading.Tasks; namespace Log4NetDBDemo { public class MyMsgPatternConverter : PatternLayoutConverter { protected override void Convert(System.IO.TextWriter writer, log4net.Core.LoggingEvent loggingEvent) { if (Option != null) { // Write the value for the specified key 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> /// <returns></returns> private object LookupProperty(string property, log4net.Core.LoggingEvent loggingEvent) { object propertyValue = string.Empty; PropertyInfo propertyInfo = loggingEvent.MessageObject.GetType().GetProperty(property); if (propertyInfo != null) propertyValue = propertyInfo.GetValue(loggingEvent.MessageObject, null); return propertyValue; } } }
MyMsgPatternConverter.cs
using log4net.Layout; using log4net.Layout.Pattern; using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Text; using System.Threading.Tasks; namespace Log4NetDBDemo { /// <summary> /// 自定義一個Layout,在該類的構造方法中将自定義的Converter加進去 /// 以便于處理property{}中的自定義字段)和一個PatternConverter: /// </summary> public class MyLayout : PatternLayout { public MyLayout() { this.AddConverter("property", typeof(MyMsgPatternConverter)); } } }
MyLayout.cs
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Log4NetDBDemo { /// <summary> /// 封裝一個消息類,用來存放異常日志資訊 /// </summary> public class LogContent { /// <summary> /// 日志消息詳情 /// </summary> public string Msg { get; set; } /// <summary> /// 目前登入使用者 /// </summary> public string UserName { get; set; } /// <summary> /// 線程名稱/子產品名稱/菜單名稱/節點名稱等 /// </summary> public string ThreadName { get; set; } } }
LogContent.cs
<?xml version="1.0" encoding="utf-8" ?> <configuration> <configSections> <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler,log4net"/> </configSections> <log4net> <root> <level value="All" /> <!--媒體類型:資料庫--> <appender-ref ref="AdoNetAppender"/> </root> <!--媒體類型配置:資料庫相關配置--> <appender name="AdoNetAppender" type="log4net.Appender.AdoNetAppender"> <bufferSize value="1" /> <!--連接配接字元串--> <connectionType value="System.Data.SqlClient.SqlConnection,System.Data, Version=1.0.3300.0, Culture=neutral,PublicKeyToken=b77a5c561934e089" /> <!--連接配接字元串value--> <connectionString value="database=test;server=localhost;User ID=sa;Password=sa" /> <!--sql語句--> <commandText value="INSERT INTO SysLogs(LogDate,Msg,UserName,ThreadName,Level) VALUES (@LogDate,@Msg,@UserName,@ThreadName,@Level)" /> <!--定義一些業務邏輯對象的屬性,以便寫入到資料庫背景--> <!--日志寫入時間--> <parameter> <parameterName value="@LogDate" /> <dbType value="DateTime" /> <layout type="log4net.Layout.RawTimeStampLayout" /> </parameter> <!--日志内容--> <parameter> <parameterName value="@Msg" /> <dbType value="String" /> <size value="2000" /> <layout type="Log4NetDBDemo.MyLayout, Log4NetDBDemo"> <param name="ConversionPattern" value="%property{Msg}"/> </layout> </parameter> <!--使用者名--> <parameter> <parameterName value="@UserName" /> <dbType value="String" /> <size value="200" /> <layout type="Log4NetDBDemo.MyLayout, Log4NetDBDemo" > <param name="ConversionPattern" value="%property{UserName}"/> </layout> </parameter> <!--程序、子產品名稱--> <parameter> <parameterName value="@ThreadName" /> <dbType value="String" /> <size value="200" /> <layout type="Log4NetDBDemo.MyLayout, Log4NetDBDemo" > <param name="ConversionPattern" value="%property{ThreadName}"/> </layout> </parameter> <!--Log4Net Level--> <parameter> <parameterName value="@Level" /> <dbType value="String" /> <size value="50" /> <layout type="log4net.Layout.PatternLayout" value="%level" /> </parameter> <!--定義一些業務邏輯對象的屬性,以便寫入到資料庫背景 結束--> </appender> </log4net> </configuration>
App.config
運作結果
注意:我這裡執行了三次。
注意:
項目除了添加Log4Net.dll引用外還需要添加System.Data.dll引用。
參考資料:https://www.cnblogs.com/Arlen/archive/2008/11/22/1338908.html