├── App.config
├── LogService.csproj
├── LogServiceHelper.cs
├── Program.cs
└── README.md
/App.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/LogService.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {F5682E77-B356-4721-A298-6BCBC121CDD0}
8 | Exe
9 | Properties
10 | LogService
11 | LogService
12 | v4.5.2
13 | 512
14 | true
15 |
16 |
17 | AnyCPU
18 | true
19 | full
20 | false
21 | bin\Debug\
22 | DEBUG;TRACE
23 | prompt
24 | 4
25 |
26 |
27 | AnyCPU
28 | pdbonly
29 | true
30 | bin\Release\
31 | TRACE
32 | prompt
33 | 4
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
61 |
--------------------------------------------------------------------------------
/LogServiceHelper.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Concurrent;
3 | using System.Collections.Generic;
4 | using System.IO;
5 | using System.Linq;
6 | using System.Text;
7 | using System.Threading;
8 | using System.Threading.Tasks;
9 |
10 | namespace LogService
11 | {
12 | /**
13 | * 日志写入组件(**多线程写日志队列,单线程写日志文件**)
14 | * created by 王军华 20170207 599871338
15 | * 使用方法:
16 | * 在Global.asax的Application_Start方法中调用: Utility.LogServiceHelper.Intance.Start();
17 | * 在使用的地方调用 LogServiceHelper.Intance.PutLog(new LogModel() { CreatedTime=DateTime.Now, Dir="operator", Msg=log.URL, Operator=log.LoginName });
18 | * 在关闭的地方调用LogServiceHelper.Intance.Stop();
19 | * 功能描述:1.多类型日志自动归类,将不同类型的日志写入不同的文件中。例如:登录日志,操作日志,错误日志等
20 | * 2.批量写入日志,减少IO操作
21 | * 3.延时写入功能,当日志大小小于某个阈值时不写入
22 | * 4.强制写入功能,当日志在批处理列表中保留时间超过某个阈值时强制写入
23 | * 5.日志分割(按天,按体积分割)
24 | * */
25 | public class LogServiceHelper
26 | {
27 | private LogServiceHelper() { }
28 | public static readonly LogServiceHelper Intance = new LogServiceHelper();
29 | private bool _isStart = false;
30 | private Thread _thread = null;
31 | private int _maxByteSize = 1024 * 64;//64KB,当单个缓存日志达到多少byte时写入文件
32 | private int _maxFileSize = 1024 * 1024 * 1;//1MB,单个日志文件的大小
33 | private int _maxSecond = 30;//最大相隔时间,不管缓存区中的日志大小到不到64K都写入硬盘
34 | private int _maxWriteErrorTime = 10;//最大连续写入错误次数
35 | private int _currentWriteErrorTime = 0;//当前连续写入错误次数
36 | private string _baseDirectory = string.Empty;
37 | private LogModel _tmpLog = null;
38 | private ConcurrentQueue queue = new ConcurrentQueue();//日志队列
39 | //对不同类型的日志进行区分合并
40 | private List _logBatch = new List();//批处理列表
41 | private LogBatch _tmpLogBatch = null;
42 | ///
43 | /// 开始写入日志,系统初始化时调用:向磁盘写入日志需要满足同一类型日志条数>=batchCount条或者同一类型日志存在超过maxSecond秒
44 | ///
45 | /// 日志保存的基目录(全路径),例如:System.Web.HttpContext.Current.Server.MapPath("~/logs/")
46 | /// 当日志缓存队列日志达到多少byte时写入文件,默认64KB
47 | /// 单个日志文件大小,默认1M
48 | /// 当同一类型日志存在且超过maxSecond秒时向磁盘写入日志
49 | public void Start(string baseDirectory, int maxByteSize = 65536, int maxFileSize = 1048576, int maxSecond = 30)
50 | {
51 | this._baseDirectory = baseDirectory;
52 | this._maxByteSize = maxByteSize;
53 | this._maxFileSize = maxFileSize;
54 | this._maxSecond = maxSecond;
55 | _thread = new Thread(TastExecuting);
56 | _thread.IsBackground = true;
57 | _thread.Start();
58 | }
59 | public void AgainStart()
60 | {
61 | _isStart = true;
62 | }
63 | public void Stop()
64 | {
65 | _isStart = false;
66 | }
67 | private void TastExecuting()
68 | {
69 | _isStart = true;
70 | while (_isStart)
71 | {
72 |
73 | if (queue.Count > 0)
74 | {
75 | QueueOut();
76 | }
77 | LogBatchWrite();
78 |
79 | }
80 | }
81 | public long GetQueueCount()
82 | {
83 | return queue.Count;
84 | }
85 | private void QueueOut()
86 | {
87 | queue.TryDequeue(out _tmpLog);
88 | if (_tmpLog != null)
89 | {
90 | //不同类型的日志放在不同的对象中
91 | _tmpLogBatch = _logBatch.FirstOrDefault(c => c.Dir == _tmpLog.Dir);
92 | if (_tmpLogBatch == null)
93 | {
94 | _tmpLogBatch = new LogBatch();
95 | _tmpLogBatch.StartTime = DateTime.Now;
96 | _tmpLogBatch.Dir = _tmpLog.Dir;
97 | _logBatch.Add(_tmpLogBatch);
98 | }
99 | _tmpLogBatch.Count += 1;
100 | _tmpLogBatch.LogStr = _tmpLogBatch.LogStr.AppendFormat("时间【{0}】,操作人【{1}】,信息【{2}】" + Environment.NewLine, _tmpLog.CreatedTime.ToString("yyyy-MM-dd HH:mm ss"), _tmpLog.Operator, _tmpLog.Msg);
101 | _tmpLog = null;
102 | }
103 | }
104 | private void LogBatchWrite()
105 | {
106 | //缓存日志,定期写入文件,空间换时间 避免频繁操作硬盘 造成IO开销过大
107 | foreach (var item in _logBatch)
108 | {
109 | int second = (DateTime.Now - item.StartTime).Seconds;
110 | if (item.LogStr.Length > _maxByteSize || second >= _maxSecond)
111 | {
112 | //写入日志
113 | if (WriteFile(item.LogStr.ToString(), item.Dir))
114 | {
115 | item.StartTime = DateTime.Now;
116 | item.Count = 0;
117 | item.LogStr.Clear();
118 | }
119 | }
120 | }
121 | }
122 |
123 | ///
124 | /// 关闭线程
125 | ///
126 | public void Abort()
127 | {
128 | _isStart = false;
129 | _thread.Abort();
130 | }
131 | ///
132 | /// 日志写入
133 | ///
134 | /// 日志类型,如登录日志(log),错误日志(error),操作日志(operator)
135 | /// 日志内容
136 | /// 操作人
137 | public void Write(string logType, string content, string userName = "")
138 | {
139 | queue.Enqueue(new LogModel() { Dir = logType, Msg = content, Operator = userName });
140 | }
141 | public void Write(LogModel log)
142 | {
143 | queue.Enqueue(log);
144 | }
145 | private bool WriteFile(string log, string sDirectory)
146 | {
147 | if (string.IsNullOrEmpty(log))
148 | {
149 | return true;
150 | }
151 | //c:\log\login\2016\1\20160111.log
152 | DateTime now = DateTime.Now;
153 | string year = now.Year.ToString();
154 | string month = now.Month.ToString();
155 | string dir = Path.Combine(_baseDirectory, year, month);
156 | try
157 | {
158 | if (!Directory.Exists(dir))
159 | {
160 | Directory.CreateDirectory(dir); //如果文件夹不存在,则创建一个新的
161 | }
162 | else
163 | {
164 |
165 | //request_2017年02月13日(0).log
166 | //获取文件夹dir下的所有文件
167 | int index = 0;
168 | var fileList = Directory.GetFiles(dir).ToList();
169 | if (fileList.Count > 0)
170 | {
171 | index = fileList.Count(c => c.Contains(sDirectory));
172 | if (index > 0)
173 | {
174 | index--;
175 | }
176 | }
177 | string filename = sDirectory + "_" + now.ToString("yyyy年MM月dd日") + "(" + index + ").log";
178 | string fileNameNew = Path.Combine(dir, filename); //文件不存在则创建
179 | if (!System.IO.File.Exists(fileNameNew))
180 | {
181 | WriteFile(log, fileNameNew, true);
182 | }
183 | else
184 | {
185 | //获取文件大小
186 | FileInfo fileInfo = new FileInfo(fileNameNew);
187 | if (fileInfo.Length > _maxFileSize)//如果大于单个文件最大体积
188 | {
189 | fileNameNew = fileNameNew.Replace("(" + index + ").log", "(" + (index + 1) + ").log");
190 | if (!System.IO.File.Exists(fileNameNew))
191 | {
192 | WriteFile(log, fileNameNew, true);
193 | }
194 | else
195 | {
196 | WriteFile(log, fileNameNew, false);
197 | }
198 | }
199 | else
200 | {
201 |
202 | WriteFile(log, fileNameNew, false);
203 | }
204 |
205 | }
206 |
207 | }
208 | _currentWriteErrorTime = 0;
209 | return true;
210 | }
211 | catch (Exception ex)
212 | {
213 | _currentWriteErrorTime++;
214 | Thread.Sleep(10000);//暂停10秒
215 | if (_currentWriteErrorTime > _maxWriteErrorTime)
216 | {
217 | Stop();//停止写入日志,发送报警短信
218 | }
219 | return false;
220 | }
221 | finally
222 | {
223 |
224 | }
225 |
226 | }
227 | private void WriteFile(string log, string filePath, bool isCreated)
228 | {
229 | FileStream file = null;
230 | if (isCreated)
231 | {
232 | file = new FileStream(filePath, FileMode.CreateNew);
233 | }
234 | else
235 | {
236 | file = new FileStream(filePath, FileMode.Append);
237 | }
238 | StreamWriter sw = new StreamWriter(file, Encoding.Default);
239 | sw.Write(log);
240 | sw.Flush();
241 | sw.Close();
242 | file.Close();
243 | }
244 | }
245 | [Serializable]
246 | public class LogModel
247 | {
248 | public LogModel()
249 | {
250 | CreatedTime = DateTime.Now;
251 | }
252 | public DateTime CreatedTime { get; set; }
253 | public string Msg { get; set; }
254 | public string Operator { get; set; }
255 | public string Level { get; set; }
256 | ///
257 | /// 保存的文件夹,基础文件夹为项目所在位置下的/log,如果Dir为login,那么Dir=/log/login
258 | ///
259 | public string Dir { get; set; }
260 | }
261 | [Serializable]
262 | internal class LogBatch
263 | {
264 | public LogBatch()
265 | {
266 | LogStr = new StringBuilder();
267 | }
268 | public string Dir { get; set; }
269 | public int Count { get; set; }
270 | public DateTime StartTime { get; set; }
271 | public StringBuilder LogStr { get; set; }
272 | }
273 | }
274 |
--------------------------------------------------------------------------------
/Program.cs:
--------------------------------------------------------------------------------
1 |
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Diagnostics;
5 | using System.IO;
6 | using System.Linq;
7 | using System.Text;
8 | using System.Threading;
9 | using System.Threading.Tasks;
10 |
11 | namespace LogService
12 | {
13 | class Program
14 | {
15 | static void Main(string[] args)
16 | {
17 | string dir= Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "log/");
18 | LogServiceHelper.Intance.Start(dir);
19 | LogWriteTest();
20 | }
21 | static void LogWriteTest()
22 | {
23 | Stopwatch sw = new Stopwatch();
24 | sw.Start();
25 | for (int i = 0; i < 5; i++)
26 | {
27 | var _thread = new Thread(Start);
28 | _thread.IsBackground = true;
29 | _thread.Start(i);
30 | }
31 | while (LogServiceHelper.Intance.GetQueueCount() > 0)
32 | {
33 |
34 | }
35 | sw.Stop();
36 | LogServiceHelper.Intance.Stop();
37 | long s = sw.ElapsedMilliseconds / 1000;
38 | Console.WriteLine("共耗时:"+s+"秒");
39 | Console.ReadLine();
40 |
41 | }
42 | static void Start(object tag)
43 | {
44 | for (int i = 0; i < 200000; i++)
45 | {
46 | LogServiceHelper.Intance.Write("测试" + tag, i + "测试测试测试测试测试", "sa");
47 | }
48 | }
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # LogService
2 | 基于C#的日志记录组件,该组件采用多线程写日志队列,单线程写日志文件的模式记录日志。平均每秒写入20万条记录。
3 |
4 |
5 | # 功能介绍
6 | * 1.多类型日志自动归类,将不同类型的日志写入不同的文件中。例如:登录日志,操作日志,错误日志等
7 | * 2.批量写入日志,减少IO操作
8 | * 3.延时写入功能,当日志缓存区大小小于某个阈值时不写入
9 | * 4.强制写入功能,当日志在批处理列表中保留时间超过某个阈值时强制写入
10 | * 5.日志分割(按天,按体积分割)
11 | * 6.报警机制,当写入日志出错,并且连续出错10次(可配置),可发送报警短信(需自己实现)
12 |
13 | 详情请参照:http://www.cnblogs.com/eggTwo/p/6394028.html
14 |
15 |
16 |
17 | # 测试代码如下
18 |
19 | static void Main(string[] args)
20 | {
21 | string dir= Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "log/");
22 | LogServiceHelper.Intance.Start(dir);
23 | LogWriteTest();
24 | }
25 | static void LogWriteTest()
26 | {
27 | Stopwatch sw = new Stopwatch();
28 | sw.Start();
29 | for (int i = 0; i < 5; i++)
30 | {
31 | var _thread = new Thread(Start);
32 | _thread.IsBackground = true;
33 | _thread.Start(i);
34 | }
35 | while (LogServiceHelper.Intance.GetQueueCount() > 0)
36 | {
37 |
38 | }
39 | sw.Stop();
40 | LogServiceHelper.Intance.Stop();
41 | long s = sw.ElapsedMilliseconds / 1000;
42 | Console.WriteLine("共耗时:"+s+"秒");
43 | Console.ReadLine();
44 |
45 | }
46 | static void Start(object tag)
47 | {
48 | for (int i = 0; i < 200000; i++)
49 | {
50 | LogServiceHelper.Intance.Write("测试" + tag, i + "测试测试测试测试测试", "sa");
51 | }
52 | }
53 |
54 |
55 |
56 |
57 |
--------------------------------------------------------------------------------