378e38906565cfc632d3fd276b73b0958013769e
[platform/core/dotnet/diagnostics.git] /
1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3
4 using System;
5 using System.Collections.Concurrent;
6 using System.Collections.Generic;
7 using System.Collections.ObjectModel;
8 using System.ComponentModel.DataAnnotations;
9 using Microsoft.Diagnostics.Tracing;
10
11 namespace Microsoft.Diagnostics.Monitoring.EventPipe.Triggers.SystemDiagnosticsMetrics
12 {
13     /// <summary>
14     /// Trigger that detects when the specified instrument's value is held
15     /// above, below, or between threshold values for a specified duration of time.
16     /// </summary>
17     internal sealed class SystemDiagnosticsMetricsTrigger :
18         ITraceEventTrigger
19     {
20         // A cache of the list of events that are expected from the specified event provider.
21         // This is a mapping of event provider name to the event map returned by GetProviderEventMap.
22         // This allows caching of the event map between multiple instances of the trigger that
23         // use the same event provider as the source of counter events.
24         private static readonly ConcurrentDictionary<string, IReadOnlyDictionary<string, IReadOnlyCollection<string>>> _eventMapCache =
25             new(StringComparer.OrdinalIgnoreCase);
26
27         private readonly CounterFilter _filter;
28         private readonly SystemDiagnosticsMetricsTriggerImpl _impl;
29         private readonly string _meterName;
30         private readonly string _clientId;
31         private readonly string _sessionId;
32
33         public SystemDiagnosticsMetricsTrigger(SystemDiagnosticsMetricsTriggerSettings settings)
34         {
35             if (null == settings)
36             {
37                 throw new ArgumentNullException(nameof(settings));
38             }
39
40             Validate(settings);
41
42             _filter = new CounterFilter(settings.CounterIntervalSeconds);
43             _filter.AddFilter(settings.MeterName, new string[] { settings.InstrumentName });
44
45             _impl = new SystemDiagnosticsMetricsTriggerImpl(settings);
46
47             _meterName = settings.MeterName;
48
49             _clientId = settings.ClientId;
50
51             _sessionId = settings.SessionId;
52         }
53
54         public IReadOnlyDictionary<string, IReadOnlyCollection<string>> GetProviderEventMap()
55         {
56             return _eventMapCache.GetOrAdd(_meterName, CreateEventMapForProvider);
57         }
58
59         public bool HasSatisfiedCondition(TraceEvent traceEvent)
60         {
61             // Filter to the counter of interest before forwarding to the implementation
62             if (traceEvent.TryGetCounterPayload(_filter, _sessionId, _clientId, out ICounterPayload payload))
63             {
64                 return _impl.HasSatisfiedCondition(payload);
65             }
66             return false;
67         }
68
69         public static MetricSourceConfiguration CreateConfiguration(SystemDiagnosticsMetricsTriggerSettings settings)
70         {
71             Validate(settings);
72
73             MetricSourceConfiguration config = new(
74                 settings.CounterIntervalSeconds,
75                 MetricSourceConfiguration.CreateProviders(new string[] { settings.MeterName }, MetricType.Meter),
76                 settings.MaxHistograms,
77                 settings.MaxTimeSeries,
78                 useSharedSession: settings.UseSharedSession);
79             settings.ClientId = config.ClientId;
80             settings.SessionId = config.SessionId;
81
82             return config;
83         }
84
85         private static void Validate(SystemDiagnosticsMetricsTriggerSettings settings)
86         {
87             ValidationContext context = new(settings);
88             Validator.ValidateObject(settings, context, validateAllProperties: true);
89         }
90
91         private IReadOnlyDictionary<string, IReadOnlyCollection<string>> CreateEventMapForProvider(string providerName)
92         {
93             return new ReadOnlyDictionary<string, IReadOnlyCollection<string>>(
94                 new Dictionary<string, IReadOnlyCollection<string>>()
95                 {
96                     { "System.Diagnostics.Metrics", new ReadOnlyCollection<string>(Array.Empty<string>()) }
97                 });
98         }
99     }
100 }