Tagged: log4net

Log4Net 写入数据库[扩展字段]

最近在写一个项目,需要将日志写入数据库,Log4Net的功能仅仅只能满足写入消息部分,若需要写入自定义信息就不行了,所以这里扩展了Log4Net的部分功能。

扩展字段

建立扩展字段的Entity类

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

namespace Kelun.Log4Net
{
    /// <summary>
    /// 扩展Log4Net字段
    /// </summary>
    class LogMessage
    {
        /// <summary>
        /// 订单号
        /// </summary>
        public string OrderNum { get; set; }

        /// <summary>
        /// 用户
        /// </summary>
        public string UserName { get; set; }

        public LogMessage(string orderNum, string userName)
        {
            OrderNum = orderNum;
            UserName = userName;
        }
    }
}

转换类

using log4net.Core;
using log4net.Layout.Pattern;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;

namespace Kelun.Log4Net
{
    /// <summary>
    /// Log4Net 扩展字段
    /// 转换自定义字段
    /// </summary>
    class ParemterContent
    {
        internal sealed class OrderNumParam : PatternLayoutConverter
        {
            protected override void Convert(TextWriter writer, LoggingEvent loggingEvent)
            {
                if (loggingEvent.MessageObject is LogMessage content)
                {
                    writer.Write(content.OrderNum);
                }
            }
        }

        internal sealed class UserNameParam : PatternLayoutConverter
        {
            protected override void Convert(TextWriter writer, LoggingEvent loggingEvent)
            {
                if (loggingEvent.MessageObject is LogMessage content)
                {
                    writer.Write(content.UserName);
                }
            }
        }
    }
}

Layout 扩展

using log4net.Layout;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Kelun.Log4Net
{
    class MyLoggerLayout : PatternLayout
    {
        public MyLoggerLayout()
        {
            this.AddConverter("OrderNum", typeof(ParemterContent.OrderNumParam));
            this.AddConverter("UserName", typeof(ParemterContent.UserNameParam));
        }
    }
}

扩展方法

接口

using log4net;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Kelun.Log4Net
{
    interface IMyLog : ILog
    {
        void Debug(object message, string OrderNum, string UserName);
        void Debug(object message, string OrderNum, string UserName,Exception t);

        void Info(object message, string OrderNum, string UserName);
        void Info(object message, string OrderNum, string UserName, Exception t);

        void Warn(object message, string OrderNum, string UserName);
        void Warn(object message, string OrderNum, string UserName, Exception t);

        void Error(object message, string OrderNum, string UserName);
        void Error(object message, string OrderNum, string UserName, Exception t);

    }
}

接口实现

using Kelun.Log4Net;
using log4net.Core;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Kelun.Log4Net
{
    class MyLogImpl : LogImpl, IMyLog
    {

        public static readonly Type ThisDeclaringType = typeof(MyLogImpl);

        public MyLogImpl(ILogger logger) : base(logger)
        {
            
        }

        #region 实现重写

        public void Debug(object message, string OrderNum, string UserName)
        {
            Debug(message, OrderNum, UserName, null);
        }

        public void Debug(object message, string OrderNum, string UserName, Exception t)
        {
            if (this.IsDebugEnabled)
            {
                LoggingEvent loggingEvent = new LoggingEvent(ThisDeclaringType, Logger.Repository, Logger.Name, Level.Debug, message, t);
                loggingEvent.Properties["UserName"] = UserName;
                loggingEvent.Properties["OrderNum"] = OrderNum;
                Logger.Log(loggingEvent);
            }
        }

        public void Error(object message, string OrderNum, string UserName)
        {
            Error(message, OrderNum, UserName, null);
        }

        public void Error(object message, string OrderNum, string UserName, Exception t)
        {
            if (this.IsErrorEnabled)
            {
                LoggingEvent loggingEvent = new LoggingEvent(ThisDeclaringType, Logger.Repository, Logger.Name, Level.Error, message, t);
                loggingEvent.Properties["UserName"] = UserName;
                loggingEvent.Properties["OrderNum"] = OrderNum;
                Logger.Log(loggingEvent);
            }
        }

        public void Info(object message, string OrderNum, string UserName)
        {
            Info(message, OrderNum, UserName, null);
        }

        public void Info(object message, string OrderNum, string UserName, Exception t)
        {
            if (this.IsInfoEnabled)
            {
                LoggingEvent loggingEvent = new LoggingEvent(ThisDeclaringType, Logger.Repository, Logger.Name, Level.Info, message, t);
                loggingEvent.Properties["UserName"] = UserName;
                loggingEvent.Properties["OrderNum"] = OrderNum;
                Logger.Log(loggingEvent);
            }
        }

        public void Warn(object message, string OrderNum, string UserName)
        {
            Warn(message, OrderNum, UserName, null);
        }

        public void Warn(object message, string OrderNum, string UserName, Exception t)
        {
            LoggingEvent loggingEvent = new LoggingEvent(ThisDeclaringType, Logger.Repository, Logger.Name, Level.Warn, message, t);
            loggingEvent.Properties["UserName"] = UserName;
            loggingEvent.Properties["OrderNum"] = OrderNum;
            Logger.Log(loggingEvent);
        }

        #endregion
    }
}

