• 设为首页
  • 点击收藏
  • 手机版
    手机扫一扫访问
    迪恩网络手机版
  • 关注官方公众号
    微信扫一扫关注
    公众号

[转]C#超高速高性能写日志代码开源 

原作者: [db:作者] 来自: [db:来源] 收藏 邀请
 

1、需求

需求很简单,就是在C#开发中高速写日志。比如在高并发,高流量的地方需要写日志。我们知道程序在操作磁盘时是比较耗时的,所以我们把日志写到磁盘上会有一定的时间耗在上面,这些并不是我们想看到的。

 

2、解决方案

2.1、简单原理说明

使用列队先缓存到内存,然后我们一直有个线程再从列队中写到磁盘上,这样就可以高速高性能的写日志了。因为速度慢的地方我们分离出来了,也就是说程序在把日志扔给列队后,程序的日志部分就算完成了,后面操作磁盘耗时的部分程序是不需要关心的,由另一个线程操作。

俗话说,鱼和熊掌不可兼得,这样会有一个问题,就是如果日志已经到列队了这个时候程序崩溃或者电脑断电都会导致日志部分丢失,但是有些地方为了高性能的写日志,是否可以忽略一些情况,请各位根据情况而定。

 

2.2、示例图

 

3、关键代码部分

这里写日志的部分LZ选用了比较常用的log4net,当然也可以选择其他的日志组件,比如nlog等等。

3.1、日志至列队部分

第一步我们首先需要把日志放到列队中,然后才能从列队中写到磁盘上。

        public void EnqueueMessage(string message, FlashLogLevel level, Exception ex = null)
        {
            if ((level == FlashLogLevel.Debug && _log.IsDebugEnabled)
             || (level == FlashLogLevel.Error && _log.IsErrorEnabled)
             || (level == FlashLogLevel.Fatal && _log.IsFatalEnabled)
             || (level == FlashLogLevel.Info && _log.IsInfoEnabled)
             || (level == FlashLogLevel.Warn && _log.IsWarnEnabled))
            {
                _que.Enqueue(new FlashLogMessage
                {
                    Message = "[" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss,fff") + "]\r\n" + message,
                    Level = level,
                    Exception = ex
                });

                // 通知线程往磁盘中写日志
                _mre.Set();
            }
        }

_log是log4net日志组件的ILog,其中包含了写日志,判断日志等级等功能,代码开始部分的if判断就是判断等级和现在的日志等级做对比,看是否需要写入列队,这样可以有效的提高日志的性能。

其中的_que是ConcurrentQueue列队。_mre是ManualResetEvent信号,ManualResetEvent是用来通知线程列队中有新的日志,可以从列队中写入磁盘了。当从列队中写完日志后,重新设置信号,在等待下次有新的日志到来。

 

3.2、列队到磁盘

从列队到磁盘我们需要有一个线程从列队写入磁盘,也就是说我们在程序启动时就要加载这个线程,比如asp.net中就要在global中的Application_Start中加载。

 

       /// <summary>
        /// 另一个线程记录日志,只在程序初始化时调用一次
        /// </summary>
        public void Register()
        {
            Thread t = new Thread(new ThreadStart(WriteLog));
            t.IsBackground = false;
            t.Start();
        }

        /// <summary>
        /// 从队列中写日志至磁盘
        /// </summary>
        private void WriteLog()
        {
            while (true)
            {
                // 等待信号通知
                _mre.WaitOne();

                FlashLogMessage msg;
                // 判断是否有内容需要如磁盘 从列队中获取内容,并删除列队中的内容
                while (_que.Count > 0 && _que.TryDequeue(out msg))
                {
                    // 判断日志等级,然后写日志
                    switch (msg.Level)
                    {
                        case FlashLogLevel.Debug:
                            _log.Debug(msg.Message, msg.Exception);
                            break;
                        case FlashLogLevel.Info:
                            _log.Info(msg.Message, msg.Exception);
                            break;
                        case FlashLogLevel.Error:
                            _log.Error(msg.Message, msg.Exception);
                            break;
                        case FlashLogLevel.Warn:
                            _log.Warn(msg.Message, msg.Exception);
                            break;
                        case FlashLogLevel.Fatal:
                            _log.Fatal(msg.Message, msg.Exception);
                            break;
                    }
                }

                // 重新设置信号
                _mre.Reset();
            }
        }

 

