欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

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

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

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

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