log4net:添加自定义字段并将日志存储到Oracle11g数据库中

为君一笑 提交于 2019-12-05 17:50:48

我的操作系统为Win7旗舰版,.NET版本为4.5,log4net版本为1.2.15,Oracle版本为11g。

使用log4net建立一个最简单的DEMO,可以参考我的上一篇博客:

http://my.oschina.net/Tsybius2014/blog/687750

log4net支持将日志打印到数据库中,将日志中指定的内容打印到数据库中特定字段的方法有多种,本文选取一种较为灵活的方式,即继承ILog接口建立子接口。

首先在Oracle数据库中建立一张表,建表SQL如下:

CREATE TABLE PROGRAM_LOG (
    DATETIME TIMESTAMP(3),
    THREAD VARCHAR2(255),
    LOG_LEVEL VARCHAR2(255),
    LOGGER VARCHAR2(255),
    SYS_CODE VARCHAR2(10),
    MESSAGE VARCHAR2(4000)
);

我建立的工程结构如下:

其中,log4net.config中保存了Appender相关配置

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <configSections>
    <section name="log4net" type="System.Configuration.IgnoreSectionHandler" />
  </configSections>
  <log4net>
    <!--数据库存储日志-->
    <appender name="ADONetAppender_Oracle" type="log4net.Appender.AdoNetAppender">
      <!--dll文件签名-->
      <connectionType value="System.Data.OracleClient.OracleConnection, System.Data.OracleClient, Version=4.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089" />
      <!--数据库连接字符串-->
      <connectionString value="Data Source=orcl;User Id=xxx;Password=xxx;" />
      <!--插入用SQL语句-->
      <commandText value="INSERT INTO PROGRAM_LOG (DATETIME, THREAD, LOG_LEVEL, LOGGER, SYS_CODE, MESSAGE) VALUES (:datetime, :thread, :log_level, :logger, :sys_code, :message)" />
      <bufferSize value="1" />
      <!--时间戳-->
      <parameter>
        <parameterName value=":datetime" />
        <dbType value="DateTime" />
        <layout type="log4net.Layout.RawTimeStampLayout" />
      </parameter>
      <!--线程号-->
      <parameter>
          <parameterName value=":thread" />
          <dbType value="String" />
          <size value="255" />
          <layout type="log4net.Layout.PatternLayout">
              <conversionPattern value="%thread" />
          </layout>
      </parameter>
      <!--日志等级-->
      <parameter>
          <parameterName value=":log_level" />
          <dbType value="String" />
          <size value="50" />
          <layout type="log4net.Layout.PatternLayout">
              <conversionPattern value="%level" />
          </layout>
      </parameter>
      <!--类名-->
      <parameter>
          <parameterName value=":logger" />
          <dbType value="String" />
          <size value="255" />
          <layout type="log4net.Layout.PatternLayout">
              <conversionPattern value="%logger" />
          </layout>
      </parameter>
      <!--模块代码-->
      <parameter>
        <parameterName value=":sys_code" />
        <dbType value="String" />
        <size value="10" />
        <layout type="log4net.Layout.PatternLayout" >
        <param name="ConversionPattern" value="%property{SysCode}"/>
        </layout>
      </parameter>
      <!--日志信息-->
      <parameter>
        <parameterName value=":message" />
        <dbType value="String" />
        <size value="1024" />
        <layout type="log4net.Layout.PatternLayout">
          <conversionPattern value="%message" />
        </layout>
      </parameter>
    </appender>
    <root>
      <appender-ref ref="ADONetAppender_Oracle" />
    </root>
  </log4net>
</configuration>

这里要说明一下:

1、dll文件的签名一定要写对。我的电脑中用的是“System.Data.OracleClient.OracleConnection, System.Data.OracleClient, Version=4.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089”,Version以VS中引用属性中显示的为准(注意不是在资源管理器里找到dll文件后查看属性界面看到的版本,那是.NET的版本,如4.0.30319.1),PublicKeyToken一般都是B77A5C561934E089,如果要确认可以在VS开发者命令行中使用SN工具查看。

2、 连接Oracle数据库需要手动添加引用 System.Data.OracleClient,数据库连接字符串connectionString就使用ADO.NET正常连接Oracle数据库时使用的连接字符串即可。

3、模块代码(SysCode)是我自定义的字段。

建立IEnhancedLog接口,继承自ILog接口:

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

