[原創]分享一個輕量級日志類


日常開發中,常常會在程序部署到生產環境后發現有些問題,但無法直接調試,這就需要用到日志,本來想找一些開源的完善的日志類來實現,但試了幾個都感覺太重。於是意識到一個問題,懶是偷不得的,只好擼起袖子,自己寫一個。這個日志類是基於訂閱模式的,而且是線程安全的,現在分享給大家,希望能給大家帶來幫助。

閑話不多說,直接上代碼。代碼有兩個實現版本(Java與C#),這里放出的是C#。

一共用到三個類:JzgLogs.cs主類,LogBuffer.cs日志緩沖類,LogInfo是用於日志緩沖中做記錄的實體類,基本原理是把所有待寫入的日志寫到緩沖中,然后一個線程輪詢緩沖,發現有需要寫入的數據,就寫入,否則就下一次。

No.1  JzgLogs.cs

  1 using System;
  2 using System.Collections.Generic;
  3 using System.Linq;
  4 using System.Text;
  5 using System.IO;
  6 using System.Text.RegularExpressions;
  7 using System.Threading;
  8 
  9 /****************************************************************************
 10 單例類     :日志記錄類
 11 作者       :賈淵
 12 版本       :1.5
 13 上一版本   :1.4
 14 更新日志   :
 15     2018-07-06  創建
 16     2018-08-06  增加了屬性LogMode,用於改變記錄日志的方式
 17     2018-12-26  修改了讀寫文件的線程鎖定方式
 18     2019-01-14  改進日志寫入的時的對象鎖定方式
 19     2019-01-15  改進了日志寫入的方式為多線程隊列寫入,操你大爺的Lock,又慢又爛
 20 ****************************************************************************/
 21 
 22 namespace Jzg.Logs
 23 {
 24     /// <summary>
 25     /// 日志記錄類
 26     /// </summary>
 27     public class JzgLogs : IDisposable
 28     {
 29         /// <summary>
 30         /// 記錄類型:消息
 31         /// </summary>
 32         public const string LOGTYPE_INFO = "INFO";
 33         /// <summary>
 34         /// 記錄類型:錯誤
 35         /// </summary>
 36         public const string LOGTYPE_ERROR = "ERROR";
 37 
 38         //線程鎖
 39         private static readonly object locker = new object();
 40 
 41         //日志記錄路徑
 42         private string logPath = "";
 43 
 44         //日志記錄方式
 45         private LogMode logMode = LogMode.lmAll;
 46 
 47         /// <summary>
 48         /// 日志記錄方式,從枚舉LogMode中取值
 49         /// </summary>
 50         public LogMode LogMode
 51         {
 52             get
 53             {
 54                 return logMode;
 55             }
 56 
 57             set
 58             {
 59                 logMode = value;
 60             }
 61         }
 62 
 63         /// <summary>
 64         /// 私有構造方法
 65         /// </summary>
 66         private JzgLogs()
 67         {
 68             //默認為全記錄
 69             logMode = LogMode.lmAll;
 70             //創建線程安全的消息隊列
 71             LogQueue = new LogBuffer();
 72             //開啟寫入線程
 73             WriteThread = new Thread(FlushBuffer);
 74             WriteThread.IsBackground = true;
 75             WriteThread.Start();
 76         }
 77 
 78         private LogBuffer LogQueue;
 79 
 80         //是否停止處理緩存
 81         private volatile bool _FlushAlive = true;
 82 
 83         private void FlushBuffer()
 84         {
 85             while (_FlushAlive)
 86             {
 87                 LogInfo logInfo = LogQueue.ReadBuffer();
 88                 if (logInfo == null)
 89                 {
 90                     //如果沒有要寫入的內容則延時100毫秒再看
 91                     Thread.Sleep(200);
 92                 }
 93                 else
 94                 {
 95                     try
 96                     {
 97                         using (FileStream fs = new FileStream(logInfo.LogFile, FileMode.OpenOrCreate, FileAccess.Write, FileShare.ReadWrite))
 98                         {
 99                             using (StreamWriter sw = new StreamWriter(fs))
100                             {
101                                 sw.BaseStream.Seek(0, SeekOrigin.End);
102                                 sw.WriteLine(logInfo.LogContent);
103                                 sw.Flush();
104                             }
105                         }
106                     }
107                     catch (Exception)
108                     {
109                         //出錯就不管丫的
110                     }
111                 }
112             }
113         }
114 
115         private static JzgLogs instance = null;
116 
117         /// <summary>
118         /// 獲取或設置日志記錄路徑
119         /// </summary>
120         public string LogPath
121         {
122             get
123             {
124                 //補齊路徑結束的\符號
125                 if (!logPath.EndsWith("\\"))
126                 {
127                     logPath += "\\";
128                 }
129                 return logPath;
130             }
131 
132             set
133             {
134                 logPath = value;
135             }
136         }
137 
138 
139         /// <summary>
140         /// 靜態方法:獲取實例(單例模式)
141         /// </summary>
142         /// <returns></returns>
143         [Obsolete("該方法已過時,推薦使用靜態屬性Instance代替")]
144         public static JzgLogs getInstance()
145         {
146             //2019-01-14 改為二次驗證單例模式,提高了性能和並發安全性
147             if (instance == null)
148             {
149                 lock (locker)
150                 {
151                     if (instance == null)
152                     {
153                         var tmp = new JzgLogs();
154                         Thread.MemoryBarrier();
155                         instance = tmp;
156                     }
157                 }
158             }
159 
160             return instance;
161         }
162 
163         /// <summary>
164         /// 靜態屬性:單例實例
165         /// </summary>
166         public static JzgLogs Instance
167         {
168             get
169             {
170                 //2019-01-14 改為二次驗證單例模式,提高了性能和並發安全性
171                 if (instance == null)
172                 {
173                     lock (locker)
174                     {
175                         if (instance == null)
176                         {
177                             var tmp = new JzgLogs();
178                             Thread.MemoryBarrier();
179                             instance = tmp;
180                         }
181                     }
182                 }
183 
184                 return instance;
185             }
186         }
187 
188         //寫入線程
189         private Thread WriteThread = null;
190 
191         /// <summary>
192         /// 記錄日志
193         /// </summary>
194         /// <param name="subPath">子路徑</param>
195         /// <param name="logType">記錄類型</param>
196         /// <param name="tag">模塊標識</param>
197         /// <param name="logContent">記錄內容</param>
198         public void Log(string subPath, string logType, string tag, string logContent)
199         {
200             //如果未設置路徑則拋出錯誤
201             if (string.IsNullOrEmpty(logPath))
202             {
203                 throw new Exception("logPath not set");
204             }
205 
206             //判斷記錄模式
207             bool canLog = (logMode == LogMode.lmAll) || (logMode == LogMode.lmError && logType == LOGTYPE_ERROR) || (logMode == LogMode.lmInfo && logType == LOGTYPE_INFO);
208             //如果不需要記錄則直接退出
209             if (!canLog)
210             {
211                 return;
212             }
213 
214             //當前時間
215             DateTime logTime = DateTime.Now;
216             //記錄時間的字符串
217             string logTimeStr = logTime.ToString("yyyy/MM/dd HH:mm:ss:fff");
218             //文件名
219             string fileName = String.Format("log_{0}.log", DateTime.Now.ToString("yyyyMMdd"));
220 
221             //計算子路徑
222             string fullLogPath = LogPath + subPath;
223             //補齊路徑結尾\符號
224             if (!fullLogPath.EndsWith("\\") && !String.IsNullOrEmpty(fullLogPath))
225             {
226                 fullLogPath += "\\";
227             }
228             //自動創建路徑
229             DirectoryInfo di = new DirectoryInfo(fullLogPath);
230             if (!di.Exists)
231             {
232                 di.Create();
233             }
234 
235             //文件完整路徑
236             string fullFilePath = fullLogPath + fileName;
237             //記錄格式模板
238             string contentTemplate = "【{0}】 【{1}】 【{2}】 【記錄】{3}";
239             logContent = Regex.Replace(logContent, @"[\n\r]", "");//去掉換行符
240                                                                   //計算日志內容
241             string lineContent = String.Format(contentTemplate, logType, logTimeStr, tag, logContent);
242 
243             LogInfo logInfo = new LogInfo()
244             {
245                 LogFile = fullFilePath,
246                 LogContent = lineContent
247             };
248 
249             //寫入緩存
250             LogQueue.WriteBuffer(logInfo);
251         }
252 
253         /// <summary>
254         /// 記錄日志
255         /// </summary>
256         /// <param name="logType">記錄類型</param>
257         /// <param name="tag">模塊標識</param>
258         /// <param name="logContent">記錄內容</param>
259         public void Log(string logType, string tag, string logContent)
260         {
261             Log("", logType, tag, logContent);
262         }
263 
264         /// <summary>
265         /// 釋放資源
266         /// </summary>
267         public void Dispose()
268         {
269             _FlushAlive = false;
270         }
271     }
272 
273     /// <summary>
274     /// 枚舉:日志記錄方式
275     /// </summary>
276     public enum LogMode
277     {
278         /// <summary>
279         /// 不記錄
280         /// </summary>
281         lmNone = 0,
282         /// <summary>
283         /// 全記錄
284         /// </summary>
285         lmAll = 1,
286         /// <summary>
287         /// 只記錄Info類型
288         /// </summary>
289         lmInfo = 2,
290         /// <summary>
291         /// 只記錄Error類型
292         /// </summary>
293         lmError = 3
294     }
295 }

No.2  LogBuffer.cs

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 using System.Collections.Concurrent;
 6 
 7 namespace Jzg.Logs
 8 {
 9     /// <summary>
10     /// 日志寫入緩沖
11     /// </summary>
12     public class LogBuffer
13     {
14         private ConcurrentQueue<LogInfo> Logs;
15 
16         /// <summary>
17         /// 構造方法
18         /// </summary>
19         public LogBuffer()
20         {
21             Logs = new ConcurrentQueue<LogInfo>();
22         }
23 
24         /// <summary>
25         /// 把日志加入寫入隊列末端
26         /// </summary>
27         /// <param name="logInfo">要寫入的日志對象</param>
28         public void WriteBuffer(LogInfo logInfo)
29         {
30             Logs.Enqueue(logInfo);
31         }
32 
33         /// <summary>
34         /// 從日志中取出開頭的一條
35         /// </summary>
36         /// <returns>為null表示隊列為空了</returns>
37         public LogInfo ReadBuffer()
38         {
39             LogInfo logInfo = null;
40 
41             if (!Logs.TryDequeue(out logInfo))
42                 return null;
43 
44             return logInfo;
45         }
46         
47         /// <summary>
48         /// 隊列中的數量
49         /// </summary>
50         /// <returns></returns>
51         public int Count()
52         {
53             return Logs.Count;
54         }
55     }
56 }

No.3  LogInfo.cs

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 
 6 namespace Jzg.Logs
 7 {
 8     /// <summary>
 9     /// 用於緩存的日志記錄實體
10     /// </summary>
11     public class LogInfo
12     {
13         /// <summary>
14         /// 要記錄到哪個文件里
15         /// </summary>
16         public string LogFile
17         {
18             get; set;
19         }
20 
21         /// <summary>
22         /// 記錄的文字內容
23         /// </summary>
24         public string LogContent
25         {
26             get; set;
27         }
28     }
29 }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM