MyException - 我的异常网
当前位置:我的异常网» C# » 筹建一套自己实用的.net架构(2)【日志模块-log4net】

筹建一套自己实用的.net架构(2)【日志模块-log4net】

www.MyException.Cn  网友分享于:2015-08-26  浏览:0次
搭建一套自己实用的.net架构(2)【日志模块-log4net】

       先谈谈简单的模块,日志。在系统中日志模块是必须的,什么系统日志,操作日志,调试日志。这里用的是log4net

对log4net还不熟悉的小伙伴们赶快去搜索基础教程哦, 我这里就不温故了。

       那么有人要问了,log4net确实很强大,而且我们也会用。还要单独写一篇文章来介绍,有必要吗?

我简单的举两个场景:

1:log4net写入DB 还需要在 log4net中配置数据库连接字符串,   我想log4net 和 我的 connectionStrings 用1个配置不行吗?

2:log4net写入参数扩展问题,我配置文件想写入ip地址,那我代码还要定义一个ip的参数。 那我再扩展,还需要再定义,这改动量太大了,能不能只传一个实体类,让log4net自己去映射那?这样我就可以写一些通用的方法,好多项目都可以直接拿过来用,代码修改量也少了点。

有人觉得这都不是问题,高手请跳过。

我这里将日志模块单独封装了一个HY.Log,截图如下:

 看图有点乱,下面我给大家捋一捋(念lv  念成lu的去面壁思过  ):

 

第一步:实现自定义参数

我们要在PatternConverter文件夹中定义一些可扩展参数,这样就可以在log4net配置文件中随心使用了,哪截图中我实现了获取客户端ip、获取服务器端ip

或许服务器mac地址。

就拿ClientIpPatternConverter.cs来说需要继承log4net下  log4net.Layout.Pattern.PatternLayoutConverter类,来实现扩展。

using System.IO;
using log4net.Core;
using log4net.Layout.Pattern;

namespace HY.Log.PatternConverter
{
    /// <summary>
    /// B/S 客户端地址扩展
    /// </summary>
    internal class ClientIpPatternConverter : PatternLayoutConverter
    {
        protected override void Convert(TextWriter writer, LoggingEvent loggingEvent)
        {
            writer.Write(HY.Utilities.IPHelper.GetRemoteIPAddress());           
        }
    }
}

 接着在分别实现 MAC、服务端IP、 以及其它你任何想扩展的参数。

 

ObjectPatternConverter.cs 这个类就比较特殊了。这个自定义参数其实是让你传入一个实体类,然后通过反射技术,让log4net通过配置文件的 配置自动映射要传入的值。

上面的类 是针对特定的通用功能扩展,这个类只需要定义一个即可。

using System.IO;
using System.Reflection;
using log4net.Core;
using log4net.Layout.Pattern;

namespace HY.Log.PatternConverter
{
    internal class ObjectPatternConverter : PatternLayoutConverter
    {
        protected override void Convert(TextWriter writer, 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>
        /// <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;
        }
    }
}

 

 

 第二步:实现log4net自定义布局,将自定义参数进行注册

 CustomLayout.cs

using HY.Log.PatternConverter;
using log4net.Layout;

namespace HY.Log
{
    /// <summary>
    /// 定义log日志布局的参数信息
    /// </summary>
    public class CustomLayout : PatternLayout
    {
        /// <summary>
        /// 构造函数
        /// </summary>
        public CustomLayout()
        {
            #region 内部自定义
            AddConverter("ServerIP", typeof(ServerIpPatternConverter));
            AddConverter("ClientIP", typeof(ClientIpPatternConverter));
            AddConverter("MAC", typeof(MacPatternConverter));
            #endregion

            #region 支持开发人员自定义
            AddConverter("Object", typeof(ObjectPatternConverter));
            #endregion
        }
    }
}

代码将自定义的参数以 key value 的形式进行注册, 以后我们再log4net进行配置的时候 就要记住这些关键字了, 这都是你自己定义的。