namespace Log4NetTest
{
    public interface IEnhancedLog : ILog
    {
        void Debug(string sysCode, object message);
        void Debug(string sysCode, object message, Exception t);

        void Info(string sysCode, object message);
        void Info(string sysCode, object message, Exception t);

        void Warn(string sysCode, object message);
        void Warn(string sysCode, object message, Exception t);

        void Error(string sysCode, object message);
        void Error(string sysCode, object message, Exception t);

        void Fatal(string sysCode, object message);
        void Fatal(string sysCode, object message, Exception t);
    }
}

建立EnhancedLogImpl实现类,继承自LogImpl类和IEnhancedLog接口:

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

namespace Log4NetTest
{
    public class EnhancedLogImpl : LogImpl, IEnhancedLog
    {
        private readonly static Type ThisDeclaringType = typeof(EnhancedLogImpl);
        
        public EnhancedLogImpl(ILogger logger)
            : base(logger)
        {
        }

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

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

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

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

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

        public void Warn(string sysCode, object message, System.Exception t)
        {
            if (this.IsWarnEnabled)
            {
                LoggingEvent loggingEvent = new LoggingEvent(ThisDeclaringType, Logger.Repository, Logger.Name, Level.Warn, message, t);
                loggingEvent.Properties["SysCode"] = sysCode;
                Logger.Log(loggingEvent);
            }
        }

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

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

        public void Fatal(string sysCode, object message)
        {
            Fatal(sysCode, null);
        }

        public void Fatal(string sysCode, object message, System.Exception t)
        {
            if (this.IsFatalEnabled)
            {
                LoggingEvent loggingEvent = new LoggingEvent(ThisDeclaringType, Logger.Repository, Logger.Name, Level.Fatal, message, t);
                loggingEvent.Properties["SysCode"] = sysCode;
                Logger.Log(loggingEvent);
            }
        }

    }
}

建立EnhancedLogManager类,用于获取logger:

using log4net.Core;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace Log4NetTest
{
    public class EnhancedLogManager
    {
        private static readonly WrapperMap s_wrapperMap = new WrapperMap(new WrapperCreationHandler(WrapperCreationHandler));

        private EnhancedLogManager() { }

        public static IEnhancedLog Exists(string name)
        {
            return Exists(Assembly.GetCallingAssembly(), name);
        }

        public static IEnhancedLog Exists(string domain, string name)
        {
            return WrapLogger(LoggerManager.Exists(domain, name));
        }

        public static IEnhancedLog Exists(Assembly assembly, string name)
        {
            return WrapLogger(LoggerManager.Exists(assembly, name));
        }

        public static IEnhancedLog[] GetCurrentLoggers()
        {
            return GetCurrentLoggers(Assembly.GetCallingAssembly());
        }

        public static IEnhancedLog[] GetCurrentLoggers(string domain)
        {
            return WrapLoggers(LoggerManager.GetCurrentLoggers(domain));
        }

        public static IEnhancedLog[] GetCurrentLoggers(Assembly assembly)
        {
            return WrapLoggers(LoggerManager.GetCurrentLoggers(assembly));
        }

        public static IEnhancedLog GetLogger(string name)
        {
            return GetLogger(Assembly.GetCallingAssembly(), name);
        }

        public static IEnhancedLog GetLogger(string domain, string name)
        {
            return WrapLogger(LoggerManager.GetLogger(domain, name));
        }

        public static IEnhancedLog GetLogger(Assembly assembly, string name)
        {
            return WrapLogger(LoggerManager.GetLogger(assembly, name));
        }

        public static IEnhancedLog GetLogger(Type type)
        {
            return GetLogger(Assembly.GetCallingAssembly(), type.FullName);
        }

        public static IEnhancedLog GetLogger(string domain, Type type)
        {
            return WrapLogger(LoggerManager.GetLogger(domain, type));
        }

        public static IEnhancedLog GetLogger(Assembly assembly, Type type)
        {
            return WrapLogger(LoggerManager.GetLogger(assembly, type));
        }

        private static IEnhancedLog WrapLogger(ILogger logger)
        {
            return (IEnhancedLog)s_wrapperMap.GetWrapper(logger);
        }

        private static IEnhancedLog[] WrapLoggers(ILogger[] loggers)
        {
            IEnhancedLog[] results = new IEnhancedLog[loggers.Length];
            for (int i = 0; i < loggers.Length; i++)
            {
                results[i] = WrapLogger(loggers[i]);
            }
            return results;
        }

        private static ILoggerWrapper WrapperCreationHandler(ILogger logger)
        {
            return new EnhancedLogImpl(logger);
        }
    }
}

