/
DiagnosticCounter.cs
156 lines (134 loc) · 5.28 KB
/
DiagnosticCounter.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
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Collections.Generic;
using System.Runtime.Versioning;
using System.Text;
using System.Threading;
namespace System.Diagnostics.Tracing
{
/// <summary>
/// DiagnosticCounter is an abstract class that serves as the parent class for various Counter* classes,
/// namely EventCounter, PollingCounter, IncrementingEventCounter, and IncrementingPollingCounter.
/// </summary>
#if !ES_BUILD_STANDALONE
#if !FEATURE_WASM_PERFTRACING
[UnsupportedOSPlatform("browser")]
#endif
#endif
public abstract class DiagnosticCounter : IDisposable
{
/// <summary>
/// All Counters live as long as the EventSource that they are attached to unless they are
/// explicitly Disposed.
/// </summary>
/// <param name="Name">The name.</param>
/// <param name="EventSource">The event source.</param>
internal DiagnosticCounter(string Name, EventSource EventSource)
{
ArgumentNullException.ThrowIfNull(Name);
ArgumentNullException.ThrowIfNull(EventSource);
this.Name = Name;
this.EventSource = EventSource;
}
/// <summary>Adds the counter to the set that the EventSource will report on.</summary>
/// <remarks>
/// Must only be invoked once, and only after the instance has been fully initialized.
/// This should be invoked by a derived type's ctor as the last thing it does.
/// </remarks>
private protected void Publish()
{
Debug.Assert(_group is null);
Debug.Assert(Name != null);
Debug.Assert(EventSource != null);
_group = CounterGroup.GetCounterGroup(EventSource);
_group.Add(this);
}
/// <summary>
/// Removes the counter from set that the EventSource will report on. After being disposed, this
/// counter will do nothing and its resource will be reclaimed if all references to it are removed.
/// If an EventCounter is not explicitly disposed it will be cleaned up automatically when the
/// EventSource it is attached to dies.
/// </summary>
public void Dispose()
{
if (_group != null)
{
_group.Remove(this);
_group = null;
}
}
/// <summary>
/// Adds a key-value metadata to the EventCounter that will be included as a part of the payload
/// </summary>
public void AddMetadata(string key, string? value)
{
lock (this)
{
_metadata ??= new Dictionary<string, string?>();
_metadata.Add(key, value);
}
}
private string _displayName = "";
public string DisplayName
{
get => _displayName;
set
{
ArgumentNullException.ThrowIfNull(DisplayName);
_displayName = value;
}
}
private string _displayUnits = "";
public string DisplayUnits
{
get => _displayUnits;
set
{
ArgumentNullException.ThrowIfNull(DisplayUnits);
_displayUnits = value;
}
}
public string Name { get; }
public EventSource EventSource { get; }
#region private implementation
private CounterGroup? _group;
private Dictionary<string, string?>? _metadata;
internal abstract void WritePayload(float intervalSec, int pollingIntervalMillisec);
internal void ReportOutOfBandMessage(string message)
{
EventSource.ReportOutOfBandMessage(message);
}
internal string GetMetadataString()
{
Debug.Assert(Monitor.IsEntered(this));
if (_metadata == null)
{
return "";
}
// The dictionary is only initialized to non-null when there's metadata to add, and no items
// are ever removed, so if the dictionary is non-null, there must also be at least one element.
Dictionary<string, string?>.Enumerator enumerator = _metadata.GetEnumerator();
Debug.Assert(_metadata.Count > 0);
bool gotOne = enumerator.MoveNext();
Debug.Assert(gotOne);
// If there's only one element, just concat a string for it.
KeyValuePair<string, string?> current = enumerator.Current;
if (!enumerator.MoveNext())
{
return current.Key + ":" + current.Value;
}
// Otherwise, append it, then append the element we moved to, and then
// iterate through the remainder of the elements, appending each.
StringBuilder sb = new StringBuilder().Append(current.Key).Append(':').Append(current.Value);
do
{
current = enumerator.Current;
sb.Append(',').Append(current.Key).Append(':').Append(current.Value);
}
while (enumerator.MoveNext());
// Return the final string.
return sb.ToString();
}
#endregion // private implementation
}
}