你敢在代码中实现  AddConverter("XXOO", typeof(XXOOPatternConverter));  吗?

 

 

 

 第三步:定义自己框架的Log接口并实现它

没啥可说的,看代码吧。

LogMessage.cs   这个类其实就是写了一个传入的的自定义参数的实体类,你可以自己写。不过最好是继承这个类进行扩展。

using System;

namespace HY.Log
{
    /// <summary>
    /// 用于记录日志信息
    /// </summary>
    [Serializable]
    public class LogMessage
    {
        /// <summary>
        /// 日志信息
        /// </summary>
        public string Message { get; set; }

    }
}

 

 

ILog.cs

using System;

namespace HY.Log
{
    public interface ILog
    {
        /// <summary>
        /// 写入Debug日志,
        /// </summary>
        /// <param name="message">日志信息,占位符为 %Object{Message}</param>
        void Debug(string message, Exception exception = null);

        /// <summary>
        /// 写入Debug日志
        /// </summary>
        /// <param name="messageEntity">日志实体信息, 占位符为 %Object{实体类的属性名称}</param>
        void Debug(object messageEntity, Exception exception = null);


        /// <summary>
        /// 写入Info日志,
        /// </summary>
        /// <param name="message">日志信息,占位符为 %Object{Message}</param>
        void Info(string message, Exception exception = null);

        /// <summary>
        /// 写入Info日志
        /// </summary>
        /// <param name="messageEntity">日志实体信息, 占位符为 %Object{实体类的属性名称}</param>
        void Info(object messageEntity, Exception exception = null);

        /// <summary>
        /// 写入Warn日志,
        /// </summary>
        /// <param name="message">日志信息,占位符为 %Object{Message}</param>
        void Warn(string message, Exception exception = null);

        /// <summary>
        /// 写入Warn日志
        /// </summary>
        /// <param name="messageEntity">日志实体信息, 占位符为 %Object{实体类的属性名称}</param>
        void Warn(object messageEntity, Exception exception = null);

        /// <summary>
        /// 写入Error日志,
        /// </summary>
        /// <param name="message">日志信息,占位符为 %Object{Message}</param>
        void Error(string message, Exception exception = null);

        /// <summary>
        /// 写入Error日志
        /// </summary>
        /// <param name="messageEntity">日志实体信息, 占位符为 %Object{实体类的属性名称}</param>
        void Error(object messageEntity, Exception exception = null);


        /// <summary>
        /// 写入Fatal日志,
        /// </summary>
        /// <param name="message">日志信息,占位符为 %Object{Message}</param>
        void Fatal(string message, Exception exception = null);

        /// <summary>
        /// 写入Fatal日志
        /// </summary>
        /// <param name="messageEntity">日志实体信息, 占位符为 %Object{实体类的属性名称}</param>
        void Fatal(object messageEntity, Exception exception = null);


    }
}

 

NormalLog.cs

using System;
using System.Collections.Generic;

namespace HY.Log
{
    public class NormalLog : ILog
    {
        #region private
        private static Dictionary<string, log4net.ILog> listILog = new Dictionary<string, log4net.ILog>();   //Log对象集合
        private log4net.ILog iLog;  //当前日志对象的实例
        #endregion

        #region 构造函数


        /// <summary>
        /// 构造函数,传入Log4NET 的ILog对象
        /// </summary>
        /// <param name="logger"></param>
        public NormalLog(log4net.ILog logger)
        {
            string LoggerName = logger.Logger.Name;     //logger 配置节名称
            if (!listILog.ContainsKey(LoggerName))
            {
                lock (listILog)
                {
                    if (!listILog.ContainsKey(LoggerName))
                    {
                        listILog.Add(LoggerName, logger);
                    }
                    else
                    {
                        listILog[LoggerName] = logger;
                    }
                }
            }
            else if (listILog[LoggerName] == null)
            {
                listILog[LoggerName] = logger;
            }
            iLog = listILog[LoggerName];
        }


