-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathLogReader.cs
165 lines (138 loc) · 5.38 KB
/
LogReader.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
/*
A simple log file monitor class for .NET
Uses a threaded timer to check for changes in the file, if the file length has changed then the unread
section of the file is read and parsed into lines before being passed back to the event handler.
Note, because the class uses the threaded timer callbacks on the event hander WILL be made form a
different thread, keep this in mind when using the class.
This class is more reliable than the FileSystemWatcher class provided by Microsoft which often does not
fire an event even after the file size changes
TODO:
* Customer timer period
* Option to ignore exsiting file contents
* Option to provide SynchronizingObject
Sample Usage:
var monitor = new LogFileMonitor("c:\temp\app.log", "\r\n");
monitor.OnLine += (s, e) =>
{
// WARNING.. this will be a different thread...
LogManager.Log(e.Line);
};
monitor.Start();
*/
// Credits https://gist.github.com/ant-fx/989dd86a1ace38a9ac58
using System;
using System.IO;
using System.Timers;
namespace ZuxiTags
{
internal class LogFileMonitorLineEventArgs : EventArgs
{
internal string Line { get; set; }
}
internal class LogFileMonitor
{
internal EventHandler<LogFileMonitorLineEventArgs> OnLine;
// file path + delimiter internals
string _path = String.Empty;
string _delimiter = String.Empty;
// timer object
Timer _t = null;
// buffer for storing data at the end of the file that does not yet have a delimiter
string _buffer = String.Empty;
// get the current size
long _currentSize = 0;
// are we currently checking the log (stops the timer going in more than once)
bool _isCheckingLog = false;
protected bool StartCheckingLog()
{
lock (_t)
{
if (_isCheckingLog)
return true;
_isCheckingLog = true;
return false;
}
}
protected void DoneCheckingLog()
{
lock (_t)
_isCheckingLog = false;
}
internal LogFileMonitor(string path, string delimiter = "\n")
{
_path = path;
_delimiter = delimiter;
}
internal void Start()
{
// get the current size
_currentSize = new FileInfo(_path).Length;
// start the timer
_t = new Timer();
_t.Elapsed += CheckLog;
_t.AutoReset = true;
_t.Start();
}
internal void CheckLog(object s, ElapsedEventArgs e)
{
if (StartCheckingLog())
{
try
{
// get the new size
var newSize = new FileInfo(_path).Length;
// if they are the same then continue.. if the current size is bigger than the new size continue
if (_currentSize >= newSize)
return;
// read the contents of the file
using (var stream = File.Open(_path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
using (StreamReader sr = new StreamReader(stream))
{
// seek to the current file position
sr.BaseStream.Seek(_currentSize, SeekOrigin.Begin);
// read from current position to the end of the file
var newData = _buffer + sr.ReadToEnd();
// if we don't end with a delimiter we need to store some data in the buffer for next time
if (!newData.EndsWith(_delimiter))
{
// we don't have any lines to process so save in the buffer for next time
if (newData.IndexOf(_delimiter) == -1)
{
_buffer += newData;
newData = String.Empty;
}
else
{
// we have at least one line so store the last section (without lines) in the buffer
var pos = newData.LastIndexOf(_delimiter) + _delimiter.Length;
_buffer = newData.Substring(pos);
newData = newData.Substring(0, pos);
}
}
// split the data into lines
var lines = newData.Split(new string[] { _delimiter }, StringSplitOptions.RemoveEmptyEntries);
// send back to caller, NOTE: this is done from a different thread!
foreach (var line in lines)
{
if (OnLine != null)
OnLine(this, new LogFileMonitorLineEventArgs { Line = line });
}
}
// set the new current position
_currentSize = newSize;
}
catch (Exception)
{
}
// we done..
DoneCheckingLog();
}
}
internal void Stop()
{
if (_t == null)
return;
_t.Stop();
}
}
}