扩展Manager

using log4net.Core;
using System.Reflection;
using System;

namespace Kelun.Log4Net
{
    class MyLogManager
    {
        #region Static Member Variables
        /// <summary>
        /// The wrapper map to use to hold the <see cref="MyLogImpl"/> objects
        /// </summary>
        private static readonly WrapperMap s_wrapperMap = new WrapperMap(new WrapperCreationHandler(WrapperCreationHandler));
        #endregion

        #region Constructor
        private MyLogManager() { }
        #endregion


        #region Type Specific Manager Methods

        /// <summary>
        /// Returns the named logger if it exists
        /// </summary>
        /// <remarks>
        /// <para>If the named logger exists (in the default hierarchy) then it
        /// returns a reference to the logger, otherwise it returns
        /// <c>null</c>.</para>
        /// </remarks>
        /// <param name="name">The fully qualified logger name to look for</param>
        /// <returns>The logger found, or null</returns>
        public static IMyLog Exists(string name)
        {
            return Exists(Assembly.GetCallingAssembly(), name);
        }

        /// <summary>
        /// Returns the named logger if it exists
        /// </summary>
        /// <remarks>
        /// <para>If the named logger exists (in the specified domain) then it
        /// returns a reference to the logger, otherwise it returns
        /// <c>null</c>.</para>
        /// </remarks>
        /// <param name="domain">the domain to lookup in</param>
        /// <param name="name">The fully qualified logger name to look for</param>
        /// <returns>The logger found, or null</returns>
        public static IMyLog Exists(string domain, string name)
        {
            return WrapLogger(LoggerManager.Exists(domain, name));
        }

        /// <summary>
        /// Returns the named logger if it exists
        /// </summary>
        /// <remarks>
        /// <para>If the named logger exists (in the specified assembly's domain) then it
        /// returns a reference to the logger, otherwise it returns
        /// <c>null</c>.</para>
        /// </remarks>
        /// <param name="assembly">the assembly to use to lookup the domain</param>
        /// <param name="name">The fully qualified logger name to look for</param>
        /// <returns>The logger found, or null</returns>
        public static IMyLog Exists(Assembly assembly, string name)
        {
            return WrapLogger(LoggerManager.Exists(assembly, name));
        }

        /// <summary>
        /// Returns all the currently defined loggers in the default domain.
        /// </summary>
        /// <remarks>
        /// <para>The root logger is <b>not</b> included in the returned array.</para>
        /// </remarks>
        /// <returns>All the defined loggers</returns>
        public static IMyLog[] GetCurrentLoggers()
        {
            return GetCurrentLoggers(Assembly.GetCallingAssembly());
        }

        /// <summary>
        /// Returns all the currently defined loggers in the specified domain.
        /// </summary>
        /// <param name="domain">the domain to lookup in</param>
        /// <remarks>
        /// The root logger is <b>not</b> included in the returned array.
        /// </remarks>
        /// <returns>All the defined loggers</returns>
        public static IMyLog[] GetCurrentLoggers(string domain)
        {
            return WrapLoggers(LoggerManager.GetCurrentLoggers(domain));
        }

        /// <summary>
        /// Returns all the currently defined loggers in the specified assembly's domain.
        /// </summary>
        /// <param name="assembly">the assembly to use to lookup the domain</param>
        /// <remarks>
        /// The root logger is <b>not</b> included in the returned array.
        /// </remarks>
        /// <returns>All the defined loggers</returns>
        public static IMyLog[] GetCurrentLoggers(Assembly assembly)
        {
            return WrapLoggers(LoggerManager.GetCurrentLoggers(assembly));
        }

        /// <summary>
        /// Retrieve or create a named logger.
        /// </summary>
        /// <remarks>
        /// <para>Retrieve a logger named as the <paramref name="name"/>
        /// parameter. If the named logger already exists, then the
        /// existing instance will be returned. Otherwise, a new instance is
        /// created.</para>
        /// 
        /// <para>By default, loggers do not have a set level but inherit
        /// it from the hierarchy. This is one of the central features of
        /// log4net.</para>
        /// </remarks>
        /// <param name="name">The name of the logger to retrieve.</param>
        /// <returns>the logger with the name specified</returns>
        public static IMyLog GetLogger(string name)
        {
            return GetLogger(Assembly.GetCallingAssembly(), name);
        }