        public NormalLog(string loggerName)
        {
            log4net.ILog logger = log4net.LogManager.GetLogger(loggerName);
            string LoggerName = logger.Logger.Name;     //logger 配置节名称
            if (!listILog.ContainsKey(LoggerName))
            {
                listILog.Add(LoggerName, logger);
            }
            else if (listILog[LoggerName] == null)
            {
                listILog[LoggerName] = logger;
            }
            iLog = listILog[LoggerName];
        }

        #endregion

        #region 写入日志

        /// <summary>
        /// 写入Debug日志,
        /// </summary>
        /// <param name="message">日志信息,占位符为 %Object{Message}</param>
        public void Debug(string message, Exception exception = null)
        {
            LogMessage messageEntity = new LogMessage
            {
                Message = message
            };
            Debug(messageEntity, exception);
        }

        /// <summary>
        /// 写入Debug日志
        /// </summary>
        /// <param name="messageEntity">日志实体信息, 占位符为 %Object{实体类的属性名称}</param>
        public void Debug(object messageEntity, Exception exception = null)
        {
            if (iLog.IsDebugEnabled)
            {
                if (exception != null)
                {
                    iLog.Debug(messageEntity, exception);
                }
                else
                {
                    iLog.Debug(messageEntity);
                }
            }
        }

        /// <summary>
        /// 写入Info日志,
        /// </summary>
        /// <param name="message">日志信息,占位符为 %Object{Message}</param>
        public void Info(string message, Exception exception = null)
        {
            LogMessage messageEntity = new LogMessage
            {
                Message = message
            };
            Info(messageEntity, exception);
        }

        /// <summary>
        /// 写入Info日志
        /// </summary>
        /// <param name="messageEntity">日志实体信息, 占位符为 %Object{实体类的属性名称}</param>
        public void Info(object messageEntity, Exception exception = null)
        {
            if (iLog.IsInfoEnabled)
            {
                if (exception != null)
                {
                    iLog.Info(messageEntity, exception);
                }
                else
                {
                    iLog.Info(messageEntity);
                }
            }
        }


        /// <summary>
        /// 写入Warn日志,
        /// </summary>
        /// <param name="message">日志信息,占位符为 %Object{Message}</param>
        public void Warn(string message, Exception exception = null)
        {
            LogMessage messageEntity = new LogMessage
            {
                Message = message
            };
            Warn(messageEntity, exception);
        }


        /// <summary>
        /// 写入Warn日志
        /// </summary>
        /// <param name="messageEntity">日志实体信息, 占位符为 %Object{实体类的属性名称}</param>
        public void Warn(object messageEntity, Exception exception = null)
        {
            if (iLog.IsWarnEnabled)
            {
                if (exception != null)
                {
                    iLog.Warn(messageEntity, exception);
                }
                else
                {
                    iLog.Warn(messageEntity);
                }
            }
        }



        /// <summary>
        /// 写入Error日志,
        /// </summary>
        /// <param name="message">日志信息,占位符为 %Object{Message}</param>
        public void Error(string message, Exception exception = null)
        {
            LogMessage messageEntity = new LogMessage
            {
                Message = message
            };
            Error(messageEntity, exception);
        }

        /// <summary>
        /// 写入Error日志
        /// </summary>
        /// <param name="messageEntity">日志实体信息, 占位符为 %Object{实体类的属性名称}</param>
        public void Error(object messageEntity, Exception exception = null)
        {
            if (iLog.IsErrorEnabled)
            {
                if (exception != null)
                {
                    iLog.Error(messageEntity, exception);
                }
                else
                {
                    iLog.Error(messageEntity);
                }
            }
        }

        /// <summary>
        /// 写入Fatal日志,
        /// </summary>
        /// <param name="message">日志信息,占位符为 %Object{Message}</param>
        public void Fatal(string message, Exception exception = null)
        {
            LogMessage messageEntity = new LogMessage
            {
                Message = message
            };
            Fatal(messageEntity, exception);
        }

