欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

c# 編寫一個(gè)輕量級(jí)的異步寫日志的實(shí)用工具類(LogAsyncWriter)

 更新時(shí)間:2021年03月10日 08:42:00   作者:夢在旅途  
這篇文章主要介紹了c# 如何編寫一個(gè)輕量級(jí)的異步寫日志的實(shí)用工具類(LogAsyncWriter),幫助大家更好的理解和學(xué)習(xí)使用c#,感興趣的朋友可以了解下

一說到寫日志,大家可能推薦一堆的開源日志框架,如:Log4Net、NLog,這些日志框架確實(shí)也不錯(cuò),比較強(qiáng)大也比較靈活,但也正因?yàn)橛謴?qiáng)大又靈活,導(dǎo)致我們使用他們時(shí)需要引用一些DLL,同時(shí)還要學(xué)習(xí)各種用法及配置文件,這對于有些小工具、小程序、小網(wǎng)站來說,有點(diǎn)“殺雞焉俺用牛刀”的感覺,而且如果對這些日志框架不了解,可能輸出來的日志性能或效果未畢是與自己所想的,鑒于這幾個(gè)原因,我自己重復(fù)造輪子,編寫了一個(gè)輕量級(jí)的異步寫日志的實(shí)用工具類(LogAsyncWriter),這個(gè)類還是比較簡單的,實(shí)現(xiàn)思路也很簡單,就是把消息日志先入內(nèi)存隊(duì)列,然后由異步監(jiān)聽線程從隊(duì)列中取出日志并批量輸出到本地文件中,同時(shí)參照各大日志框架,對單個(gè)文件過大采取分割生成多個(gè)日志文件。

經(jīng)測試發(fā)現(xiàn)性能非常不錯(cuò),先看示例使用代碼:(采取并發(fā)多線程同時(shí)寫入1000000萬條日志)

    Task.Factory.StartNew(() =>
    {
        DateTime startTime = DateTime.Now;
        int logCount = 1000000;
        Parallel.For(1, logCount, (i) =>
        {
            if (i % 2 == 0)
            {
                LogAsyncWriter.Default.Error("測試并發(fā)寫錯(cuò)誤日志-" + i.ToString(), "TestClass.TestLog", i.ToString());
            }
            else
            {
                LogAsyncWriter.Default.Info("測試并發(fā)寫普通日志-" + i.ToString(), "TestClass.TestLog", i.ToString());
            }
        });
 
        this.Invoke(new MethodInvoker(() =>
        {
            MessageBox.Show(DateTime.Now.ToString() + "," + logCount + "條日志寫完了!,耗時(shí):" + (DateTime.Now - startTime).TotalMilliseconds + "ms");
        }));
    });
 
    MessageBox.Show(DateTime.Now.ToString() + ",同步方法已結(jié)束");
}

 執(zhí)行效果如下圖示:

因?yàn)椴捎卯惒?,故方法先走到結(jié)尾,輸出了同步的MsgBox,隨后彈出的是100W日志輸出到文件后的耗時(shí)MsgBox,從截圖可以看出,不足1S(當(dāng)然這里的1S不是真實(shí)的輸出到本地方件,而是把所有的日志推到了Queue中而矣,但不影響不阻塞業(yè)務(wù)處理),而本地日志文件的大小達(dá)到了263MB(設(shè)置最大值的MaxSizeBackup,使其不滾動(dòng)備份),由此看性能是不錯(cuò)的;

因?yàn)槭钱惒窖舆t輸出到本地日志文件,故大家在代碼中任意地方,比如:循環(huán)中都可以使用它,不用擔(dān)心會(huì)影響你的正常的業(yè)務(wù)邏輯的執(zhí)行效率;

如果采取滾動(dòng)備份(設(shè)置MaxSizeBackup為一個(gè)合理值,默認(rèn)為10MB),則生成的日志文件形式如下:(超過最大容量則生成同名文件+2位序號(hào)),超過一個(gè)月則會(huì)自動(dòng)清除

打開日志文件會(huì)看到所有的日志內(nèi)容:

