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