        /// <summary>
        /// 写入Fatal日志
        /// </summary>
        /// <param name="messageEntity">日志实体信息, 占位符为 %Object{实体类的属性名称}</param>
        public void Fatal(object messageEntity, Exception exception = null)
        {
            if (iLog.IsFatalEnabled)
            {
                if (exception != null)
                {
                    iLog.Fatal(messageEntity, exception);
                }
                else
                {
                    iLog.Fatal(messageEntity);
                }
            }
        }
        #endregion

    }
}

 

  第四步:写一下log模块的支持方法(这里有实现log4net配置和连接字符串共用一个配置的方法)

 

 LogManager.cs

这个类定义的了log4net模块的初始化,数据库连接设置……

using System;
using System.IO;
using log4net.Appender;
using log4net.Config;
using log4net.Repository.Hierarchy;

namespace HY.Log
{
    public class LogManager
    {

        /// <summary>
        /// 设置DB连接字符串
        /// </summary>
        /// <param name="conString">连接字符串</param>
        /// <param name="loggerName">loggerName</param>
        /// <param name="appenderName">appenderName</param>
        public static void ConfigConnection(string conString, string loggerName, string appenderName = "ADONetAppender")
        {
            try
            {
                Hierarchy h = log4net.LogManager.GetRepository() as Hierarchy;
                if (h != null)
                {
                    AdoNetAppender adoAppender = (AdoNetAppender)h.GetLogger(loggerName,
                        h.LoggerFactory).GetAppender(appenderName);
                    if (adoAppender != null)
                    {
                        adoAppender.ConnectionString = conString;
                        adoAppender.ActivateOptions();
                    }
                }

            }
            catch (NullReferenceException) { }
        }


        /// <summary>
        /// 初始化HY.Log,  Log配置文件需要写到 Web.config  OR  App.config
        /// </summary>
        public static void Init()
        {
            XmlConfigurator.Configure();
        }

        /// <summary>
        /// 初始化HY.Log, 
        /// </summary>
        /// <param name="configFileName">制定Log配置文件的文件绝对路径</param>
        public static void Init(string configFileName)
        {
            XmlConfigurator.Configure(new FileInfo(configFileName));
        }



        /// <summary>
        /// 检索Logger名称返回日志处理接口
        /// </summary>
        /// <param name="name">Logger名称</param>
        /// <returns>日志接口</returns>
        public static ILog GetLogger(string name)
        {
            var log4Logger = log4net.LogManager.GetLogger(name);
            return new NormalLog(log4Logger);
        }


    }
}

 

 第五步:如何使用HY.Log.DLL

上面的代码大家看到了,已经可以成功编译一个dll 文件了。


先来编辑一个log4net配置文件