有些人可能要問,輸出格式是固定的嗎?能否自定義布局,告訴你是可以的,我考慮到每個(gè)人對日志輸出的格式都有嚴(yán)格的要求,故可以通過設(shè)置:LineLayoutRenderFormat屬性來設(shè)置每條日志輸出的格式內(nèi)容

 好了,介紹了使用功能及效果、性能,下面貼出源代碼:

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;
using System.Text.RegularExpressions;
 
namespace Zuowj.Common
{
    /// <summary>
    /// 日志異步生成器
    /// Author:Zuowenjun(http://www.zuowenjun.cn)
    /// Date:2018-6-14
    /// </summary>
    public class LogAsyncWriter
    {
        public const string InfoLevel = "INFO";
        public const string WarnLevel = "WARN";
        public const string ErrorLevel = "ERROR";
 
        private readonly ConcurrentQueue<string[]> logMsgQueue = new ConcurrentQueue<string[]>();
        private readonly CancellationTokenSource cts = null;
        private string lineLayoutRenderFormat = "[{0:yyyy-MM-dd HH:mm:ss}]\t{1}\t{2}\t{3}:{4},Trace:{5};Other1:{6},Other2:{7},Other3:{8}";
        private long maxSizeBackup = 10485760L;//默認(rèn)10MB
        private string todayLogName = null;
 
        private static readonly LogAsyncWriter instance = new LogAsyncWriter();
 
 
        private LogAsyncWriter()
        {
            cts = new CancellationTokenSource();
            ListenSaveLogAsync(cts.Token);
        }
 
        private void ListenSaveLogAsync(CancellationToken cancellationToken)
        {
            Task.Factory.StartNew(() =>
            {
                DateTime lastSaveLogTime = DateTime.Now;
                while (!cancellationToken.IsCancellationRequested)//如果沒有取消線程,則一直監(jiān)聽執(zhí)行寫LOG
                {
                    if (logMsgQueue.Count >= 10 || (logMsgQueue.Count > 0 && (DateTime.Now - lastSaveLogTime).TotalSeconds > 30))//如是待寫日志消息累計(jì)>=10條或上一次距離現(xiàn)在寫日志時(shí)間超過30s則需要批量提交日志
                    {
                        List<string[]> logMsgList = new List<string[]>();
                        string[] logMsgItems = null;
 
                        while (logMsgList.Count < 10 && logMsgQueue.TryDequeue(out logMsgItems))
                        {
                            logMsgList.Add(logMsgItems);
                        }
 
                        WriteLog(logMsgList);
 
                        lastSaveLogTime = DateTime.Now;
                    }
                    else
                    {
                        SpinWait.SpinUntil(() => logMsgQueue.Count >= 10, 5000);//自旋等待直到日志隊(duì)列有>=10的記錄或超時(shí)5S后再進(jìn)入下一輪的判斷
                    }
                }
            }, cancellationToken);
        }
 
        private string GetLogFilePath()
        {
            string logFileDir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Logs");
            if (!Directory.Exists(logFileDir))
            {
                Directory.CreateDirectory(logFileDir);
            }
 
            string logDateStr = DateTime.Now.ToString("yyyyMMdd");
            string logName = logDateStr;
            if (!string.IsNullOrEmpty(todayLogName) && todayLogName.StartsWith(logName))
            {
                logName = todayLogName;
            }
            else
            {
                todayLogName = logName;
            }
 
            string logFilePath = Path.Combine(logFileDir, logName + ".log");
 
            if (File.Exists(logFilePath))
            {
                File.SetAttributes(logFilePath, FileAttributes.Normal);
                if (File.GetLastWriteTime(logFilePath).Month != DateTime.Today.Month) //30天滾動(dòng)(刪除舊的文件),防止日志文件過多
                {
                    File.Delete(logFilePath);
                    string[] oldLogFiles = Directory.GetFiles(logFileDir, string.Format("{0}-##.log", logDateStr), SearchOption.TopDirectoryOnly);
                    foreach (string fileName in oldLogFiles)
                    {
                        File.SetAttributes(fileName, FileAttributes.Normal);
                        File.Delete(fileName);
                    }
                }
                else if (new FileInfo(logFilePath).Length > MaxSizeBackup)
                {
                    Regex rgx = new Regex(@"^\d{8}-(?<fnum>\d{2})$");
                    int fnum = 2;
                    if (rgx.IsMatch(logName))
                    {
                        fnum = int.Parse(rgx.Match(logName).Groups["fnum"].Value) + 1;
                    }
 
                    logName = string.Format("{0}-{1:D2}", logDateStr, fnum);
                    todayLogName = logName;
                    logFilePath = Path.Combine(logFileDir, logName + ".log");
                }
            }
 
            return logFilePath;
        }
 