主函数代码如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using System.Threading;
using log4net;
using log4net.Config;
using System.Data.OracleClient;

namespace Log4NetTest
{
    class Program
    {
        static void Main(string[] args)
        {
            //加载log4net配置
            FileInfo configFile = new FileInfo(AppDomain.CurrentDomain.BaseDirectory + "log4net.config");
            XmlConfigurator.ConfigureAndWatch(configFile);

            IEnhancedLog logger = EnhancedLogManager.GetLogger(typeof(Program));
            logger.Debug("sys1", "调试类型日志");
            logger.Info("sys1", "一般类型日志");
            logger.Warn("sys2", "警告类型日志");
            logger.Error("sys3", "错误类型日志");
            //logger.Fatal("sys2", "致命错误日志"); //会导致StackOverFlow,原因未知

            try
            {
                int a = 0;
                int b = 0;
                int i = a / b;
            }
            catch (Exception ex)
            {
                logger.Error("sys3", ex);
            }

            Console.WriteLine("日志输出完毕");
            Console.Read();
        }
    }
}

程序执行结果如下:

这段代码有一个问题到现在我也没找到原因,就是调用logger.Fatal时总会提示StackOverFlow异常~~~╮(╯▽╰)╭

刚开始调试log4net有一个比较痛苦的地方,就是log4net的日志并不会打印到控制台上,在出现问题后总会让人感觉丈二和尚摸不着头脑。不过后来我发现,在App.config文件中加入这句话就可以了:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <appSettings>
    <!--↓↓↓加入这句话↓↓↓-->
    <add key="log4net.Internal.Debug" value="true "/>
  </appSettings>
  <startup> 
      <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
  </startup>
</configuration>

添加了该项配置后,执行上面程序时,控制台中打印的log4net运行日志如下:

log4net: log4net assembly [log4net, Version=1.2.15.0, Culture=neutral, PublicKeyToken=669e0ddf0bb1aa2a]. Loaded from [D:\MyPrograms\Log4NetTest\Log4NetTest\bin\
Debug\log4net.dll]. (.NET Runtime [4.0.30319.18444] on Microsoft Windows NT 6.1.7601 Service Pack 1)
log4net: defaultRepositoryType [log4net.Repository.Hierarchy.Hierarchy]
log4net: Creating repository for assembly [Log4NetTest, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]
log4net: Assembly [Log4NetTest, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null] Loaded From [D:\MyPrograms\Log4NetTest\Log4NetTest\bin\Debug\Log4NetTest.
exe]
log4net: Assembly [Log4NetTest, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null] does not have a RepositoryAttribute specified.
log4net: Assembly [Log4NetTest, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null] using repository [log4net-default-repository] and repository type [log4ne
t.Repository.Hierarchy.Hierarchy]
log4net: Creating repository [log4net-default-repository] using type [log4net.Repository.Hierarchy.Hierarchy]
log4net: configuring repository [log4net-default-repository] using file [D:\MyPrograms\Log4NetTest\Log4NetTest\bin\Debug\log4net.config] watching for file updat
es
log4net: configuring repository [log4net-default-repository] using file [D:\MyPrograms\Log4NetTest\Log4NetTest\bin\Debug\log4net.config]
log4net: configuring repository [log4net-default-repository] using stream
log4net: loading XML configuration
log4net: Configuring Repository [log4net-default-repository]
log4net: Configuration update mode [Merge].
log4net: Loading Appender [ADONetAppender_Oracle] type: [log4net.Appender.AdoNetAppender]
log4net: Setting Property [ConnectionType] to String value [System.Data.OracleClient.OracleConnection, System.Data.OracleClient, Version=4.0.0.0, Culture=neutra
l, PublicKeyToken=B77A5C561934E089]
log4net: Setting Property [ConnectionString] to String value [Data Source=orcl;User Id=XXXX;Password=XXXX;]
log4net: Setting Property [CommandText] to String value [INSERT INTO PROGRAM_LOG (DATETIME, THREAD, LOG_LEVEL, LOGGER, SYS_CODE, MESSAGE) VALUES (:datetime, :th
read, :log_level, :logger, :sys_code, :message)]
log4net: Setting Property [BufferSize] to Int32 value [1]
log4net: Setting Property [ParameterName] to String value [:datetime]
log4net: Setting Property [DbType] to DbType value [DateTime]
log4net: Setting Property [Layout] to object [log4net.Layout.RawTimeStampLayout]