<?xml version="1.0"?>
<configuration>
  <!--Log4net Begin-->
  <configSections>
    <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net" />
  </configSections>
  <log4net>
    <!--Log4net Begin,程序运行异常记录-->

    <logger name="LogInfoDB">
      <level value="ALL" />
      <appender-ref ref="ADONetAppender" />
    </logger>
    <appender name="ADONetAppender" type="log4net.Appender.ADONetAppender">
      <bufferSize value="1" />
      <connectionType value="System.Data.SqlClient.SqlConnection, System.Data, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
      <!--<connectionString value="Data Source=127.0.0.1;Database=test;uid=sa;pwd=test;Max Pool Size=300;Connect Timeout=15;" />-->
      <commandText value="INSERT INTO HY_Log ([LogType],[ModelName],[Message],[Exception],[IP],[Log_Date],[UserID]) VALUES (@LogType,@ModelName, @Message, @Exception, @IP,@Log_Date,@UserID)" />
      <parameter>
        <parameterName value="@LogType" />
        <dbType value="String" />
        <size value="20" />
        <layout type="HY.Log.CustomLayout">
          <conversionPattern value="%Object{LogType}" />
        </layout>
      </parameter>
      <parameter>
        <parameterName value="@ModelName" />
        <dbType value="String" />
        <size value="110" />
        <layout type="HY.Log.CustomLayout">
          <conversionPattern value="%Object{ModelName}" />
        </layout>
      </parameter>
      <parameter>
        <parameterName value="@Message" />
        <dbType value="String" />
        <size value="2000" />
        <layout type="HY.Log.CustomLayout">
          <conversionPattern value="%Object{Message}" />
        </layout>
      </parameter>
      <parameter>
        <parameterName value="@Exception" />
        <dbType value="String" />
        <size value="8000" />
        <layout type="HY.Log.CustomLayout">
          <conversionPattern value="%Object{Exception}" />
        </layout>
      </parameter>
      <parameter>
        <parameterName value="@IP" />
        <dbType value="String" />
        <size value="20" />
        <layout type="HY.Log.CustomLayout">
          <conversionPattern value="%ClientIP" />
        </layout>
      </parameter>
      <parameter>
        <parameterName value="@Log_Date" />
        <dbType value="DateTime" />
        <layout type="log4net.Layout.RawTimeStampLayout" />
      </parameter>
      <parameter>
        <parameterName value="@UserID" />
        <dbType value="String" />
        <size value="100" />
        <layout type="HY.Log.CustomLayout">
          <conversionPattern value="%Object{UserID}" />
        </layout>
      </parameter>
    </appender>

  </log4net>
  <!--Log4net End-->
</configuration>


<!--%m(message):输出的日志消息,如ILog.Debug(…)输出的一条消息
      %n(new line):换行
      %d(datetime):输出当前语句运行的时刻
      %r(run time):输出程序从运行到执行到当前语句时消耗的毫秒数
      %t(thread id):当前语句所在的线程ID
      %p(priority): 日志的当前优先级别,即DEBUG、INFO、WARN…等
      %c(class):当前日志对象的名称,例如:
      %f(file):输出语句所在的文件名。
      %l(line):输出语句所在的行号。-->

 上面的配置文件是一个写入DB的配置

请注意如下配置节:

<layout type="HY.Log.CustomLayout">
<conversionPattern value="%Object{Exception}" />
<conversionPattern value="%Object{LogType}" />
<conversionPattern value="%ClientIP" />
这便是采用了自定义配置。


创建HY.ILog对象
public static ILog ilog = null;

HY.Log.LogManager.Init(HttpContext.Current.Server.MapPath(@"~/Config/log4net.config"));//加载配置文件
HY.Log.LogManager.ConfigConnection(ConfigurationManager.ConnectionStrings["Connection"].ToString(), "LogInfoDB");//修改连接字符串
ilog = LogManager.GetLogger("LogInfoText");     //获取Ilog对象 这里可以采用单例模式。代码就不贴了,


