-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathSampleAgent.cs
More file actions
100 lines (85 loc) · 2.58 KB
/
Copy pathSampleAgent.cs
File metadata and controls
100 lines (85 loc) · 2.58 KB
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
using System;
using System.Diagnostics;
using System.IO;
using System.Threading;
namespace StatsSharp
{
public class SampleAgent
{
readonly StatsCollection collectedStats = new();
Thread worker = null;
public StatsSummary CurrentStats = new(DateTime.UtcNow, Array.Empty<StatsValue>());
public TimeSpan FlushInterval = TimeSpan.FromSeconds(10);
public TimeSpan SampleInterval = TimeSpan.FromSeconds(1);
public IStatsClient Stats => collectedStats;
public event EventHandler<ErrorEventArgs> OnError;
public event Action<IStatsClient> Flushing;
public event Action<StatsSummary> Flushed;
public event Action<IStatsClient> Sample;
public void Start() {
if (worker != null && worker.IsAlive)
throw new InvalidOperationException("Already started.");
if (worker == null)
worker = new Thread(RunWorker) {
IsBackground = true,
Name = nameof(SampleAgent),
};
worker.Start(this);
}
static void RunWorker(object obj) {
var self = (SampleAgent)obj;
try {
var sampleTime = new Stopwatch();
var nextFlush = AlignToInterval(DateTime.UtcNow + self.FlushInterval, self.FlushInterval);
while(self.worker != null) {
sampleTime.Restart();
self.ReadSample();
if (DateTime.UtcNow >= nextFlush) {
self.Flush(nextFlush);
nextFlush += self.FlushInterval;
}
AwaitNextSample(self.SampleInterval, sampleTime.Elapsed);
}
}
catch (Exception ex) {
self.HandleError(ex);
}
}
void ReadSample() => Invoke(Sample, Stats);
static void AwaitNextSample(TimeSpan sampleInterval, TimeSpan sampleTime) {
var delay = sampleInterval - sampleTime;
if (delay <= TimeSpan.Zero)
return;
Thread.Sleep(delay);
}
public void Stop() {
var x = worker;
worker = null;
x.Join();
}
public void Flush(DateTime lastFlush) {
Invoke(Flushing, collectedStats);
CurrentStats = collectedStats.Flush(lastFlush, FlushInterval);
Invoke(Flushed, CurrentStats);
}
void Invoke<T>(Action<T> action, T args) {
if(action == null)
return;
try {
action(args);
} catch(Exception ex) {
HandleError(ex);
}
}
internal void HandleError(Exception ex) {
var err = OnError;
if (err == null)
return;
var e = new ErrorEventArgs(ex);
foreach (EventHandler<ErrorEventArgs> handler in err.GetInvocationList())
try { handler(this, e); } catch { }
}
static DateTime AlignToInterval(DateTime now, TimeSpan interval) =>
now.AddTicks(-(now.TimeOfDay.Ticks % interval.Ticks));
}
}