        /// <summary>
        /// Retrieve or create a named logger.
        /// </summary>
        /// <remarks>
        /// <para>Retrieve a logger named as the <paramref name="name"/>
        /// parameter. If the named logger already exists, then the
        /// existing instance will be returned. Otherwise, a new instance is
        /// created.</para>
        /// 
        /// <para>By default, loggers do not have a set level but inherit
        /// it from the hierarchy. This is one of the central features of
        /// log4net.</para>
        /// </remarks>
        /// <param name="domain">the domain to lookup in</param>
        /// <param name="name">The name of the logger to retrieve.</param>
        /// <returns>the logger with the name specified</returns>
        public static IMyLog GetLogger(string domain, string name)
        {
            return WrapLogger(LoggerManager.GetLogger(domain, name));
        }

        /// <summary>
        /// Retrieve or create a named logger.
        /// </summary>
        /// <remarks>
        /// <para>Retrieve a logger named as the <paramref name="name"/>
        /// parameter. If the named logger already exists, then the
        /// existing instance will be returned. Otherwise, a new instance is
        /// created.</para>
        /// 
        /// <para>By default, loggers do not have a set level but inherit
        /// it from the hierarchy. This is one of the central features of
        /// log4net.</para>
        /// </remarks>
        /// <param name="assembly">the assembly to use to lookup the domain</param>
        /// <param name="name">The name of the logger to retrieve.</param>
        /// <returns>the logger with the name specified</returns>
        public static IMyLog GetLogger(Assembly assembly, string name)
        {
            return WrapLogger(LoggerManager.GetLogger(assembly, name));
        }

        /// <summary>
        /// Shorthand for <see cref="LogManager.GetLogger(string)"/>.
        /// </summary>
        /// <remarks>
        /// Get the logger for the fully qualified name of the type specified.
        /// </remarks>
        /// <param name="type">The full name of <paramref name="type"/> will 
        /// be used as the name of the logger to retrieve.</param>
        /// <returns>the logger with the name specified</returns>
        public static IMyLog GetLogger(Type type)
        {
            return GetLogger(Assembly.GetCallingAssembly(), type.FullName);
        }

        /// <summary>
        /// Shorthand for <see cref="LogManager.GetLogger(string)"/>.
        /// </summary>
        /// <remarks>
        /// Get the logger for the fully qualified name of the type specified.
        /// </remarks>
        /// <param name="domain">the domain to lookup in</param>
        /// <param name="type">The full name of <paramref name="type"/> will 
        /// be used as the name of the logger to retrieve.</param>
        /// <returns>the logger with the name specified</returns>
        public static IMyLog GetLogger(string domain, Type type)
        {
            return WrapLogger(LoggerManager.GetLogger(domain, type));
        }

        /// <summary>
        /// Shorthand for <see cref="LogManager.GetLogger(string)"/>.
        /// </summary>
        /// <remarks>
        /// Get the logger for the fully qualified name of the type specified.
        /// </remarks>
        /// <param name="assembly">the assembly to use to lookup the domain</param>
        /// <param name="type">The full name of <paramref name="type"/> will 
        /// be used as the name of the logger to retrieve.</param>
        /// <returns>the logger with the name specified</returns>
        public static IMyLog GetLogger(Assembly assembly, Type type)
        {
            return WrapLogger(LoggerManager.GetLogger(assembly, type));
        }

        #endregion

        #region Extension Handlers

        /// <summary>
        /// Lookup the wrapper object for the logger specified
        /// </summary>
        /// <param name="logger">the logger to get the wrapper for</param>
        /// <returns>the wrapper for the logger specified</returns>
        private static IMyLog WrapLogger(ILogger logger)
        {
            return (IMyLog)s_wrapperMap.GetWrapper(logger);
        }

        /// <summary>
        /// Lookup the wrapper objects for the loggers specified
        /// </summary>
        /// <param name="loggers">the loggers to get the wrappers for</param>
        /// <returns>Lookup the wrapper objects for the loggers specified</returns>
        private static IMyLog[] WrapLoggers(ILogger[] loggers)
        {
            IMyLog[] results = new IMyLog[loggers.Length];
            for (int i = 0; i < loggers.Length; i++)
            {
                results[i] = WrapLogger(loggers[i]);
            }
            return results;
        }