3.3、完整代码

 View Code

 

4、性能对比和应用

4.1、性能对比

经过测试发现

使用原始的log4net写入日志100000条数据需要:19104毫秒。

同样数据使用列队方式只需要251毫秒。

 

 

 

4.2、应用

4.2.1、需要在程序启动时注册,如asp.net 程序中在Global.asax中的Application_Start注册。

    public class MvcApplication : System.Web.HttpApplication
    {
        protected void Application_Start()
        {
            AreaRegistration.RegisterAllAreas();
            FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
            RouteConfig.RegisterRoutes(RouteTable.Routes);
            BundleConfig.RegisterBundles(BundleTable.Bundles);

            FlashLogger.Instance().Register();
        }
    }

 

4.2.2、在需要写入日志的地方直接调用FlashLogger的静态方法即可。

            FlashLogger.Debug("Debug");
            FlashLogger.Debug("Debug", new Exception("testexception"));
            FlashLogger.Info("Info");
            FlashLogger.Fatal("Fatal");
            FlashLogger.Error("Error");
            FlashLogger.Warn("Warn", new Exception("testexception"));

 

5、代码开源

https://github.com/Emrys5/Emrys.FlashLog

 

转自   http://www.cnblogs.com/emrys5/p/flashlog.html

1、需求

需求很简单,就是在C#开发中高速写日志。比如在高并发,高流量的地方需要写日志。我们知道程序在操作磁盘时是比较耗时的,所以我们把日志写到磁盘上会有一定的时间耗在上面,这些并不是我们想看到的。

 

2、解决方案

2.1、简单原理说明

使用列队先缓存到内存,然后我们一直有个线程再从列队中写到磁盘上,这样就可以高速高性能的写日志了。因为速度慢的地方我们分离出来了,也就是说程序在把日志扔给列队后,程序的日志部分就算完成了,后面操作磁盘耗时的部分程序是不需要关心的,由另一个线程操作。

俗话说,鱼和熊掌不可兼得,这样会有一个问题,就是如果日志已经到列队了这个时候程序崩溃或者电脑断电都会导致日志部分丢失,但是有些地方为了高性能的写日志,是否可以忽略一些情况,请各位根据情况而定。

 

2.2、示例图

 

3、关键代码部分

这里写日志的部分LZ选用了比较常用的log4net,当然也可以选择其他的日志组件,比如nlog等等。

3.1、日志至列队部分

第一步我们首先需要把日志放到列队中,然后才能从列队中写到磁盘上。

        public void EnqueueMessage(string message, FlashLogLevel level, Exception ex = null)
        {
            if ((level == FlashLogLevel.Debug && _log.IsDebugEnabled)
             || (level == FlashLogLevel.Error && _log.IsErrorEnabled)
             || (level == FlashLogLevel.Fatal && _log.IsFatalEnabled)
             || (level == FlashLogLevel.Info && _log.IsInfoEnabled)
             || (level == FlashLogLevel.Warn && _log.IsWarnEnabled))
            {
                _que.Enqueue(new FlashLogMessage
                {
                    Message = "[" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss,fff") + "]\r\n" + message,
                    Level = level,
                    Exception = ex
                });

                // 通知线程往磁盘中写日志
                _mre.Set();
            }
        }

_log是log4net日志组件的ILog,其中包含了写日志,判断日志等级等功能,代码开始部分的if判断就是判断等级和现在的日志等级做对比,看是否需要写入列队,这样可以有效的提高日志的性能。