log4net: Setting Collection Property [AddParameter] to object [log4net.Appender.AdoNetAppenderParameter]
log4net: Setting Property [ParameterName] to String value [:thread]
log4net: Setting Property [DbType] to DbType value [String]
log4net: Setting Property [Size] to Int32 value [255]
log4net: Converter [message] Option [] Format [min=-1,max=2147483647,leftAlign=False]
log4net: Converter [newline] Option [] Format [min=-1,max=2147483647,leftAlign=False]
log4net: Setting Property [ConversionPattern] to String value [%thread]
log4net: Converter [thread] Option [] Format [min=-1,max=2147483647,leftAlign=False]
log4net: Setting Property [Layout] to object [log4net.Layout.Layout2RawLayoutAdapter]
log4net: Setting Collection Property [AddParameter] to object [log4net.Appender.AdoNetAppenderParameter]
log4net: Setting Property [ParameterName] to String value [:log_level]
log4net: Setting Property [DbType] to DbType value [String]
log4net: Setting Property [Size] to Int32 value [50]
log4net: Converter [message] Option [] Format [min=-1,max=2147483647,leftAlign=False]
log4net: Converter [newline] Option [] Format [min=-1,max=2147483647,leftAlign=False]
log4net: Setting Property [ConversionPattern] to String value [%level]
log4net: Converter [level] Option [] Format [min=-1,max=2147483647,leftAlign=False]
log4net: Setting Property [Layout] to object [log4net.Layout.Layout2RawLayoutAdapter]
log4net: Setting Collection Property [AddParameter] to object [log4net.Appender.AdoNetAppenderParameter]
log4net: Setting Property [ParameterName] to String value [:logger]
log4net: Setting Property [DbType] to DbType value [String]
log4net: Setting Property [Size] to Int32 value [255]
log4net: Converter [message] Option [] Format [min=-1,max=2147483647,leftAlign=False]
log4net: Converter [newline] Option [] Format [min=-1,max=2147483647,leftAlign=False]
log4net: Setting Property [ConversionPattern] to String value [%logger]
log4net: Converter [logger] Option [] Format [min=-1,max=2147483647,leftAlign=False]
log4net: Setting Property [Layout] to object [log4net.Layout.Layout2RawLayoutAdapter]
log4net: Setting Collection Property [AddParameter] to object [log4net.Appender.AdoNetAppenderParameter]
log4net: Setting Property [ParameterName] to String value [:sys_code]
log4net: Setting Property [DbType] to DbType value [String]
log4net: Setting Property [Size] to Int32 value [10]
log4net: Converter [message] Option [] Format [min=-1,max=2147483647,leftAlign=False]
log4net: Converter [newline] Option [] Format [min=-1,max=2147483647,leftAlign=False]
log4net: Setting Property [ConversionPattern] to String value [%property{SysCode}]
log4net: Converter [property] Option [SysCode] Format [min=-1,max=2147483647,leftAlign=False]
log4net: Setting Property [Layout] to object [log4net.Layout.Layout2RawLayoutAdapter]
log4net: Setting Collection Property [AddParameter] to object [log4net.Appender.AdoNetAppenderParameter]
log4net: Setting Property [ParameterName] to String value [:message]
log4net: Setting Property [DbType] to DbType value [String]
log4net: Setting Property [Size] to Int32 value [1024]
log4net: Converter [message] Option [] Format [min=-1,max=2147483647,leftAlign=False]
log4net: Converter [newline] Option [] Format [min=-1,max=2147483647,leftAlign=False]
log4net: Setting Property [ConversionPattern] to String value [%message]
log4net: Converter [message] Option [] Format [min=-1,max=2147483647,leftAlign=False]
log4net: Setting Property [Layout] to object [log4net.Layout.Layout2RawLayoutAdapter]
log4net: Setting Collection Property [AddParameter] to object [log4net.Appender.AdoNetAppenderParameter]
log4net: Created Appender [ADONetAppender_Oracle]
log4net: Adding appender named [ADONetAppender_Oracle] to logger [root].
log4net: Hierarchy Threshold []
日志输出完毕

参考资料:

扩展Log4Net中的ILog实现自定义日志字段

http://www.cnblogs.com/hb_cattle/articles/1560778.html

Apache log4net™ Config Examples

http://logging.apache.org/log4net/release/config-examples.html

END

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!