File: System\Diagnostics\Metrics\ObservableInstrument.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;

namespace System.Diagnostics.Metrics
{
    /// <summary>
    /// ObservableInstrument{T} is the base class from which all metrics observable instruments will inherit from.
    /// </summary>
    /// <remarks>
    /// This class supports only the following generic parameter types: <see cref="byte" />, <see cref="short" />, <see cref="int" />, <see cref="long" />, <see cref="float" />, <see cref="double" />, and <see cref="decimal" />
    /// </remarks>
    public abstract class ObservableInstrument<T> : Instrument where T : struct
    {
        /// <summary>
        /// Create the metrics observable instrument using the properties meter, name, description, and unit.
        /// All classes extending ObservableInstrument{T} need to call this constructor when constructing object of the extended class.
        /// </summary>
        /// <param name="meter">The meter that created the instrument.</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>
        protected ObservableInstrument(Meter meter, string name, string? unit, string? description) : this(meter, name, unit, description, tags: null)
        {
        }

        /// <summary>
        /// Create the metrics observable instrument using the properties meter, name, description, and unit.
        /// All classes extending ObservableInstrument{T} need to call this constructor when constructing object of the extended class.
        /// </summary>
        /// <param name="meter">The meter that created the instrument.</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">tags to attach to the counter.</param>
        protected ObservableInstrument(Meter meter, string name, string? unit, string? description, IEnumerable<KeyValuePair<string, object?>>? tags) : base(meter, name, unit, description, tags)
        {
            ValidateTypeParameter<T>();
        }

        /// <summary>
        /// Observe() fetches the current measurements being tracked by this instrument. All classes extending ObservableInstrument{T} need to implement this method.
        /// </summary>
        protected abstract IEnumerable<Measurement<T>> Observe();

        /// <summary>
        /// A property tells if the instrument is an observable instrument. This property will return true for all metrics observable instruments.
        /// </summary>
        public override bool IsObservable => true;

        // Returns the underlying user callback for built-in observable instruments, or null for user-defined subclasses.
        // Subclasses provide their measurements via the abstract Observe() method instead.
        internal virtual object? Callback => null;

        // Will be called from MeterListener.RecordObservableInstruments for each observable instrument.
        internal override void Observe(MeterListener listener)
        {
            object? state = GetSubscriptionState(listener);

            // Fast path for the built-in observable instruments: dispatch their callbacks
            // directly to the listener, avoiding the per-observation Measurement<T>[1] allocation that
            // the IEnumerable<Measurement<T>> path used to require.
            object? callback = Callback;

            if (callback is Func<T> valueOnlyFunc)
            {
                listener.NotifyMeasurement(this, valueOnlyFunc(), Instrument.EmptyTags, state);
                return;
            }

            if (callback is Func<Measurement<T>> measurementOnlyFunc)
            {
                Measurement<T> measurement = measurementOnlyFunc();
                listener.NotifyMeasurement(this, measurement.Value, measurement.Tags, state);
                return;
            }

            // Func<IEnumerable<Measurement<T>>> built-ins and user-defined ObservableInstrument<T> subclasses
            // both fall through to the virtual Observe() override.
            IEnumerable<Measurement<T>> measurements = Observe();
            if (measurements is null)
            {
                return;
            }

            foreach (Measurement<T> measurement in measurements)
            {
                listener.NotifyMeasurement(this, measurement.Value, measurement.Tags, state);
            }
        }
    }
}