File: System\Diagnostics\Metrics\InstrumentAdvice.cs
Web Access
Project: src\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.Collections.ObjectModel;
 
namespace System.Diagnostics.Metrics
{
    /// <summary>
    /// Contains configuration settings advised to be used by metrics consumers when recording measurements for a given <see cref="Instrument{T}"/>.
    /// </summary>
    /// <typeparam name="T">Instrument value type.</typeparam>
    public sealed class InstrumentAdvice<T> where T : struct
    {
        private readonly ReadOnlyCollection<T>? _HistogramBucketBoundaries;
 
        /// <summary>
        /// Initializes a new instance of the <see cref="InstrumentAdvice{T}"/> class.
        /// </summary>
        public InstrumentAdvice()
        {
            Instrument.ValidateTypeParameter<T>();
        }
 
        /// <summary>
        /// Gets the explicit bucket boundaries advised to be used with histogram instruments.
        /// </summary>
        /// <remarks>
        /// Notes:
        /// <list type="bullet">
        /// <item>A <see langword="null"/> value means no bucket boundaries have been configured and default values should be used for bucket configuration.</item>
        /// <item>An empty set of bucket boundaries hints that the histogram by default should NOT contain buckets and should only track count and sum values.</item>
        /// <item>A set of distinct increasing values for bucket boundaries hints that the histogram should use those for its default bucket configuration.</item>
        /// </list>
        /// </remarks>
        public IReadOnlyList<T>? HistogramBucketBoundaries
        {
            get => _HistogramBucketBoundaries;
            init
            {
                if (value is null)
                {
                    throw new ArgumentNullException(nameof(value));
                }
 
                List<T> bucketBoundariesCopy = new List<T>(value);
 
                if (!IsSortedAndDistinct(bucketBoundariesCopy))
                {
                    throw new ArgumentException(SR.InvalidHistogramExplicitBucketBoundaries, nameof(value));
                }
 
                _HistogramBucketBoundaries = new ReadOnlyCollection<T>(bucketBoundariesCopy);
            }
        }
 
        private static bool IsSortedAndDistinct(List<T> values)
        {
            Comparer<T> comparer = Comparer<T>.Default;
 
            for (int i = 1; i < values.Count; i++)
            {
                if (comparer.Compare(values[i - 1], values[i]) >= 0)
                {
                    return false;
                }
            }
 
            return true;
        }
    }
}