C ා log4net learning notes: log to database

I. data preparation

To create a log data table LogDetail in SQL Server:

CREATE TABLE [dbo].[LogDetail](
    [LogID] [INT] IDENTITY(1,1) NOT NULL, --Self increasing ID
    [LogDate] [DATETIME] NULL,            --Log time
    [LogLevel] [NVARCHAR](10) NULL,       --log level
    [LogThread] [NVARCHAR](10) NULL,      --thread  ID
    [Logger] [NVARCHAR](50) NULL,         --Log name
    [LogMessage] [NVARCHAR](3000) NULL,   --Log content
 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]

In this table, log time, log level, thread ID and log name can be taken from Log4Net library through configuration file, and the log content field needs to be processed.

II. Log to database

2.1 configuration file

Add a ConfigFile folder, create a new Log4NetToDB.config configuration file under it, and then select always copy under the copy to output directory item of its properties.

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <configSections>
    <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net" />
  </configSections>
  <log4net debug="false">
    <!--type: Indicates which type of logging is used, log4net.Appender.ADONetAppender Indicates logging with a database.-->
    <appender name="ADONetAppender" type="log4net.Appender.ADONetAppender">
      
      <!--When the number of log cache writes is set to 0, as long as one is written to the database immediately.-->
      <bufferSize value="0" />

      <!--Database connection string-->
      <!--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" />

      <!--Database connection string-->
      <connectionString value="Server=.;Database=Test;Uid=sa;Pwd=********;" />
      
      <!--Database script-->
      <commandText value="INSERT INTO LogDetail (LogDate,LogLevel,LogThread,Logger,LogMessage) VALUES (@LogDate,@LogLevel,@LogThread,@Logger,@LogMessage)" />
      
      <!--Log time-->
      <parameter>
        <parameterName value="@LogDate" />
        <dbType value="DateTime" />
        <layout type="log4net.Layout.RawTimeStampLayout" />
      </parameter>

      <!--log level-->
      <parameter>
        <parameterName value="@LogLevel" />
        <dbType value="String" />
        <size value="10" />
        <layout type="log4net.Layout.PatternLayout">
          <conversionPattern value="%p" />
        </layout>
      </parameter>
      
      <!--thread  ID-->
      <parameter>
        <parameterName value="@LogThread" />
        <dbType value="String" />
        <size value="10" />
        <layout type="log4net.Layout.PatternLayout">
          <conversionPattern value="%t" />
        </layout>
      </parameter>

      <!--Log name-->
      <parameter>
        <parameterName value="@Logger" />
        <dbType value="String" />
        <size value="50" />
        <layout type="log4net.Layout.PatternLayout">
          <conversionPattern value="%logger" />
        </layout>
      </parameter>
      
      <!--Log content-->
      <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 log content processing

Note: the four classes (including help classes) involved in log content processing are stored in the Utility folder.

From the configured < Layout type = "linkto. Test. Consolelog4net. Utility. CustomLayout" > we can see that the value of log content comes from a custom Layout class, 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>
        /// Constructor: add the attribute to be written to the database
        /// </summary>
        public CustomLayout()
        {
            AddConverter("property", typeof(CustomLayoutConverter));
        }
    }
}

When the CustomLayout class adds properties, the type comes from the CustomLayoutConverter class:

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)
            {
                //Write the value of the specified key
                WriteObject(writer, loggingEvent.Repository, LookupProperty(Option, loggingEvent));
            }
            else
            {
                //Write all the key value pairs
                WriteDictionary(writer, loggingEvent.Repository, loggingEvent.GetProperties());
            }
        }

        /// <summary>
        /// Get the value of a property of the incoming log object through reflection
        /// </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;
        }


    }
}

From the configured < conversionpattern value = "% property {LogMessage}" / > it can be seen that the value of log content comes from the property LogMessage, and this LogMessage comes from an entity class 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>
        /// Log content
        /// </summary>
        public string LogMessage { get; set; }

        public LogContent(string logMessage)
        {
            LogMessage = logMessage;
        }
    }
}

2.3 help

To simplify the process of writing logs, a simple help class LogHelper is encapsulated:

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");    //The parameters here cannot be used Type 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 test code

    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 operation results

2.6 one point optimization

Every time you write a log, you need to configure the Log4Net file. It is certainly unnecessary:

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

You can add the following sentence at the bottom of the Properties\AssemblyInfo of the project to perform global unified configuration:

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

Tags: C# Database SQL xml encoding

Posted on Tue, 12 May 2020 09:48:01 -0400 by nathus