自定义实体类:
 [Serializable]
    public class LogEntity
    {
        /// <summary>
        /// 日志类型
        /// </summary>
        public LogType LogType { get; set; }

        /// <summary>
        /// 模块名称
        /// </summary>
        public string ModelName { get; set; }

        /// <summary>
        /// 信息
        /// </summary>
        public new string Message { get; set; }

        /// <summary>
        /// 异常信息
        /// </summary>
        public string Exception { get; set; }

        public string UserID { get; set; }

 


LogEntity loginfo = new LogEntity();
loginfo.ModelName = "ModelName";
           loginfo.Message = "Message";
           loginfo.Exception = "Exception";
           loginfo.UserID = "UserID";
           loginfo.LogType = "LogType.Business";
ilog.Error(loginfo,ex);
 
 到这里就已经完了。  关于使用的代码比较粗糙,这块就需要根据你程序的具体需求来实现了。这里点到为止。  因为后续我们将会采用spring.net来注入。 将在后续的文章介绍。


各位如果有更好的建立请给我留言, 请各位不吝赐教。
 

 

相关文章:

搭建一套自己实用的.net架构(1)【概述】

搭建一套自己实用的.net架构(2)【日志模块-log4net】

文章评论

Web开发者需具备的8个好习惯
Web开发者需具备的8个好习惯
每天工作4小时的程序员
每天工作4小时的程序员
程序员和编码员之间的区别
程序员和编码员之间的区别
Google伦敦新总部 犹如星级庄园
Google伦敦新总部 犹如星级庄园
Java程序员必看电影
Java程序员必看电影
当下全球最炙手可热的八位少年创业者
当下全球最炙手可热的八位少年创业者
鲜为人知的编程真相
鲜为人知的编程真相
程序员眼里IE浏览器是什么样的
程序员眼里IE浏览器是什么样的
程序员最害怕的5件事 你中招了吗?
程序员最害怕的5件事 你中招了吗?
程序员应该关注的一些事儿
程序员应该关注的一些事儿
2013年中国软件开发者薪资调查报告
2013年中国软件开发者薪资调查报告
为啥Android手机总会越用越慢?
为啥Android手机总会越用越慢?
写给自己也写给你 自己到底该何去何从
写给自己也写给你 自己到底该何去何从
旅行,写作,编程
旅行,写作,编程
看13位CEO、创始人和高管如何提高工作效率
看13位CEO、创始人和高管如何提高工作效率
那些性感的让人尖叫的程序员
那些性感的让人尖叫的程序员
为什么程序员都是夜猫子
为什么程序员都是夜猫子
程序员的一天:一寸光阴一寸金
程序员的一天:一寸光阴一寸金
老程序员的下场
老程序员的下场
总结2014中国互联网十大段子
总结2014中国互联网十大段子
60个开发者不容错过的免费资源库
60个开发者不容错过的免费资源库
 程序员的样子
程序员的样子
不懂技术不要对懂技术的人说这很容易实现
不懂技术不要对懂技术的人说这很容易实现
漫画:程序员的工作
漫画:程序员的工作
一个程序员的时间管理
一个程序员的时间管理
5款最佳正则表达式编辑调试器
5款最佳正则表达式编辑调试器
程序员必看的十大电影
程序员必看的十大电影
Web开发人员为什么越来越懒了?
Web开发人员为什么越来越懒了?
老美怎么看待阿里赴美上市
老美怎么看待阿里赴美上市
10个调试和排错的小建议
10个调试和排错的小建议
10个帮程序员减压放松的网站
10个帮程序员减压放松的网站
如何成为一名黑客
如何成为一名黑客
要嫁就嫁程序猿—钱多话少死的早
要嫁就嫁程序猿—钱多话少死的早
我的丈夫是个程序员
我的丈夫是个程序员
我跳槽是因为他们的显示器更大
我跳槽是因为他们的显示器更大
编程语言是女人
编程语言是女人
十大编程算法助程序员走上高手之路
十大编程算法助程序员走上高手之路
程序猿的崛起——Growth Hacker
程序猿的崛起——Growth Hacker
2013年美国开发者薪资调查报告
2013年美国开发者薪资调查报告
团队中“技术大拿”并非越多越好
团队中“技术大拿”并非越多越好
科技史上最臭名昭著的13大罪犯
科技史上最臭名昭著的13大罪犯
程序员周末都喜欢做什么?
程序员周末都喜欢做什么?
我是如何打败拖延症的
我是如何打败拖延症的
那些争议最大的编程观点
那些争议最大的编程观点
中美印日四国程序员比较
中美印日四国程序员比较
程序员都该阅读的书
程序员都该阅读的书
什么才是优秀的用户界面设计
什么才是优秀的用户界面设计
亲爱的项目经理,我恨你
亲爱的项目经理,我恨你
Java 与 .NET 的平台发展之争
Java 与 .NET 的平台发展之争
软件开发程序错误异常ExceptionCopyright © 2009-2015 MyException 版权所有