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

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

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

原文地址:https://www.cnblogs.com/vbcrazy/p/10934163.html

时间: 2024-10-13 00:02:50

[原创]分享一个轻量级日志类的相关文章

分享一个记录日志的类,可多线程使用。

好久没写博客了,今天分享一个自己用的日志类,非原创,借鉴了前辈的一个想法,然后修改来的. 日志我们是必须的,现在程序都是多线程并发了,记日志就有可能出现问题了,lock?影响性能.log4net太重量级了,本日志是一个轻量级的小工具. 废话不多说,看源码: 1 using System; 2 using System.Collections.Generic; 3 using System.IO; 4 using System.Text; 5 6 namespace GEDU.CourseOnli

分享一个Redis帮助类

最近在项目中使用了redis来存储已经下载过的URL,项目中用的是ServiceStack来操作Redis,一开始ServiceStack的版本用的是最新的,后来发现ServiceStack已经商业化了,非商业版本每个小时只能操作6000次Redis,后来把ServiceStack换成了V3版本. 在项目中用到了redis的Hash集合,但是ServiceStack封装的使用起来不方便,于是就自己封装了一个dll,利用的ServiceStack的pool来动态创建IRedisClient实例,创

今天分享一个抽奖的类Lottery

/* * Copyright (C) 2014 Jason Fang ( [email protected] ) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://

分享一个PHP调试日志类

分享一个我自己用的在 WordPress 开发中用得到的一个调试日志类. <?php /** * @author: suifengtec coolwp.com * @date: 2013-02-03 09:55:55 * @last Modified by: suifengtec coolwp.com * @last Modified time: 2015-07-12 18:40:02 */ if(function_exists('add_action')){ defined('ABSPATH'

重复造轮子,编写一个轻量级的异步写日志的实用工具类(LogAsyncWriter)

一说到写日志,大家可能推荐一堆的开源日志框架,如:Log4Net.NLog,这些日志框架确实也不错,比较强大也比较灵活,但也正因为又强大又灵活,导致我们使用他们时需要引用一些DLL,同时还要学习各种用法及配置文件,这对于有些小工具.小程序.小网站来说,有点“杀鸡焉俺用牛刀”的感觉,而且如果对这些日志框架不了解,可能输出来的日志性能或效果未毕是与自己所想的,鉴于这几个原因,我自己重复造轮子,编写了一个轻量级的异步写日志的实用工具类(LogAsyncWriter),这个类还是比较简单的,实现思路也很

【开源.NET】 分享一个前后端分离的轻量级内容管理框架

开发框架要考虑的面太多了:安全.稳定.性能.效率.扩展.整洁,还要经得起实践的考验,从零开发一个可用的框架,是很耗时费神的工作.网上很多开源的框架,为何还要自己开发?我是基于以下两点: 没找到合适的:安全.稳定.简单.易用.高效.免费: 想成为架构师: 于是就自己动手,参考网上开源的项目和借鉴网友的设计思路(特别是萧秦系列博文),结合自己的实践,开发了一个简单.易用.高效的的框架,虽然不完善,但也能解决现实中的问题.不过随着见识增广,发现没负责过千万级别的项目难以成为架构师,也不可能开发出一个完

分享一个简单的简单的SQLHelper类

分享一个简单的简单的SQLHelper类,代码如下: 1 class SqlHelper 2 { 3 public static readonly string connstr = 4 ConfigurationManager.ConnectionStrings["dbConnStr"].ConnectionString; 5 6 public static int ExecuteNonQuery(string cmdText, 7 params SqlParameter[] para

分享一个我用的 unity 日志系统

我们在开发中,往往会觉得unity自带的日志系统不是那么好用,比如,不能筛选日志,不能打印在屏幕上,想生成自己的日志文件,等等等等的需求,这时候就需要自己来扩展编辑器的相关功能了. 我这里实现的需求比较简单,就是给日志加了个key,可以根据这个key来对暂时不需要显示的日志进行过滤,还有就是将我们的日志打印到屏幕上去. 打印屏幕的类参考了由 Jeremy Hollingsworth 和 Simon Waite 这2人写的一个 DebugConsole.cs 类,不过他们2个的版本实在是太早了,而

自己实现的一个 .net 缓存类(原创)

public class CacheContainer { private static Hashtable ht = new Hashtable(); /// <summary> /// objId :缓存id,objObject:缓存对象,expires:绝对过期时间 /// </summary> /// <param name="objId"></param> /// <param name="objObject&q