        private void WriteLog(IEnumerable<string[]> logMsgs)
        {
            try
            {
                List<string> logMsgLines = new List<string>();
                foreach (var logMsgItems in logMsgs)
                {
                    var logMsgLineFields = (new object[] { DateTime.Now }).Concat(logMsgItems).ToArray();
                    string logMsgLineText = string.Format(LineLayoutRenderFormat, logMsgLineFields);
                    logMsgLines.Add(logMsgLineText);
                }
 
                string logFilePath = GetLogFilePath();
                File.AppendAllLines(logFilePath, logMsgLines);
            }
            catch
            { }
        }
 
 
 
        public static LogAsyncWriter Default
        {
            get
            {
                return instance;
            }
        }
 
        public string LineLayoutRenderFormat
        {
            get { return lineLayoutRenderFormat; }
            set
            {
                if (string.IsNullOrWhiteSpace(value))
                {
                    throw new ArgumentException("無效的LineLayoutRenderFormat屬性值");
                }
 
                lineLayoutRenderFormat = value;
            }
        }
 
        public long MaxSizeBackup
        {
            get { return maxSizeBackup; }
            set
            {
                if (value <= 0)
                {
                    throw new ArgumentException("無效的MaxSizeBackup屬性值");
                }
 
                maxSizeBackup = value;
            }
        }
 
        public void SaveLog(string logLevel, string msg, string source, string detailTrace = null, string other1 = null, string other2 = null, string other3 = null)
        {
            logMsgQueue.Enqueue(new[] { logLevel, Thread.CurrentThread.ManagedThreadId.ToString(), source, msg, detailTrace ?? string.Empty, other1 ?? string.Empty, other2 ?? string.Empty, other3 ?? string.Empty });
        }
 
        public void Info(string msg, string source, string detailTrace = null, string other1 = null, string other2 = null, string other3 = null)
        {
            SaveLog(InfoLevel, msg, source, detailTrace, other1, other2, other3);
        }
 
        public void Warn(string msg, string source, string detailTrace = null, string other1 = null, string other2 = null, string other3 = null)
        {
            SaveLog(WarnLevel, msg, source, detailTrace, other1, other2, other3);
        }
 
        public void Error(string msg, string source, string detailTrace = null, string other1 = null, string other2 = null, string other3 = null)
        {
            SaveLog(ErrorLevel, msg, source, detailTrace, other1, other2, other3);
        }
 
        public void Error(Exception ex, string source, string other1 = null, string other2 = null, string other3 = null)
        {
            SaveLog(ErrorLevel, ex.Message, source, ex.StackTrace, other1, other2, other3);
        }
 
        ~LogAsyncWriter()
        {
            cts.Cancel();
        }
 
    }
}

代碼重點(diǎn)說明:

1.各種日志方法入?yún)⒔忉專?/p>

i. Msg:日志消息(一般是指簡要消息)
ii. Source:日志產(chǎn)生源(一般是指該日志是由哪個(gè)位置產(chǎn)生的,可以定義為:類.方法名)
iii. detailTrace:日志詳情(一般是指堆棧信息或日志更具體的信息)
iv. other1, other2, other3:備用日志字段,可根據(jù)實(shí)際情況記錄相關(guān)信息,比如:入?yún)?、返參,?zhí)行耗時(shí)等;
v. log Level:日志消息級(jí)別一般有很多級(jí)別,但常用的只有3類,即:Info=普通日志消息,Warn=警告日志消息,Error=錯(cuò)誤日志消息;

