| File: System\Diagnostics\Metrics\Instrument.cs | Web Access |
| Project: src\runtime\src\libraries\System.Diagnostics.DiagnosticSource\src\System.Diagnostics.DiagnosticSource.csproj (System.Diagnostics.DiagnosticSource) |
// 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.ComponentModel; using System.Runtime.CompilerServices; namespace System.Diagnostics.Metrics { /// <summary> /// Base class of all Metrics Instrument classes /// </summary> public abstract class Instrument { internal static KeyValuePair<string, object?>[] EmptyTags => Array.Empty<KeyValuePair<string, object?>>(); // The SyncObject is used to synchronize the following operations: // - Instrument.Publish() // - Meter constructor // - Meter.Dispose // - MeterListener.EnableMeasurementEvents // - MeterListener.DisableMeasurementEvents // - MeterListener.Start // - MeterListener.Dispose internal static object SyncObject { get; } = new object(); // We use LikedList here so we don't have to take any lock while iterating over the list as we always hold on a node which be either valid or null. // DiagLinkedList is thread safe for Add and Remove operations. internal readonly DiagLinkedList<ListenerSubscription> _subscriptions = new DiagLinkedList<ListenerSubscription>(); /// <summary> /// Constructs a new instance of <see cref="Instrument"/>. /// </summary> /// <param name="meter">The meter that created the instrument. Cannot be null.</param> /// <param name="name">The instrument name. Cannot be null.</param> protected Instrument(Meter meter, string name) : this(meter, name, unit: null, description: null, tags: null) { } /// <summary> /// Constructs a new instance of <see cref="Instrument"/>. /// </summary> /// <param name="meter">The meter that created the instrument. Cannot be null.</param> /// <param name="name">The instrument name. Cannot be null.</param> /// <param name="unit">Optional instrument unit of measurements.</param> /// <param name="description">Optional instrument description.</param> [EditorBrowsable(EditorBrowsableState.Never)] protected Instrument(Meter meter, string name, string? unit, string? description) : this(meter, name, unit, description, tags: null) { } /// <summary> /// Constructs a new instance of <see cref="Instrument"/>. /// </summary> /// <param name="meter">The meter that created the instrument. Cannot be null.</param> /// <param name="name">The instrument name. Cannot be null.</param> /// <param name="unit">Optional instrument unit of measurements.</param> /// <param name="description">Optional instrument description.</param> /// <param name="tags">Optional instrument tags.</param> protected Instrument( Meter meter, string name, string? unit = default, string? description = default, IEnumerable<KeyValuePair<string, object?>>? tags = default) { Meter = meter ?? throw new ArgumentNullException(nameof(meter)); Name = name ?? throw new ArgumentNullException(nameof(name)); Description = description; Unit = unit; if (tags is not null) { var tagList = new List<KeyValuePair<string, object?>>(tags); tagList.Sort((left, right) => string.Compare(left.Key, right.Key, StringComparison.Ordinal)); Tags = tagList; } } /// <summary> /// Publish is activating the instrument to start recording measurements and to allow listeners to start listening to such measurements. /// </summary> protected void Publish() { // All instruments call Publish when they are created. We don't want to publish the instrument if the Meter is not supported. if (!Meter.IsSupported) { return; } List<MeterListener>? allListeners = null; lock (Instrument.SyncObject) { if (Meter.Disposed || !Meter.AddInstrument(this)) { return; } allListeners = MeterListener.GetAllListeners(); } if (allListeners is not null) { foreach (MeterListener listener in allListeners) { listener.InstrumentPublished?.Invoke(this, listener); } } } /// <summary> /// Gets the Meter which created the instrument. /// </summary> public Meter Meter { get; } /// <summary> /// Gets the instrument name. /// </summary> public string Name { get; } /// <summary> /// Gets the instrument description. /// </summary> public string? Description { get; } /// <summary> /// Gets the instrument unit of measurements. /// </summary> public string? Unit { get; } /// <summary> /// Returns the tags associated with the instrument. /// </summary> public IEnumerable<KeyValuePair<string, object?>>? Tags { get; } /// <summary> /// Checks if there is any listeners for this instrument. /// </summary> public bool Enabled => _subscriptions.First is not null; /// <summary> /// A property tells if the instrument is an observable instrument. /// </summary> public virtual bool IsObservable => false; // NotifyForUnpublishedInstrument is called from Meter.Dispose() internal void NotifyForUnpublishedInstrument() { DiagNode<ListenerSubscription>? current = _subscriptions.First; while (current is not null) { current.Value.Listener.DisableMeasurementEvents(this); current = current.Next; } _subscriptions.Clear(); } internal static void ValidateTypeParameter<T>() { Type type = typeof(T); if (type != typeof(byte) && type != typeof(short) && type != typeof(int) && type != typeof(long) && type != typeof(double) && type != typeof(float) && type != typeof(decimal)) { throw new InvalidOperationException(SR.Format(SR.UnsupportedType, type)); } } // Called from MeterListener.EnableMeasurementEvents internal object? EnableMeasurement(ListenerSubscription subscription, out bool oldStateStored) { oldStateStored = false; if (!_subscriptions.AddIfNotExist(subscription, (s1, s2) => object.ReferenceEquals(s1.Listener, s2.Listener))) { ListenerSubscription oldSubscription = _subscriptions.Remove(subscription, (s1, s2) => object.ReferenceEquals(s1.Listener, s2.Listener)); _subscriptions.AddIfNotExist(subscription, (s1, s2) => object.ReferenceEquals(s1.Listener, s2.Listener)); oldStateStored = object.ReferenceEquals(oldSubscription.Listener, subscription.Listener); return oldSubscription.State; } return false; } // Called from MeterListener.DisableMeasurementEvents internal object? DisableMeasurements(MeterListener listener) => _subscriptions.Remove(new ListenerSubscription(listener), (s1, s2) => object.ReferenceEquals(s1.Listener, s2.Listener)).State; internal virtual void Observe(MeterListener listener) { Debug.Assert(false); throw new InvalidOperationException(); } internal object? GetSubscriptionState(MeterListener listener) { DiagNode<ListenerSubscription>? current = _subscriptions.First; while (current is not null) { if (object.ReferenceEquals(listener, current.Value.Listener)) { return current.Value.State; } current = current.Next; } return null; } } }