C#写日志工具类(新版)

C#写日志工具类(新版),第1张

概述昨天打算把我以前写的一个C#写日志工具类放到GitHub上,却发现了一个BUG,当然,已经修复了。 然后写Demo对比了NLog和log4net,发现我这个LogUtil比它们性能低了不止一个数量级(

昨天打算把我以前写的一个C#写日志工具类放到GitHub上,却发现了一个BUG,当然,已经修复了。

然后写Demo对比了NLog和log4net,发现我这个LogUtil比它们性能低了不止一个数量级(后来发现是通过共用Mutex修复BUG导致的)。工作多年,平时都是用别人写的库,自己写的很少。因为当初自己没有时间研究log4net或NLog,并且写个简单的日志工具类自己也有能力实现,所以就自己写了LogUtil自己用。修修改改了很多次了,居然还是有BUG。因为用了多线程和锁,导致BUG很隐蔽,而且性能比较差(后来发现是通过共用Mutex修复BUG导致的)。代码写的很挫,逻辑复杂,更容易出BUG。用NLog或log4net它不香吗?但又心有不甘,而且对于自己写的一些小的程序,可能第三方日志类库的dll比自己的程序都大,所以也有必要自己写一个,以便平时写各种Demo用。

之前写的很挫,逻辑很复杂的日志工具类:https://www.cnblogs.com/s0611163/p/4023859.html

日志类型LogType类:

using System; System.Collections.Generic; System.linq; System.Text; System.Threading.Tasks;namespace Utils{    /// <summary>    /// 日志类型    </summary>    public enum LogType    {        DeBUG,Info,Error    }}
VIEw Code

当前日志写入流LogStream类:

 System.IO; Utils{    internal class LogStream    {        public fileStream CurrentfileStream { get; set; }        public StreamWriter CurrentStreamWriter { int CurrentArchiveIndex { long CurrentfileSize { string CurrentDateStr { string CurrentLogfilePath { string CurrentLogfileDir { ; }    }}
VIEw Code

LogWriter类:

 System.Collections.Concurrent; System.Reflection; System.Text.RegularExpressions; System.Threading; LogWriter    {        #region 字段属性        private LogType _logType;        private string _basePath;        int _fileSize = 10 * 1024 * 1024; //日志分隔文件大小        private LogStream _currentStream = new LogStream();        string _dateFormat = "yyyyMMdd"; 日志文件名日期格式化        string _rootFolder = Log日志文件夹名称        object _lockWriter = new object();        #endregion        #region LogWriter        public LogWriter(LogType logType)        {            _logType = logType;            Init();        }        #region Init        <summary>         初始化        </summary>        voID Init()        {            初始化 _basePath            InitBasePath();            创建目录            CreateLogDir();            更新日志写入流            UpdateCurrentStream();        }        #region 初始化 _basePath         初始化 _basePath         InitBasePath()        {            UriBuilder uri =  UriBuilder(Assembly.GetExecutingAssembly().CodeBase);            _basePath = Path.GetDirectoryname(Uri.UnescapeDataString(uri.Path));        }        #region 初始化 _currentArchiveIndex         初始化 _currentArchiveIndex         InitCurrentArchiveIndex()        {            Regex regex = new Regex(_currentStream.CurrentDateStr + _*(\d*).txt");            string[] fileArr = Directory.Getfiles(_currentStream.CurrentLogfileDir,_currentStream.CurrentDateStr + *foreach (string file in fileArr)            {                Match match = regex.Match(file);                if (match.Success)                {                    string str = match.Groups[1].Value;                    if (!.IsNullOrWhiteSpace(str))                    {                        int temp = Convert.ToInt32(str);                        if (temp > _currentStream.CurrentArchiveIndex)                        {                            _currentStream.CurrentArchiveIndex = temp;                        }                    }                    else                    {                        _currentStream.CurrentArchiveIndex = 0;                    }                }            }        }        #region 初始化 _currentfileSize         初始化 _currentfileSize         InitCurrentfileSize()        {            fileInfo fileInfo =  fileInfo(_currentStream.CurrentLogfilePath);            _currentStream.CurrentfileSize = fileInfo.Length;        }        #region CreateLogDir()         创建日志目录         CreateLogDir()        {            string logDir = Path.Combine(_basePath,_rootFolder + \" + _logType.ToString());            Directory.Exists(logDir))            {                Directory.CreateDirectory(logDir);            }        }        #region CreateStream         创建日志写入流         CreateStream()        {            _currentStream.CurrentfileStream =  fileStream(_currentStream.CurrentLogfilePath,fileMode.Append,fileAccess.Write,fileShare.ReaDWrite);            _currentStream.CurrentStreamWriter =  StreamWriter(_currentStream.CurrentfileStream,EnCoding.UTF8);        }        #region CloseStream         关闭日志写入流         CloseStream()        {            if (_currentStream.CurrentStreamWriter != null)            {                _currentStream.CurrentStreamWriter.Close();            }            if (_currentStream.CurrentfileStream != )            {                _currentStream.CurrentfileStream.Close();            }        }        #region 拼接日志内容         拼接日志内容        static string CreateLogString(LogType logType, log)        {            return string.Format(@"{0} {1} {2}",DateTime.Now.ToString(yyyy-MM-dd HH:mm:ss.fff"),([" + logType.ToString() + ]").padright(7,' '),log);        }        #region 写文件         写文件        voID Writefile(try            {                lock (_lockWriter)                {                    判断是否更新Stream                    string dateStr = DateTime.Now.ToString(_dateFormat);                    if (_currentStream.CurrentDateStr != dateStr)                    {                        _currentStream.CurrentDateStr = dateStr;                        UpdateCurrentStream();                    }                    判断是否创建Archive                    int byteCount = EnCoding.UTF8.GetByteCount(log);                    _currentStream.CurrentfileSize += byteCount;                    if (_currentStream.CurrentfileSize >= _fileSize)                    {                        _currentStream.CurrentfileSize = ;                        CreateArchive();                    }                    日志内容写入文件                    _currentStream.CurrentStreamWriter.Writeline(log);                    _currentStream.CurrentStreamWriter.Flush();                }            }            catch (Exception ex)            {                Console.Writeline(ex.Message + \r\n ex.StackTrace);            }        }        #region CreateArchive         创建日志存档         CreateArchive()        {            string filename = Path.GetfilenameWithoutExtension(_currentStream.CurrentLogfilePath);            CloseStream(); 关闭日志写入流            file.Move(_currentStream.CurrentLogfilePath,Path.Combine(_currentStream.CurrentLogfileDir,filename + _" + (++_currentStream.CurrentArchiveIndex) + .txt")); 存档            CreateStream(); 创建日志写入流        }        #region UpdateCurrentStream         更新日志写入流         UpdateCurrentStream()        {            关闭日志写入流                CloseStream();                创建新的日志路径                _currentStream.CurrentDateStr = DateTime.Now.ToString(_dateFormat);                _currentStream.CurrentLogfileDir = Path.Combine(_basePath,1)"> _logType.ToString());                _currentStream.CurrentLogfilePath = Path.Combine(_currentStream.CurrentLogfileDir,1)">);                                CreateStream();                初始化 _currentArchiveIndex                InitCurrentArchiveIndex();                初始化 _currentfileSize                InitCurrentfileSize();            }            #region 写日志         写日志        </summary>        <param name="log">日志内容</param>        voID WriteLog(            {                log = CreateLogString(_logType,log);                Writefile(log);            }            #endregion    }}
VIEw Code

静态类LogUtil类:

 写日志类     LogUtil    {        #region 字段        static LogWriter _infoWriter =  LogWriter(LogType.Info);        static LogWriter _deBUGWriter =  LogWriter(LogType.DeBUG);        static LogWriter _errorWriter =  LogWriter(LogType.Error);        #region 写 *** 作日志         写 *** 作日志        voID Log( log)        {            _infoWriter.WriteLog(log);        }        #region 写调试日志         写调试日志        voID DeBUG( log)        {            _deBUGWriter.WriteLog(log);        }        #region 写错误日志        voID Error(Exception ex,1)">string log = )        {            Error(string.IsNullOrEmpty(log) ? ex.Message + " + ex.StackTrace : (log + ") + ex.Message +  ex.StackTrace);        }         写错误日志        voID Error( log)        {            _errorWriter.WriteLog(log);        }            }}
VIEw Code

测试代码(LogUtil、NLog、log4net写日志性能对比):

 NLog; System.ComponentModel; System.Data; System.Diagnostics; System.Drawing; System.Threading.Tasks; System.windows.Forms; Utils; LogUtilTest{    partial  Form1 : Form    {        private Logger _log = NLog.LogManager.GetCurrentClassLogger();        private log4net.ILog _log2 = ;        int n = 300000 Form1()        {            InitializeComponent();            ThreadPool.SetMinThreads(20,1)">20);            UriBuilder uri =  UriBuilder(Assembly.GetExecutingAssembly().CodeBase);            string path = Path.GetDirectoryname(Uri.UnescapeDataString(uri.Path));            fileInfo configfile = new fileInfo(Path.Combine(path,1)">log4net.config));            log4net.Config.XmlConfigurator.Configure(configfile);            _log2 = log4net.LogManager.GetLogger(typeof(Form1));        }        #region Log        this.Isdisposed)            {                if (.Invokerequired)                {                    this.BeginInvoke(new Action(() =>                    {                        textBox1.AppendText(DateTime.Now.ToString(HH:mm:ss.fff") + " " + log + \r\n\r\n);                    }));                }                                {                    textBox1.AppendText(DateTime.Now.ToString();                }            }        }        voID button1_Click( sender,EventArgs e)        {            LogUtil.Log(测试写 Info 日志);            LogUtil.DeBUG(测试写 DeBUG 日志);            LogUtil.Error(测试写 Error 日志);        }        voID button2_Click(            {                Log(==== 开始 ========);                Stopwatch stopwatch =  Stopwatch();                stopwatch.Start();                List<Task> taskList = new List<Task>();                Task tsk = ;                int taskCount = ;                tsk = Task.Run(() =>                {                    for (int i = 0; i < n; i++)                    {                        LogUtil.Log(测试日志 " + i.ToString(000000));                        Interlocked.Increment(ref taskCount);                    }                });                taskList.Add(tsk);                tsk = Task.Run(() =>)                    {                        LogUtil.DeBUG()                    {                        LogUtil.Error( taskCount);                    }                });                taskList.Add(tsk);                Task.WaitAll(taskList.ToArray());                Log(Task Count= taskCount);                Log(==== 结束 " + ,耗时:" + stopwatch.Elapsed.TotalSeconds.ToString(0.000 秒 ========);                stopwatch.Stop();            });        }        对比NLog        voID button3_Click()                    {                        _log.Info()                    {                        _log.DeBUG()                    {                        _log.Error(对比log4net        voID button4_Click()                    {                        _log2.Info()                    {                        _log2.DeBUG()                    {                        _log2.Error();                stopwatch.Stop();            });        }    }}
VIEw Code

log4net.config配置文件:

<?xml version="1.0" enCoding="utf-8"?><log4net>  <!-- 日志文件配置-->  root>    level value="ALL"/>    按文件存储日志-->    appender-ref ="DeBUGAppender"="InfoAppender"="ErrorAppender" />  </appender name="ErrorAppender" type="log4net.Appender.RollingfileAppender"param ="file" value=".\Logs\Error\" 日志记录的存在路="AppendTofile"="true" 为true就表示日志会附加到文件,为false,则会重新创建一个新文件="MaxSizeRollBackups"="100" 创建最大文件数="maximumfileSize"="10MB" 文件大小="StaticLogfilename"="false" 是否指定文件名="DatePattern"="yyyy-MM-dd&quot;.log&quot;"文件格式="RollingStyle"="Composite" 创建新文件的方式,可选为Size(按文件大小),Date(按日期),Once(每启动一次创建一个文件),Composite(按日期及文件大小),默认为Compositelayout type="log4net.Layout.PatternLayout">      输出内容布局-->      ="ConversionPattern"="%date [%thread] %-5level %logger [%property{NDC}] - %message%newline" />      method会影响性能layoutfilter ="log4net.Filter.LevelRangeFilter"="LevelMin"="ERROR" ="LevelMax"filterappender="InfoAppender"=".\Logs\Info\" ="yyyy-MM-dd&quot;.log&quot;" ="INFO" ="DeBUGAppender"=".\Logs\DeBUG\" ="DEBUG" >>
VIEw Code

NLog.config配置文件:

xml version="1.0" enCoding="utf-8" nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"      xsi:schemaLocation="http://www.nlog-project.org/schemas/NLog.xsd NLog.xsd"      autoReload="true"      throwExceptions="false"      internalLogLevel="Off"      internalLogfile="d:\nlog\nlog-internal.log">   optional,add some variables  https://github.com/nlog/NLog/wiki/Configuration-file#variables  <variable name="myvar" value="myvalue"/>-->  variable ="logDir"="${basedir}/nlog"="logfilename"="${date:format=yyyyMMdd}.txt"="logArchivefilename"="${date:format=yyyyMMdd}_{#}.txt"="logLayout"="${date:format=yyyy-MM-dd HH\:mm\:ss.fff} [${level}] ${message}"/>    See https://github.com/nlog/nlog/wiki/Configuration-file  for information on customizing logging rules and outputs.   targets>        add your targets here    See https://github.com/nlog/NLog/wiki/Targets for possible targets.    See https://github.com/nlog/NLog/wiki/Layout-Renderers for the possible layout renderers.    -->        Write events to a file with the date in the filename.    <target xsi:type="file" name="f" filename="${basedir}/logs/${shortdate}.log"            layout="${longdate} ${uppercase:${level}} ${message}" />    target xsi:type name="info"            layout="${logLayout}"            filename="${logDir}/info/${logfilename}"            archivefilename="${logDir}/info/${logArchivefilename}"            archiveAboveSize="10485760"            archiveNumbering="Sequence"            maxArchivefiles="100"            concurrentWrites            keepfileOpen            openfileCacheTimeout="30"            enCoding="UTF-8" />    ="deBUG"="${logDir}/deBUG/${logfilename}"="${logDir}/deBUG/${logArchivefilename}"="error"="${logDir}/error/${logfilename}"="${logDir}/error/${logArchivefilename}"rules add your logging rules here     Write all events with minimal level of DeBUG (So DeBUG,Warn,Error and Fatal,but not Trace)  to "f"    <logger name="*" minlevel="DeBUG" writeto="f" />    logger ="*" minlevel="Info" maxlevel writeto="info" ="DeBUG"="deBUG" ="Error"="error" nlog>
VIEw Code

测试截图:

写Info、DeBUG、Error日志各30万行,LogUtil耗时4.628秒,NLog耗时4.900秒,log4net耗时10.564秒,硬盘是固态硬盘。

 

说明:

该版本不支持多进程并发。

支持多进程并发的LogWriter版本(注意:代码中要加上 _currentStream.CurrentfileStream.Seek(0,SeekOrigin.End); 这句,不然不支持多进程并发):

 支持多进程并发写日志的LogWriter版本     LogWriterUseMutex    {         Mutex _mutex;         LogWriterUseMutex(LogType logType)        {            _logType = logType;            _mutex = new Mutex(false,1)">Mutex.LogWriter..7693FFAD38004F6B8FD31F6A8B4CE2BD);            Init();        }                    {                _mutex.WaitOne();                判断是否更新Stream                 DateTime.Now.ToString(_dateFormat);                 dateStr)                {                    _currentStream.CurrentDateStr = dateStr;                    UpdateCurrentStream();                }                判断是否创建Archive                 EnCoding.UTF8.GetByteCount(log);                _currentStream.CurrentfileSize += byteCount;                 _fileSize)                {                    _currentStream.CurrentfileSize = ;                    CreateArchive();                }                日志内容写入文件                _currentStream.CurrentfileStream.Seek(,SeekOrigin.End);                _currentStream.CurrentStreamWriter.Writeline(log);                _currentStream.CurrentStreamWriter.Flush();            }             ex.StackTrace);            }            finally            {                _mutex.ReleaseMutex();            }        }            }}
VIEw Code

多进程并发的版本,性能差一些。

有BUG,file.Move这行代码多进程并发会异常,因文件一直是打开状态的,所以这种实现方式可能无法解决这个BUG。

 

总结:

新版本比旧版本代码逻辑更简单,代码组织更合理。

一个方法的代码行数不宜太长,逻辑要简单,这样不容易出BUG;单线程相比多线程,不容易出BUG。

自己写的代价很大,花了整整一天时间,用来练手没问题,但是不经过一两个项目的实际使用以验证没有BUG的话,你敢用吗?

 

 

 

总结

以上是内存溢出为你收集整理的C#写日志工具类(新版)全部内容,希望文章能够帮你解决C#写日志工具类(新版)所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/1212652.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-06-04
下一篇 2022-06-04

发表评论

登录后才能评论

评论列表(0条)