        /// <summary>
        /// Method to create the <see cref="ILoggerWrapper"/> objects used by
        /// this manager.
        /// </summary>
        /// <param name="logger">The logger to wrap</param>
        /// <returns>The wrapper for the logger specified</returns>
        private static ILoggerWrapper WrapperCreationHandler(ILogger logger)
        {
            return new MyLogImpl(logger);
        }

        #endregion
    }
}

配置文件及数据库建表语句

配置文件

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <configSections>
    <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net" />
  </configSections>

  <log4net debug="true">

    <appender name="RollingFileAppender" type="log4net.Appender.RollingFileAppender" >
      <file value="testlog.txt" />
      <appendToFile value="true" />
      <maxSizeRollBackups value="10" />
      <maximumFileSize value="100" />
      <rollingStyle value="Date" />
      <datePattern value="yyyy-MM-dd"/>
      <staticLogFileName value="true" />
      <layout type="log4net.Layout.PatternLayout">
        <conversionPattern value="%-5level %date %logger - [%message] - %property{OrderNum} - %property{UserName %newline" />
      </layout>
    </appender>


    <!-- 写入SqlServer数据库 -->
    <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" />

      <!-- SQL连接字符串-->
      <!--<connectionString value="Data Source=JALENAPC;Initial Catalog=erp;integrated security=false;Persist Security Info=True;User ID=sa;Password=privacy" />-->
      <ConnectionStringName value="Kelun.Properties.Settings.ConnectionString"/>

      <!--SQL写入语句-->
      <commandText value="INSERT INTO [Loging] (logDate, logLevel, logLogger, logOrder, logMessage, logUser, logException) VALUES (@logDate, @logLevel, @logLogger, @logOrder, @logMessage ,@logUser, @logException)" />


      <!-- 写入参数 -->
      <parameter>
        <parameterName value="@logDate" />
        <dbType value="DateTime" />
        <layout type="log4net.Layout.RawTimeStampLayout" />
      </parameter>

      <parameter>
        <parameterName value="@logLevel" />
        <dbType value="String" />
        <size value="50" />
        <layout type="log4net.Layout.PatternLayout">
          <conversionPattern value="%level" />
        </layout>
      </parameter>

      <parameter>
        <parameterName value="@logLogger" />
        <dbType value="String" />
        <size value="500" />
        <layout type="log4net.Layout.PatternLayout">
          <conversionPattern value="%logger" />
        </layout>
      </parameter>

      <parameter>
        <parameterName value="@logOrder" />
        <dbType value="String" />
        <size value="20" />
        <layout type="log4net.Layout.PatternLayout">
          <conversionPattern value="%property{OrderNum}" />
        </layout>
      </parameter>

      <parameter>
        <parameterName value="@logMessage" />
        <dbType value="String" />
        <size value="4000" />
        <!--<layout type="log4net.Layout.PatternLayout">-->
        <layout type="log4net.Layout.PatternLayout">
          <conversionPattern value="%message" />
        </layout>
      </parameter>

      <parameter>
        <parameterName value="@logUser" />
        <dbType value="String" />
        <size value="50" />
        <layout type="log4net.Layout.PatternLayout">
          <conversionPattern value="%property{UserName}" />
        </layout>
      </parameter>

      <parameter>
        <parameterName value="@logException" />
        <dbType value="String" />
        <size value="2000" />
        <layout type="log4net.Layout.ExceptionLayout" />
      </parameter>

    </appender>

    <root>
      <level value="ALL"/>
      <appender-ref ref="AdoNetAppender" />
      <appender-ref ref="RollingFileAppender" />
    </root>

    <!--<logger name="MyLogger">
      <level value="ALL"/>
      <appender-ref ref="AdoNetAppender" />
    </logger>-->

  </log4net>
</configuration>

建表语句

create table Loging (
   id                   int                  identity,
   logDate              datetime             null,
   logLevel             varchar(50)          null,
   logLogger            varchar(500)         null,
   logOrder             varchar(20)          null,
   logMessage           text                 null,
   logUser              varchar(50)          null,
   logException         text                 null,
   constraint PK_LOGING primary key (id)
)

使用

AssemblyInfo.cs 增加如下内容

// 自动配置Log4Net
[assembly: log4net.Config.XmlConfigurator(ConfigFile = "log4net.config",Watch =true)]
private static readonly IMyLog logger = MyLogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);

logger.Debug("只有消息的输出");
logger.Debug("日志内容", "OrderNum", "Jalena");
logger.Warn("Warn.......","OrderNum","Jalena");
logger.Error("Error...", "order..", "王麻子", new Exception("异常信息"));

最后来个数据库最终数据