[原创]分享一个轻量级日志类

Posted vbcrazy

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[原创]分享一个轻量级日志类相关的知识,希望对你有一定的参考价值。

日常开发中,常常会在程序部署到生产环境后发现有些问题,但无法直接调试,这就需要用到日志,本来想找一些开源的完善的日志类来实现,但试了几个都感觉太重。于是意识到一个问题,懒是偷不得的,只好撸起袖子,自己写一个。这个日志类是基于订阅模式的,而且是线程安全的,现在分享给大家,希望能给大家带来帮助。

闲话不多说,直接上代码。代码有两个实现版本(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 }

 

以上是关于[原创]分享一个轻量级日志类的主要内容,如果未能解决你的问题,请参考以下文章

分享一个简单且轻量级的日志库:log.c

分享一个简单且轻量级的日志库:log.c

原创设计分享面向服务的分布式日志架构

Swift 日志输出类 SwiftyBeaver

[原创]java WEB学习笔记61:Struts2学习之路--通用标签 property,uri,param,set,push,if-else,itertor,sort,date,a标签等(代码片段

线程-使用SimaphoreSlim类