prometheus-net
118 строк · 3.9 Кб
1#if NET
2using System.Diagnostics;
3
4namespace Prometheus;
5
6/// <summary>
7/// Monitors all DiagnosticSource events and exposes them as Prometheus counters.
8/// The event data is discarded, only the number of occurrences is measured.
9/// </summary>
10/// <remarks>
11/// This is a very coarse data set due to lacking any intelligence on the payload.
12/// Users are recommended to make custom adapters with more detail for specific use cases.
13/// </remarks>
14public sealed class DiagnosticSourceAdapter : IDisposable
15{
16/// <summary>
17/// Starts listening for DiagnosticSource events and reporting them as Prometheus metrics.
18/// Dispose of the return value to stop listening.
19/// </summary>
20public static IDisposable StartListening() => StartListening(DiagnosticSourceAdapterOptions.Default);
21
22/// <summary>
23/// Starts listening for DiagnosticSource events and reporting them as Prometheus metrics.
24/// Dispose of the return value to stop listening.
25/// </summary>
26public static IDisposable StartListening(DiagnosticSourceAdapterOptions options) => new DiagnosticSourceAdapter(options);
27
28private DiagnosticSourceAdapter(DiagnosticSourceAdapterOptions options)
29{
30_options = options;
31_metric = Metrics.WithCustomRegistry(options.Registry)
32.CreateCounter("diagnostic_events_total", "Total count of events received via the DiagnosticSource infrastructure.", labelNames: new[]
33{
34"source", // Name of the DiagnosticSource
35"event" // Name of the event
36});
37
38var newListenerObserver = new NewListenerObserver(OnNewListener);
39_newListenerSubscription = DiagnosticListener.AllListeners.Subscribe(newListenerObserver);
40}
41
42private readonly DiagnosticSourceAdapterOptions _options;
43private readonly Counter _metric;
44
45private readonly IDisposable _newListenerSubscription;
46
47// listener name -> subscription
48private readonly Dictionary<string, IDisposable> _newEventSubscription = new Dictionary<string, IDisposable>();
49private readonly object _newEventSubscriptionLock = new object();
50
51private void OnNewListener(DiagnosticListener listener)
52{
53lock (_newEventSubscriptionLock)
54{
55if (_newEventSubscription.TryGetValue(listener.Name, out var oldSubscription))
56{
57oldSubscription.Dispose();
58_newEventSubscription.Remove(listener.Name);
59}
60
61if (!_options.ListenerFilterPredicate(listener))
62return;
63
64var listenerName = listener.Name;
65var newEventObserver = new NewEventObserver(kvp => OnEvent(listenerName, kvp.Key, kvp.Value));
66_newEventSubscription[listenerName] = listener.Subscribe(newEventObserver);
67}
68}
69
70private void OnEvent(string listenerName, string eventName, object? payload)
71{
72_metric.WithLabels(listenerName, eventName).Inc();
73}
74
75private sealed class NewListenerObserver(Action<DiagnosticListener> onNewListener) : IObserver<DiagnosticListener>
76{
77public void OnCompleted()
78{
79}
80
81public void OnError(Exception error)
82{
83}
84
85public void OnNext(DiagnosticListener listener)
86{
87onNewListener(listener);
88}
89}
90
91private sealed class NewEventObserver(Action<KeyValuePair<string, object?>> onEvent) : IObserver<KeyValuePair<string, object?>>
92{
93public void OnCompleted()
94{
95}
96
97public void OnError(Exception error)
98{
99}
100
101public void OnNext(KeyValuePair<string, object?> receivedEvent)
102{
103onEvent(receivedEvent);
104}
105}
106
107public void Dispose()
108{
109_newListenerSubscription.Dispose();
110
111lock (_newEventSubscriptionLock)
112{
113foreach (var subscription in _newEventSubscription.Values)
114subscription.Dispose();
115}
116}
117}
118#endif