2.核心異步批量寫日志的方法:(采用后臺(tái)線程監(jiān)聽日志消息隊(duì)列,當(dāng)達(dá)到10條日志消息或?qū)懭罩镜臅r(shí)間間隔超過1分鐘,則會(huì)批量提交1次日志,解決了普通的同步寫日志方法造成寫壓力過大,且存在阻塞業(yè)務(wù)邏輯的情況)

3.采用單例模式,當(dāng)?shù)谝淮问褂迷擃悤r(shí),則會(huì)啟動(dòng)異步監(jiān)聽線程,當(dāng)該類釋放時(shí)(一般指應(yīng)用進(jìn)程關(guān)閉時(shí),會(huì)發(fā)送通知線程取消監(jiān)聽,避免一切可能的線程駐留問題)

好了就介紹到這里,大家若想試用或想改造,可以直接復(fù)制上述代碼,不足之處可以指出,謝謝!

以上就是c# 編寫一個(gè)輕量級(jí)的異步寫日志的實(shí)用工具類(LogAsyncWriter)的詳細(xì)內(nèi)容,更多關(guān)于c# 編寫異步寫日志工具類的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Unity3D基于OnGUI實(shí)時(shí)顯示FPS

    Unity3D基于OnGUI實(shí)時(shí)顯示FPS

    這篇文章主要介紹了Unity3D基于OnGUI實(shí)時(shí)顯示FPS,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2019-11-11
  • 在C#中使用MSMQ的方法

    在C#中使用MSMQ的方法

    這篇文章主要介紹了在C#中使用MSMQ的方法,幫助大家更好的理解和使用c#,感興趣的朋友可以了解下
    2021-01-01
  • c#讀寫excel文件使用示例

    c#讀寫excel文件使用示例

    這篇文章主要介紹了c#讀寫excel文件使用示例,需要的朋友可以參考下
    2014-02-02
  • C#多線程等待所有子線程結(jié)束的示例

    C#多線程等待所有子線程結(jié)束的示例

    這篇文章主要介紹了C#多線程等待所有子線程結(jié)束的示例,幫助大家更好的理解和學(xué)習(xí)c#編程語言,感興趣的朋友可以了解下
    2020-12-12
  • C#導(dǎo)入導(dǎo)出EXCEL文件的代碼實(shí)例

    C#導(dǎo)入導(dǎo)出EXCEL文件的代碼實(shí)例

    這篇文章主要介紹了C#導(dǎo)入導(dǎo)出EXCEL文件代碼實(shí)例,代碼的流程和方法都很詳細(xì),需要的朋友可以參考下
    2014-04-04
  • C#使用iTextSharp操作PDF

    C#使用iTextSharp操作PDF

    這篇文章介紹了C#使用iTextSharp操作PDF的方法,文中通過示例代碼介紹的非常詳細(xì)。對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-06-06
  • 深入理解C#序列化與反序列化的詳解

    深入理解C#序列化與反序列化的詳解

    本篇文章是對C#中序列化與反序列化進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下
    2013-05-05
  • 深入解析C#中的泛型類與泛型接口

    深入解析C#中的泛型類與泛型接口

    這篇文章主要介紹了C#中的泛型類與泛型接口,對泛型的支持是C#語言的重要特性,需要的朋友可以參考下
    2016-02-02
  • C#實(shí)現(xiàn)冒泡排序算法的代碼示例

    C#實(shí)現(xiàn)冒泡排序算法的代碼示例

    冒泡排序即是對數(shù)組每次輪循出最大數(shù)或最小數(shù)放在隊(duì)尾,這里我們來看一下C#實(shí)現(xiàn)冒泡排序算法的代碼示例,需要的朋友可以參考下
    2016-07-07
  • c#利用Session對象實(shí)現(xiàn)購物車的方法示例

    c#利用Session對象實(shí)現(xiàn)購物車的方法示例

    這篇文章主要介紹了c#利用Session對象實(shí)現(xiàn)購物車的方法示例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-02-02

最新評論