C#實(shí)現(xiàn)線程安全的簡(jiǎn)易日志記錄方法
一般在實(shí)際項(xiàng)目的開發(fā)中,會(huì)要求涉及日志記錄的問題,比較常用的有Log4Net,NLog等幾個(gè),而小項(xiàng)目小工具的話,則無需費(fèi)此大駕。而譬如串口開發(fā)的話,需要記錄串口過來的數(shù)據(jù)等等,這時(shí)候就要考慮日志記錄上線程的問題。對(duì)此,為了方便后續(xù)使用,封裝了下代碼:
using System;
using System.Diagnostics;
using System.IO;
using System.Text;
using System.Threading;
namespace CSharpUtilHelpV2
{
/// <summary>
/// 日志類型枚舉
/// </summary>
public enum LogType
{
/// <summary>
/// 一般輸出
/// </summary>
Trace,
/// <summary>
/// 警告
/// </summary>
Warning,
/// <summary>
/// 錯(cuò)誤
/// </summary>
Error,
/// <summary>
/// SQL
/// </summary>
SQL
}
/// <summary>
/// 基于.NET 2.0日志工具類
/// </summary>
public class LogToolV2
{
private static readonly Thread LogTask;
private static readonly ThreadSafeQueueV2<string> LogColQueue;//自定義線程安全的Queue
private static readonly object SyncRoot;
private static readonly string FilePath;
private static readonly long BackFileSize_MB = 2;//超過2M就開始備份日志文件
static LogToolV2()
{
SyncRoot = new object();
FilePath = AppDomain.CurrentDomain.SetupInformation.ApplicationBase + "Log\\";
LogTask = new Thread(WriteLog);
LogColQueue = new ThreadSafeQueueV2<string>();
LogTask.Start();
Debug.WriteLine("Log Start......");
}
/// <summary>
/// 記錄日志
/// </summary>
/// <param name="msg">日志內(nèi)容</param>
public static void Log(string msg)
{
string _msg = string.Format("{0} : {2}", DateTime.Now.ToString("HH:mm:ss"), msg);
LogColQueue.Enqueue(msg);
}
/// <summary>
/// 記錄日志
/// </summary>
/// <param name="msg">日志內(nèi)容</param>
/// <param name="type">日志類型</param>
public static void Log(string msg, LogType type)
{
string _msg = string.Format("{0} {1}: {2}", DateTime.Now.ToString("HH:mm:ss"), type, msg);
LogColQueue.Enqueue(_msg);
}
/// <summary>
/// 記錄日志
/// </summary>
/// <param name="ex">異常</param>
public static void Log(Exception ex)
{
if (ex != null)
{
string _newLine = Environment.NewLine;
StringBuilder _builder = new StringBuilder();
_builder.AppendFormat("{0}: {1}{2}", DateTime.Now.ToString("HH:mm:ss"), ex.Message, _newLine);
_builder.AppendFormat("{0}{1}", ex.GetType(), _newLine);
_builder.AppendFormat("{0}{1}", ex.Source, _newLine);
_builder.AppendFormat("{0}{1}", ex.TargetSite, _newLine);
_builder.AppendFormat("{0}{1}", ex.StackTrace, _newLine);
LogColQueue.Enqueue(_builder.ToString());
}
}
private static void WriteLog()
{
while (true)
{
if (LogColQueue.Count() > 0)
{
string _msg = LogColQueue.Dequeue();
Monitor.Enter(SyncRoot);
if (!CreateDirectory()) continue;
string _path = string.Format("{0}{1}.log", FilePath, DateTime.Now.ToString("yyyyMMdd"));
Monitor.Exit(SyncRoot);
lock (SyncRoot)
{
if (CreateFile(_path))
ProcessWriteLog(_path, _msg);//寫入日志到文本
}
ProcessBackLog(_path);//日志備份
}
}
}
private static void ProcessBackLog(string path)
{
lock (SyncRoot)
{
if (FileToolV2.GetMBSize(path) > BackFileSize_MB)
{
FileToolV2.CopyToBak(path);
}
}
}
private static void ProcessWriteLog(string path, string msg)
{
try
{
StreamWriter _sw = File.AppendText(path);
_sw.WriteLine(msg);
_sw.Flush();
_sw.Close();
}
catch (Exception ex)
{
Debug.WriteLine(string.Format("寫入日志失敗,原因:{0}", ex.Message));
}
}
private static bool CreateFile(string path)
{
bool _result = true;
try
{
if (!File.Exists(path))
{
FileStream _files = File.Create(path);
_files.Close();
}
}
catch (Exception)
{
_result = false;
}
return _result;
}
private static bool CreateDirectory()
{
bool _result = true;
try
{
if (!Directory.Exists(FilePath))
{
Directory.CreateDirectory(FilePath);
}
}
catch (Exception)
{
_result = false;
}
return _result;
}
}
}
測(cè)試代碼如下:
using CSharpUtilHelpV2;
using System;
using System.Diagnostics;
using System.Threading;
namespace LogUtilHelpV2Test
{
class Program
{
static void Main(string[] args)
{
try
{
Debug.WriteLine("-------------");
Action _writeLog = delegate()
{
for (int i = 0; i < 10000; i++)
LogToolV2.Log(Guid.NewGuid().ToString(), LogType.Trace);
};
Thread _wireteLogTask1 = new Thread(new ThreadStart(_writeLog));
_wireteLogTask1.Start();
Thread _wireteLogTask2 = new Thread(new ThreadStart(_writeLog));
_wireteLogTask2.Start();
//throw new Exception("test aaa bb cc");
}
catch (Exception ex)
{
LogToolV2.Log(ex);
Console.WriteLine(ex.Message.Trim());
}
finally
{
Console.WriteLine("ok");
Console.ReadLine();
}
}
}
}
代碼運(yùn)行效果如下所示:

感興趣的讀者可以自己測(cè)試運(yùn)行一下,希望能對(duì)大家起到一點(diǎn)幫助!
相關(guān)文章
C#如何實(shí)現(xiàn)子進(jìn)程跟隨主進(jìn)程關(guān)閉
多進(jìn)程開發(fā)經(jīng)常會(huì)遇到主進(jìn)程關(guān)閉,子進(jìn)程需要跟隨主進(jìn)程一同關(guān)閉,比如調(diào)ffmpeg命令行實(shí)現(xiàn)的錄屏程序等,下面我們就來看看C#是如何實(shí)現(xiàn)子進(jìn)程跟隨主進(jìn)程關(guān)閉的吧2024-04-04
C#獲取文件名和文件路徑的兩種實(shí)現(xiàn)方式
這篇文章主要介紹了C#獲取文件名和文件路徑的兩種實(shí)現(xiàn)方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-07-07
在C#中創(chuàng)建和讀取XML文件的實(shí)現(xiàn)方法
項(xiàng)目中需要將前臺(tái)頁(yè)面中的信息保存下來并存儲(chǔ)為xml文件格式到數(shù)據(jù)庫(kù)中去。因此我先在這里通過一個(gè)小實(shí)例來學(xué)習(xí)xml的創(chuàng)建與讀取2013-09-09
C#簡(jiǎn)單的通用基礎(chǔ)字典實(shí)現(xiàn)方法
這篇文章主要介紹了C#簡(jiǎn)單的通用基礎(chǔ)字典實(shí)現(xiàn)方法,包含了字典的索引、記錄、回調(diào)與查詢等技巧,需要的朋友可以參考下2014-12-12

