您的位置:首页 > 编程语言 > C#

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

2018-02-06 11:19 232 查看

1、需求

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

2、解决方案

2.1、简单原理说明

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

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

2.2、示例图




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

namespace Emrys.FlashLog
{
public sealed class FlashLogger
{
/// <summary>
/// 记录消息Queue
/// </summary>
private readonly ConcurrentQueue<FlashLogMessage> _que;

/// <summary>
/// 信号
/// </summary>
private readonly ManualResetEvent _mre;

/// <summary>
/// 日志
/// </summary>
private readonly ILog _log;

/// <summary>
/// 日志
/// </summary>
private static FlashLogger _flashLog = new FlashLogger();

private FlashLogger()
{
var configFile = new FileInfo(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "log4net.config"));
if (!configFile.Exists)
{
throw new Exception("未配置log4net配置文件!");
}

// 设置日志配置文件路径
XmlConfigurator.Configure(configFile);

_que = new ConcurrentQueue<FlashLogMessage>();
_mre = new ManualResetEvent(false);
_log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
}

/// <summary>
/// 实现单例
/// </summary>
/// <returns></returns>
public static FlashLogger Instance()
{
return _flashLog;
}

/// <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();
Thread.Sleep(1);
}
}

/// <summary>
/// 写日志
/// </summary>
/// <param name="message">日志文本</param>
/// <param name="level">等级</param>
/// <param name="ex">Exception</param>
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();
}
}

public static void Debug(string msg, Exception ex = null)
{
Instance().EnqueueMessage(msg, FlashLogLevel.Debug, ex);
}

public static void Error(string msg, Exception ex = null)
{
Instance().EnqueueMessage(msg, FlashLogLevel.Error, ex);
}

public static void Fatal(string msg, Exception ex = null)
{
Instance().EnqueueMessage(msg, FlashLogLevel.Fatal, ex);
}

public static void Info(string msg, Exception ex = null)
{
Instance().EnqueueMessage(msg, FlashLogLevel.Info, ex);
}

public static void Warn(string msg, Exception ex = null)
{
Instance().EnqueueMessage(msg, FlashLogLevel.Warn, ex);
}

}

/// <summary>
/// 日志等级
/// </summary>
public enum FlashLogLevel
{
Debug,
Info,
Error,
Warn,
Fatal
}

/// <summary>
/// 日志内容
/// </summary>
public class FlashLogMessage
{
public string Message { get; set; }
public FlashLogLevel Level { get; set; }
public Exception Exception { get; set; }

}

}





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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