其中的_que是ConcurrentQueue列队。_mre是ManualResetEvent信号,ManualResetEvent是用来通知线程列队中有新的日志,可以从列队中写入磁盘了。当从列队中写完日志后,重新设置信号,在等待下次有新的日志到来。

 

3.2、列队到磁盘

从列队到磁盘我们需要有一个线程从列队写入磁盘,也就是说我们在程序启动时就要加载这个线程,比如asp.net中就要在global中的Application_Start中加载。

 

       /// <summary>
        /// 另一个线程记录日志,只在程序初始化时调用一次
        /// </summary>
        public void Register()
        {
            Thread t = new Thread(new ThreadStart(WriteLog));
            t.IsBackground = false;
            t.Start();
        }

        /// <summary>
        /// 从队列中写日志至磁盘
        /// </summary>
        private void WriteLog()
        {
            while (true)
            {
                // 等待信号通知
                _mre.WaitOne();

                FlashLogMessage msg;
                // 判断是否有内容需要如磁盘 从列队中获取内容,并删除列队中的内容
                while (_que.Count > 0 && _que.TryDequeue(out msg))
                {
                    // 判断日志等级,然后写日志
                    switch (msg.Level)
                    {
                        case FlashLogLevel.Debug:
                            _log.Debug(msg.Message, msg.Exception);
                            break;
                        case FlashLogLevel.Info:
                            _log.Info(msg.Message, msg.Exception);
                            break;
                        case FlashLogLevel.Error:
                            _log.Error(msg.Message, msg.Exception);
                            break;
                        case FlashLogLevel.Warn:
                            _log.Warn(msg.Message, msg.Exception);
                            break;
                        case FlashLogLevel.Fatal:
                            _log.Fatal(msg.Message, msg.Exception);
                            break;
                    }
                }

                // 重新设置信号
                _mre.Reset();
            }
        }

 

3.3、完整代码

 View Code

 

4、性能对比和应用

4.1、性能对比

经过测试发现

使用原始的log4net写入日志100000条数据需要:19104毫秒。

同样数据使用列队方式只需要251毫秒。

 

 

 

4.2、应用

4.2.1、需要在程序启动时注册,如asp.net 程序中在Global.asax中的Application_Start注册。

    public class MvcApplication : System.Web.HttpApplication
    {
        protected void Application_Start()
        {
            AreaRegistration.RegisterAllAreas();
            FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
            RouteConfig.RegisterRoutes(RouteTable.Routes);
            BundleConfig.RegisterBundles(BundleTable.Bundles);

            FlashLogger.Instance().Register();
        }
    }

 

4.2.2、在需要写入日志的地方直接调用FlashLogger的静态方法即可。

            FlashLogger.Debug("Debug");
            FlashLogger.Debug("Debug", new Exception("testexception"));
            FlashLogger.Info("Info");
            FlashLogger.Fatal("Fatal");
            FlashLogger.Error("Error");
            FlashLogger.Warn("Warn", new Exception("testexception"));

 

5、代码开源

https://github.com/Emrys5/Emrys.FlashLog

 

转自   http://www.cnblogs.com/emrys5/p/flashlog.html


鲜花

握手

雷人

路过

鸡蛋
该文章已有0人参与评论

请发表评论

全部评论

专题导读
上一篇:
C++自学笔记_定义智能指针类_《C++Primer》发布时间:2022-07-13
下一篇:
使用C#调用外部Ping命令获取网络连接情况发布时间:2022-07-13
热门推荐
阅读排行榜

扫描微信二维码

查看手机版网站

随时了解更新最新资讯

139-2527-9053

在线客服(服务时间 9:00~18:00)

在线QQ客服
地址:深圳市南山区西丽大学城创智工业园
电邮:jeky_zhao#qq.com
移动电话:139-2527-9053

Powered by 互联科技 X3.4© 2001-2213 极客世界.|Sitemap