• 开源框架


    序言

    众所周知,大多数情况下,业务需要记录的并不是简单的系统时间%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

  • 相关阅读:
    自底向上的归并排序 .[转]
    分治法寻找数组最大的两个数和最小的两个数
    分治法求最大最小值
    数字移动【转】
    NRF24L01无线模块的使用
    对钙铀云母放射强度的测量
    自制用于放置钙铀云母的铅盒
    Arduino从DHT11读取温湿度数据并显示在1602LCD
    β particle, α particle, γ ray, ionization chamber
    Arduino通过I2C(PCF8574T)驱动1602LCD
  • 原文地址:https://www.cnblogs.com/jeremywucnblog/p/12819964.html
Copyright © 2020-2023  润新知