| | | 1 | | using System; |
| | | 2 | | using System.Collections.Generic; |
| | | 3 | | using System.Linq; |
| | | 4 | | |
| | | 5 | | namespace AmbientServices; |
| | | 6 | | |
| | | 7 | | #pragma warning disable CA1510 |
| | | 8 | | /// <summary> |
| | | 9 | | /// An interface to create and manage system statistics, which provide long-lived high-performance tracking of accumulat |
| | | 10 | | /// Statistics can be used to track memory allocated, time waited, minimum or maximum sizes or times, request processing |
| | | 11 | | /// Ratios of two statistics can be used to track things like average sizes or times, events per second, bytes per secon |
| | | 12 | | /// All times are in terms of ticks whose frequency is <see cref="System.Diagnostics.Stopwatch.Frequency"/>. |
| | | 13 | | /// </summary> |
| | | 14 | | /// <remarks> |
| | | 15 | | /// <pitch> |
| | | 16 | | /// The registry answering "how well are the various systems functioning under how much load?" — long-lived, high-perfor |
| | | 17 | | /// It holds only the current raw value of each statistic — sampling over time, graphing, and cross-system roll-up are l |
| | | 18 | | /// </pitch> |
| | | 19 | | /// <pledge> |
| | | 20 | | /// Statistics are identified by dash-delimited IDs and live until removed: a get-or-add call returns the already-regist |
| | | 21 | | /// Each statistic carries immutable metadata (name, description, units, fixed-floating-point adjustment, aggregation pr |
| | | 22 | | /// A built-in execution-time statistic always exists, cannot be removed, and serves as the standard denominator for rat |
| | | 23 | | /// All operations are thread-safe and safe to call from any call context. |
| | | 24 | | /// </pledge> |
| | | 25 | | /// <priority> |
| | | 26 | | /// 1. Correct aggregation across servers over convenience at the reporting site: ratios are published as descriptors na |
| | | 27 | | /// 2. Update cost over read richness: only the current raw value of each statistic is held, with sampling over time, gr |
| | | 28 | | /// </priority> |
| | | 29 | | /// </remarks> |
| | | 30 | | public interface IAmbientStatistics |
| | | 31 | | { |
| | | 32 | | /// <summary> |
| | | 33 | | /// Gets the built-in execution time statistic. |
| | | 34 | | /// </summary> |
| | | 35 | | public IAmbientStatisticReader ExecutionTime { get; } |
| | | 36 | | /// <summary> |
| | | 37 | | /// Gets a <see cref="IDictionary{TKey, TValue}"/> with all the statistics. |
| | | 38 | | /// </summary> |
| | | 39 | | IDictionary<string, IAmbientStatisticReader> Statistics { get; } |
| | | 40 | | /// <summary> |
| | | 41 | | /// Gets a <see cref="IDictionary{TKey, TValue}"/> with all the ratio statistics. |
| | | 42 | | /// </summary> |
| | | 43 | | IDictionary<string, IAmbientRatioStatistic> RatioStatistics { get; } |
| | | 44 | | /// <summary> |
| | | 45 | | /// Finds the specified statistic. |
| | | 46 | | /// </summary> |
| | | 47 | | /// <returns>A <see cref="IAmbientStatisticReader"/> the caller can use to read the statistic, or null if there is n |
| | | 48 | | IAmbientStatisticReader? ReadStatistic(string id); |
| | | 49 | | /// <summary> |
| | | 50 | | /// Adds or updates a time-based statistic with the specified identifier, description, and properties. |
| | | 51 | | /// Time-based statistics are always in seconds. |
| | | 52 | | /// </summary> |
| | | 53 | | /// <param name="type">The <see cref="AmbientStatisticType"/> for the statistic.</param> |
| | | 54 | | /// <param name="id">A dash-delimited identifier for the statistic.</param> |
| | | 55 | | /// <param name="name">A name for the statistic, presumably to use as a chart title.</param> |
| | | 56 | | /// <param name="description">A human-readable description for the statistic.</param> |
| | | 57 | | /// <param name="replaceIfAlreadyExists">true to use a new statistic even if one already exists, false to return an |
| | | 58 | | /// <param name="initialValue">The initial value for the statistic, if it is created.</param> |
| | | 59 | | /// <param name="minimumValue">An optional value indicating the minimum possible value, if applicable.</param> |
| | | 60 | | /// <param name="maximumValue">An optional value indicating the maximum possible value, if applicable.</param> |
| | | 61 | | /// <param name="temporalAggregationTypes">A set of <see cref="AggregationTypes"/> indicating how this statistic sho |
| | | 62 | | /// <param name="spatialAggregationTypes">A set of <see cref="AggregationTypes"/> indicating how this statistic shou |
| | | 63 | | /// <param name="preferredTemporalAggregationType">A single <see cref="AggregationTypes"/> indicating the default wa |
| | | 64 | | /// <param name="preferredSpatialAggregationType">A single <see cref="AggregationTypes"/> indicating the default way |
| | | 65 | | /// <param name="missingSampleHandling">A <see cref="MissingSampleHandling"/> indicating how clients should treat mi |
| | | 66 | | /// <returns>An <see cref="IAmbientStatistic"/> the caller can use to update the statistic samples.</returns> |
| | | 67 | | IAmbientStatistic GetOrAddTimeBasedStatistic(AmbientStatisticType type, string id, string name, string description, |
| | | 68 | | , long initialValue = 0, long? minimumValue = null, long? maximumValue = null |
| | | 69 | | , AggregationTypes temporalAggregationTypes = AggregationTypes.None |
| | | 70 | | , AggregationTypes spatialAggregationTypes = AggregationTypes.None |
| | | 71 | | , AggregationTypes preferredTemporalAggregationType = AggregationTypes.None |
| | | 72 | | , AggregationTypes preferredSpatialAggregationType = AggregationTypes.None |
| | | 73 | | , MissingSampleHandling missingSampleHandling = MissingSampleHandling.LinearEstimation |
| | | 74 | | ); |
| | | 75 | | /// <summary> |
| | | 76 | | /// Adds or updates a statistic with the specified identifier, description, and properties. |
| | | 77 | | /// </summary> |
| | | 78 | | /// <param name="type">The <see cref="AmbientStatisticType"/> for the statistic.</param> |
| | | 79 | | /// <param name="id">A dash-delimited identifier for the statistic.</param> |
| | | 80 | | /// <param name="name">A name for the statistic, presumably to use as a chart title.</param> |
| | | 81 | | /// <param name="description">A human-readable description for the statistic.</param> |
| | | 82 | | /// <param name="replaceIfAlreadyExists">true to use a new statistic even if one already exists, false to return an |
| | | 83 | | /// <param name="initialValue">The initial value for the statistic, if it is created.</param> |
| | | 84 | | /// <param name="minimumValue">An optional value indicating the minimum possible value, if applicable.</param> |
| | | 85 | | /// <param name="maximumValue">An optional value indicating the maximum possible value, if applicable.</param> |
| | | 86 | | /// <param name="units">An optional string describing the units of the statistic (after the decimal point is adjuste |
| | | 87 | | /// <param name="fixedFloatingPointAdjustment">An optional value to divide raw samples by to get the floating-point |
| | | 88 | | /// <param name="temporalAggregationTypes">A set of <see cref="AggregationTypes"/> indicating how this statistic sho |
| | | 89 | | /// <param name="spatialAggregationTypes">A set of <see cref="AggregationTypes"/> indicating how this statistic shou |
| | | 90 | | /// <param name="preferredTemporalAggregationType">A single <see cref="AggregationTypes"/> indicating the default wa |
| | | 91 | | /// <param name="preferredSpatialAggregationType">A single <see cref="AggregationTypes"/> indicating the default way |
| | | 92 | | /// <param name="missingSampleHandling">A <see cref="MissingSampleHandling"/> indicating how clients should treat mi |
| | | 93 | | /// <returns>An <see cref="IAmbientStatistic"/> the caller can use to update the statistic samples.</returns> |
| | | 94 | | IAmbientStatistic GetOrAddStatistic(AmbientStatisticType type, string id, string name, string description, bool repl |
| | | 95 | | , long initialValue = 0, long? minimumValue = null, long? maximumValue = null |
| | | 96 | | , string? units = null, double fixedFloatingPointAdjustment = 1.0 |
| | | 97 | | , AggregationTypes temporalAggregationTypes = AggregationTypes.None |
| | | 98 | | , AggregationTypes spatialAggregationTypes = AggregationTypes.None |
| | | 99 | | , AggregationTypes preferredTemporalAggregationType = AggregationTypes.None |
| | | 100 | | , AggregationTypes preferredSpatialAggregationType = AggregationTypes.None |
| | | 101 | | , MissingSampleHandling missingSampleHandling = MissingSampleHandling.LinearEstimation |
| | | 102 | | ); |
| | | 103 | | /// <summary> |
| | | 104 | | /// Removes the specified statistic if it exists. |
| | | 105 | | /// </summary> |
| | | 106 | | /// <returns>Whether or not the statistic was successfully removed.</returns> |
| | | 107 | | /// <remarks> |
| | | 108 | | /// Note that the ExecutionTime statistic cannot be removed and will return false from this function. |
| | | 109 | | /// </remarks> |
| | | 110 | | bool RemoveStatistic(string id); |
| | | 111 | | /// <summary> |
| | | 112 | | /// Adds or updates a ratio statistic with the specified identifier, description, and statistics. |
| | | 113 | | /// </summary> |
| | | 114 | | /// <param name="id">A dash-delimited identifier for the statistic.</param> |
| | | 115 | | /// <param name="name">A name for the statistic, presumably to use as a chart title.</param> |
| | | 116 | | /// <param name="description">A human-readable description for the statistic.</param> |
| | | 117 | | /// <param name="replaceIfAlreadyExists">true to use a new statistic even if one already exists, false to return an |
| | | 118 | | /// <param name="units">An optional string describing the units of the statistic (after the decimal point is adjuste |
| | | 119 | | /// <param name="numeratorStatistic">The ID of the numerator statistic. The constant 1 will be used if null.</param |
| | | 120 | | /// <param name="numeratorDelta">Whether or not the numerator should be the difference over time.</param> |
| | | 121 | | /// <param name="denominatorStatistic">The ID of the denominator statistic. The constant 1 will be used if null.</p |
| | | 122 | | /// <param name="denominatorDelta">Whether or not the denominator should be the difference over time.</param> |
| | | 123 | | /// <returns>An <see cref="IAmbientRatioStatistic"/> the caller can use to access the ratio statistic data.</returns |
| | | 124 | | IAmbientRatioStatistic GetOrAddRatioStatistic(string id, string name, string description, bool replaceIfAlreadyExist |
| | | 125 | | , string? numeratorStatistic = null, bool numeratorDelta = true |
| | | 126 | | , string? denominatorStatistic = null, bool denominatorDelta = true |
| | | 127 | | ); |
| | | 128 | | /// <summary> |
| | | 129 | | /// Removes the specified ratio statistic if it exists. |
| | | 130 | | /// </summary> |
| | | 131 | | /// <returns>Whether or not the ratio statistic was successfully removed.</returns> |
| | | 132 | | bool RemoveRatioStatistic(string id); |
| | | 133 | | } |
| | | 134 | | /// <summary> |
| | | 135 | | /// An enumeration of the types of statistics that can be collected. |
| | | 136 | | /// </summary> |
| | | 137 | | public enum AmbientStatisticType |
| | | 138 | | { |
| | | 139 | | /// <summary> |
| | | 140 | | /// A raw statistic is one that is not cumulative, that usually uses <see cref="IAmbientStatistic.SetRawValue"/> for |
| | | 141 | | /// </summary> |
| | | 142 | | Raw, |
| | | 143 | | /// <summary> |
| | | 144 | | /// A cumulative statistic is one that is added to over time, using <see cref="IAmbientStatistic.IncrementRaw"/> or |
| | | 145 | | /// </summary> |
| | | 146 | | Cumulative, |
| | | 147 | | /// <summary> |
| | | 148 | | /// A min statistic is one that always uses <see cref="IAmbientStatistic.SetRawMin"/> for updates. |
| | | 149 | | /// </summary> |
| | | 150 | | Min, |
| | | 151 | | /// <summary> |
| | | 152 | | /// A min statistic is one that always uses <see cref="IAmbientStatistic.SetRawMax"/> for updates. |
| | | 153 | | /// </summary> |
| | | 154 | | Max, |
| | | 155 | | } |
| | | 156 | | /// <summary> |
| | | 157 | | /// An enumeration of ways to aggregate statistic data. |
| | | 158 | | /// </summary> |
| | | 159 | | [Flags] |
| | | 160 | | public enum AggregationTypes |
| | | 161 | | { |
| | | 162 | | /// <summary> |
| | | 163 | | /// Since not aggregating doesn't ever make sense, we use this as the default value to do aggregation based on the < |
| | | 164 | | /// See remarks for details about temporal and spatial aggregation defaults for each type. |
| | | 165 | | /// </summary> |
| | | 166 | | /// <remarks> |
| | | 167 | | /// For <see cref="AmbientStatisticType.Raw"/>, temporal aggregation is <see cref="Average"/> and spatial aggregatio |
| | | 168 | | /// For <see cref="AmbientStatisticType.Cumulative"/>, temporal aggregation is <see cref="MostRecent"/> and spatial |
| | | 169 | | /// For <see cref="AmbientStatisticType.Min"/>, temporal aggregation is <see cref="Min"/> and spatial aggregation is |
| | | 170 | | /// For <see cref="AmbientStatisticType.Max"/>, temporal aggregation is <see cref="Max"/> and spatial aggregation is |
| | | 171 | | /// </remarks> |
| | | 172 | | None = 0, |
| | | 173 | | /// <summary> |
| | | 174 | | /// The aggregation should sum the values. This type of aggregation would be useful for statistics that count items |
| | | 175 | | /// Such statistics are not recommended unless the available graphing system can't display the change in time for a |
| | | 176 | | /// </summary> |
| | | 177 | | Sum = 1, |
| | | 178 | | /// <summary> |
| | | 179 | | /// The aggregation should average the values. Statistics that use <see cref="IAmbientStatistic.SetRawValue"/> migh |
| | | 180 | | /// </summary> |
| | | 181 | | Average = 2, |
| | | 182 | | /// <summary> |
| | | 183 | | /// The aggregation should take the least of the values. Statistics that use <see cref="IAmbientStatistic.SetRawMin |
| | | 184 | | /// </summary> |
| | | 185 | | Min = 4, |
| | | 186 | | /// <summary> |
| | | 187 | | /// The aggregation should take the greatest of the values. Statistics that use <see cref="IAmbientStatistic.SetRaw |
| | | 188 | | /// </summary> |
| | | 189 | | Max = 8, |
| | | 190 | | /// <summary> |
| | | 191 | | /// The aggregation should take the most recent value. Statistics that use <see cref="IAmbientStatistic.SetRawValue |
| | | 192 | | /// For spatial aggregation, this would only be useful if every system is reporting some value from a shared externa |
| | | 193 | | /// </summary> |
| | | 194 | | MostRecent = 16, |
| | | 195 | | } |
| | | 196 | | /// <summary> |
| | | 197 | | /// An enumeration indicating how missing (null) samples should be handled, usually on the client side (perhaps on the s |
| | | 198 | | /// </summary> |
| | | 199 | | public enum MissingSampleHandling |
| | | 200 | | { |
| | | 201 | | /// <summary> |
| | | 202 | | /// When samples get missed, the missing samples should just be ignored. This is useful when you want to see missin |
| | | 203 | | /// </summary> |
| | | 204 | | Skip, |
| | | 205 | | /// <summary> |
| | | 206 | | /// When samples get missed, the missing samples should be filled in with zeros. |
| | | 207 | | /// </summary> |
| | | 208 | | Zero, |
| | | 209 | | /// <summary> |
| | | 210 | | /// When samples get missed, the missing values should be filled in using linear estimation. This is the default ty |
| | | 211 | | /// </summary> |
| | | 212 | | LinearEstimation, |
| | | 213 | | /// <summary> |
| | | 214 | | /// When samples get missed, the missing values should be filled in using exponential estimation. |
| | | 215 | | /// </summary> |
| | | 216 | | ExponentialEstimation, |
| | | 217 | | /// <summary> |
| | | 218 | | /// When samples get missed, the missing values should be filled in using logarithmic estimation. |
| | | 219 | | /// </summary> |
| | | 220 | | LogarithmicEstimation, |
| | | 221 | | } |
| | | 222 | | /// <summary> |
| | | 223 | | /// An interface that gives read access to a single statistic. |
| | | 224 | | /// Note that many user-facing statistics will naturally be a ratio of the samples of two statistics or the changes in t |
| | | 225 | | /// </summary> |
| | | 226 | | /// <remarks> |
| | | 227 | | /// <pitch>The read side of one statistic: the current raw sample plus the immutable metadata a consumer needs to displa |
| | | 228 | | /// <pledge> |
| | | 229 | | /// Everything except <see cref="CurrentRawValue"/> is immutable for the life of the statistic. <see cref="CurrentRawVa |
| | | 230 | | /// The aggregation-type and missing-sample-handling properties are advice to consumers, not behavior of this object — t |
| | | 231 | | /// </pledge> |
| | | 232 | | /// </remarks> |
| | | 233 | | public interface IAmbientStatisticReader |
| | | 234 | | { |
| | | 235 | | /// <summary> |
| | | 236 | | /// Gets the <see cref="IAmbientStatistics"/> this statistic belongs to. |
| | | 237 | | /// </summary> |
| | | 238 | | IAmbientStatistics StatisticsSet { get; } |
| | | 239 | | /// <summary> |
| | | 240 | | /// Gets the <see cref="AmbientStatisticType"/> for the statistic. Immutable. |
| | | 241 | | /// </summary> |
| | | 242 | | AmbientStatisticType StatisticType { get; } |
| | | 243 | | /// <summary> |
| | | 244 | | /// Gets the identifier for the statistic. |
| | | 245 | | /// The identifier should be a dash-delimited path identifying the data. Immutable. |
| | | 246 | | /// </summary> |
| | | 247 | | string Id { get; } |
| | | 248 | | /// <summary> |
| | | 249 | | /// Gets a human-readable name, presumably for the chart title. Should describe the adjusted values, not the raw va |
| | | 250 | | /// </summary> |
| | | 251 | | string Name { get; } |
| | | 252 | | /// <summary> |
| | | 253 | | /// Gets a human-readable description of this statistic. Should describe the adjusted values, not the raw values. |
| | | 254 | | /// </summary> |
| | | 255 | | string Description { get; } |
| | | 256 | | /// <summary> |
| | | 257 | | /// Gets the current statistic sample value. Thread-safe, possibly interlocked. |
| | | 258 | | /// </summary> |
| | | 259 | | long CurrentRawValue { get; } |
| | | 260 | | /// <summary> |
| | | 261 | | /// The expected minimum value, if any. Null if there is no expected minimum. Immutable. |
| | | 262 | | /// </summary> |
| | | 263 | | long? ExpectedMinimumRawValue { get; } |
| | | 264 | | /// <summary> |
| | | 265 | | /// The expected maximum value, if any. Null if there is no expected maximum. Immutable. |
| | | 266 | | /// </summary> |
| | | 267 | | long? ExpectedMaximumRawValue { get; } |
| | | 268 | | /// <summary> |
| | | 269 | | /// Gets an optional human-readable units name, presumably for the y-axis of the chart. |
| | | 270 | | /// Assumes that the numbers in the axis have already been divided by <see cref="FixedFloatingPointAdjustment"/>. |
| | | 271 | | /// Immutable. |
| | | 272 | | /// </summary> |
| | | 273 | | string? AdjustedUnits { get; } |
| | | 274 | | /// <summary> |
| | | 275 | | /// The number used to divide <see cref="CurrentRawValue"/> to adjust the integer sample into a floating point numbe |
| | | 276 | | /// Immutable. |
| | | 277 | | /// </summary> |
| | | 278 | | double FixedFloatingPointAdjustment { get; } |
| | | 279 | | /// <summary> |
| | | 280 | | /// The types of aggregation that should be used when aggregating samples over time. |
| | | 281 | | /// Immutable. |
| | | 282 | | /// </summary> |
| | | 283 | | AggregationTypes TemporalAggregationTypes { get; } |
| | | 284 | | /// <summary> |
| | | 285 | | /// The types of aggregation that should be used when aggregating samples from different systems. |
| | | 286 | | /// Immutable. |
| | | 287 | | /// </summary> |
| | | 288 | | AggregationTypes SpatialAggregationTypes { get; } |
| | | 289 | | /// <summary> |
| | | 290 | | /// The type of aggregation that should be used when aggregating samples over time and only one aggregation can be k |
| | | 291 | | /// Immutable. |
| | | 292 | | /// </summary> |
| | | 293 | | AggregationTypes PreferredTemporalAggregationType { get; } |
| | | 294 | | /// <summary> |
| | | 295 | | /// The type of aggregation that should be used when aggregating samples from different systems and only one aggrega |
| | | 296 | | /// Immutable. |
| | | 297 | | /// </summary> |
| | | 298 | | AggregationTypes PreferredSpatialAggregationType { get; } |
| | | 299 | | /// <summary> |
| | | 300 | | /// How missing samples should be handled. |
| | | 301 | | /// Immutable. |
| | | 302 | | /// </summary> |
| | | 303 | | MissingSampleHandling MissingSampleHandling { get; } |
| | | 304 | | } |
| | | 305 | | /// <summary> |
| | | 306 | | /// An interface that gives write access to a single statistic. |
| | | 307 | | /// Implementations are disposable, but should not throw exceptions if methods are called after disposal. |
| | | 308 | | /// Disposability is meant to stop reporting results. |
| | | 309 | | /// </summary> |
| | | 310 | | /// <remarks> |
| | | 311 | | /// <pitch>The write side of one statistic: atomic raw-sample updates cheap enough to call on every operation, in whiche |
| | | 312 | | /// <pledge><see cref="IAmbientStatisticReader"/></pledge> |
| | | 313 | | /// <pledge> |
| | | 314 | | /// All updates are atomic and thread-safe, and concurrent updates never lose each other's effects (min/max updates only |
| | | 315 | | /// Disposal deregisters the statistic from its <see cref="IAmbientStatistics"/> to stop reporting; calling any member a |
| | | 316 | | /// </pledge> |
| | | 317 | | /// </remarks> |
| | | 318 | | public interface IAmbientStatistic : IAmbientStatisticReader, IDisposable |
| | | 319 | | { |
| | | 320 | | /// <summary> |
| | | 321 | | /// Increments the raw statistic sample value. This value is not adjusted by <see cref="IAmbientStatisticReader.Fix |
| | | 322 | | /// </summary> |
| | | 323 | | /// <returns>The incremented sample value.</returns> |
| | | 324 | | long IncrementRaw(); |
| | | 325 | | /// <summary> |
| | | 326 | | /// Decrements the raw statistic sample value. This value is not adjusted by <see cref="IAmbientStatisticReader.Fix |
| | | 327 | | /// </summary> |
| | | 328 | | /// <returns>The decremented sample value.</returns> |
| | | 329 | | long DecrementRaw(); |
| | | 330 | | /// <summary> |
| | | 331 | | /// Adds to the raw statistic sample value. This value is not adjusted by <see cref="IAmbientStatisticReader.FixedF |
| | | 332 | | /// </summary> |
| | | 333 | | /// <param name="addend">The amount to add to the statistic sample value.</param> |
| | | 334 | | /// <returns>The new sample value.</returns> |
| | | 335 | | long AddRaw(long addend); |
| | | 336 | | /// <summary> |
| | | 337 | | /// Sets the raw integer statistic sample value. This value is not adjusted by <see cref="IAmbientStatisticReader.F |
| | | 338 | | /// </summary> |
| | | 339 | | /// <param name="newValue">The new value to use.</param> |
| | | 340 | | void SetRawValue(long newValue); |
| | | 341 | | /// <summary> |
| | | 342 | | /// Sets the raw statistic sample minimum value. This value is not adjusted by <see cref="IAmbientStatisticReader.F |
| | | 343 | | /// </summary> |
| | | 344 | | /// <param name="newPossibleMinValue">A value which will be the new sample value if it is smaller than the current s |
| | | 345 | | /// <returns>The new sample value.</returns> |
| | | 346 | | long SetRawMin(long newPossibleMinValue); |
| | | 347 | | /// <summary> |
| | | 348 | | /// Sets the raw statistic sample maximum value. This value is not adjusted by <see cref="IAmbientStatisticReader.F |
| | | 349 | | /// </summary> |
| | | 350 | | /// <param name="newPossibleMaxValue">A value which will be the new sample value if it is larger than the current sa |
| | | 351 | | /// <returns>The new sample value.</returns> |
| | | 352 | | long SetRawMax(long newPossibleMaxValue); |
| | | 353 | | } |
| | | 354 | | /// <summary> |
| | | 355 | | /// An interface that indicates that a useful statistic exists that is the ratio of two other statistics or the change o |
| | | 356 | | /// These statistics should not be recorded independently, but should be recorded as a ratio of the two specified statis |
| | | 357 | | /// Using ratio statistics instead of computing the ratio on the server prevents the need to send the raw data to the cl |
| | | 358 | | /// For example, if a statistic for the average request processing time is computed on the server, aggregating that rati |
| | | 359 | | /// Using ratio statistics to compute the ratios on the aggregated raw data weights all the requests equally, whereas us |
| | | 360 | | /// Implementations are disposable, but should not throw exceptions if methods are called after disposal. |
| | | 361 | | /// Disposability is meant to stop reporting results. |
| | | 362 | | /// </summary> |
| | | 363 | | /// <remarks> |
| | | 364 | | /// <pitch>A declaration that a useful user-facing value is the ratio of two statistics (or of their changes over time) |
| | | 365 | | /// <pledge> |
| | | 366 | | /// A pure descriptor: it carries no samples and performs no computation — consumers resolve the numerator and denominat |
| | | 367 | | /// All properties are immutable; disposal deregisters the descriptor and must not throw afterwards. |
| | | 368 | | /// </pledge> |
| | | 369 | | /// </remarks> |
| | | 370 | | public interface IAmbientRatioStatistic : IDisposable |
| | | 371 | | { |
| | | 372 | | /// <summary> |
| | | 373 | | /// Gets the <see cref="IAmbientStatistics"/> this statistic belongs to. |
| | | 374 | | /// </summary> |
| | | 375 | | IAmbientStatistics StatisticsSet { get; } |
| | | 376 | | /// <summary> |
| | | 377 | | /// Gets the identifier for the statistic. |
| | | 378 | | /// The identifier should be a dash-delimited path identifying the data. Immutable. |
| | | 379 | | /// </summary> |
| | | 380 | | string Id { get; } |
| | | 381 | | /// <summary> |
| | | 382 | | /// Gets a human-readable name, presumably for the chart title. Should describe the adjusted values, not the raw va |
| | | 383 | | /// </summary> |
| | | 384 | | string Name { get; } |
| | | 385 | | /// <summary> |
| | | 386 | | /// Gets a human-readable description of this statistic. Should describe the adjusted values, not the raw values. |
| | | 387 | | /// </summary> |
| | | 388 | | string Description { get; } |
| | | 389 | | /// <summary> |
| | | 390 | | /// Gets an optional human-readable units name, presumably for the y-axis of the chart. |
| | | 391 | | /// Immutable. |
| | | 392 | | /// </summary> |
| | | 393 | | string? AdjustedUnits { get; } |
| | | 394 | | /// <summary> |
| | | 395 | | /// Gets the ID of the numerator statistic. Use the constant 1 (and ignore <see cref="NumeratorDelta"/>) if null. |
| | | 396 | | /// </summary> |
| | | 397 | | string? NumeratorStatisticId { get; } |
| | | 398 | | /// <summary> |
| | | 399 | | /// The numerator should be the change in the numerator statistic over time rather than the raw value. Immutable. |
| | | 400 | | /// </summary> |
| | | 401 | | bool NumeratorDelta { get; } |
| | | 402 | | /// <summary> |
| | | 403 | | /// Gets the ID of the denominator statistic, often the built-in "ExecutionTime" statistic. Use 1 (and ignore <see |
| | | 404 | | /// </summary> |
| | | 405 | | string? DenominatorStatisticId { get; } |
| | | 406 | | /// <summary> |
| | | 407 | | /// The denominator should be the change in the numerator statistic over time rather than the raw value. Immutable. |
| | | 408 | | /// </summary> |
| | | 409 | | bool DenominatorDelta { get; } |
| | | 410 | | } |
| | | 411 | | |
| | | 412 | | /// <summary> |
| | | 413 | | /// A class that contains extension methods for various statistics interfaces that add functions to aggregate statistic |
| | | 414 | | /// </summary> |
| | | 415 | | /// <remarks> |
| | | 416 | | /// <pitch>The client-side math for statistics: set adjusted (display-unit) values without hand-multiplying by the fixed |
| | | 417 | | /// <pledge> |
| | | 418 | | /// Adjusted-value setters convert display units to raw samples using the statistic's own adjustment factor and are as t |
| | | 419 | | /// Aggregation treats a <see cref="AggregationTypes.None"/> preference as "use the default for the statistic's type"; S |
| | | 420 | | /// </pledge> |
| | | 421 | | /// </remarks> |
| | | 422 | | public static class IAmbientStatisticsExtensions |
| | | 423 | | { |
| | | 424 | | /// <summary> |
| | | 425 | | /// Sets the statistic sample value in the units specified by <see cref="IAmbientStatisticReader.AdjustedUnits"/>, a |
| | | 426 | | /// </summary> |
| | | 427 | | /// <param name="statistic">The <see cref="IAmbientStatistic"/> whose value should be set.</param> |
| | | 428 | | /// <param name="newValue">The new value to use.</param> |
| | | 429 | | public static void SetValue(this IAmbientStatistic statistic, long newValue) |
| | | 430 | | { |
| | 2 | 431 | | if (statistic == null) throw new ArgumentNullException(nameof(statistic)); |
| | 2 | 432 | | statistic.SetRawValue((long)(newValue * statistic.FixedFloatingPointAdjustment)); |
| | 2 | 433 | | } |
| | | 434 | | /// <summary> |
| | | 435 | | /// Sets the statistic sample value in the units specified by <see cref="IAmbientStatisticReader.AdjustedUnits"/>, a |
| | | 436 | | /// </summary> |
| | | 437 | | /// <param name="statistic">The <see cref="IAmbientStatistic"/> whose value should be set.</param> |
| | | 438 | | /// <param name="newValue">The new value to use.</param> |
| | | 439 | | public static void SetValue(this IAmbientStatistic statistic, float newValue) |
| | | 440 | | { |
| | 2 | 441 | | if (statistic == null) throw new ArgumentNullException(nameof(statistic)); |
| | 2 | 442 | | statistic.SetRawValue((long)(newValue * statistic.FixedFloatingPointAdjustment)); |
| | 2 | 443 | | } |
| | | 444 | | /// <summary> |
| | | 445 | | /// Sets the statistic sample value in the units specified by <see cref="IAmbientStatisticReader.AdjustedUnits"/>, a |
| | | 446 | | /// </summary> |
| | | 447 | | /// <param name="statistic">The <see cref="IAmbientStatistic"/> whose value should be set.</param> |
| | | 448 | | /// <param name="newValue">The new value to use.</param> |
| | | 449 | | public static void SetValue(this IAmbientStatistic statistic, double newValue) |
| | | 450 | | { |
| | 2 | 451 | | if (statistic == null) throw new ArgumentNullException(nameof(statistic)); |
| | 2 | 452 | | statistic.SetRawValue((long)(newValue * statistic.FixedFloatingPointAdjustment)); |
| | 2 | 453 | | } |
| | | 454 | | /// <summary> |
| | | 455 | | /// Uses the preferred aggregation type to aggregate samples from a time range. |
| | | 456 | | /// </summary> |
| | | 457 | | /// <param name="reader">The reader to use to aggregate the samples.</param> |
| | | 458 | | /// <param name="samples">An enumeration of samples to aggregate.</param> |
| | | 459 | | /// <returns>The preferred temporally aggregated sample.</returns> |
| | | 460 | | public static long? PreferredTemporalAggregation(this IAmbientStatisticReader reader, IEnumerable<long?> samples) |
| | | 461 | | { |
| | 2 | 462 | | if (reader == null) throw new ArgumentNullException(nameof(reader)); |
| | 2 | 463 | | AggregationTypes types = reader.PreferredTemporalAggregationType; |
| | 2 | 464 | | if (types == AggregationTypes.None) types = DefaultTemporalAggregation(reader.StatisticType); |
| | 2 | 465 | | return types.Aggregate(samples); |
| | | 466 | | } |
| | | 467 | | /// <summary> |
| | | 468 | | /// Uses the preferred aggregation type to aggregate samples from different systems. |
| | | 469 | | /// </summary> |
| | | 470 | | /// <param name="reader">The reader to use to aggregate the samples.</param> |
| | | 471 | | /// <param name="samples">An enumeration of samples to aggregate.</param> |
| | | 472 | | /// <returns>The preferred temporally aggregated sample.</returns> |
| | | 473 | | public static long? PreferredSpatialAggregation(this IAmbientStatisticReader reader, IEnumerable<long?> samples) |
| | | 474 | | { |
| | 2 | 475 | | if (reader == null) throw new ArgumentNullException(nameof(reader)); |
| | 2 | 476 | | AggregationTypes types = reader.PreferredSpatialAggregationType; |
| | 2 | 477 | | if (types == AggregationTypes.None) types = DefaultSpatialAggregation(reader.StatisticType); |
| | 2 | 478 | | return types.Aggregate(samples); |
| | | 479 | | } |
| | | 480 | | /// <summary> |
| | | 481 | | /// Uses the specified aggregation type to aggregate samples. |
| | | 482 | | /// </summary> |
| | | 483 | | /// <param name="type">The <see cref="AggregationTypes"/> to use to aggregate the samples (only one may be set).</pa |
| | | 484 | | /// <param name="samples">An enumeration of samples to aggregate.</param> |
| | | 485 | | /// <returns>The aggregated sample.</returns> |
| | | 486 | | public static long? Aggregate(this AggregationTypes type, IEnumerable<long?> samples) |
| | | 487 | | { |
| | 2 | 488 | | if (samples == null) throw new ArgumentNullException(nameof(samples)); |
| | 2 | 489 | | return type switch |
| | 2 | 490 | | { |
| | 2 | 491 | | AggregationTypes.Average => Average(samples), |
| | 2 | 492 | | AggregationTypes.Min => samples.Min(), |
| | 2 | 493 | | AggregationTypes.Max => samples.Max(), |
| | 2 | 494 | | AggregationTypes.MostRecent => samples.LastOrDefault(), |
| | 2 | 495 | | _ => SumWithCutoff(samples), |
| | 2 | 496 | | }; |
| | | 497 | | } |
| | | 498 | | private static long? Average(IEnumerable<long?> samples) |
| | | 499 | | { |
| | 2 | 500 | | double? avg = samples.Select(l => (double?)l).Average(); |
| | 2 | 501 | | return (avg == null) ? null : (long?)Math.Round(avg.Value); |
| | | 502 | | } |
| | | 503 | | private static long? SumWithCutoff(IEnumerable<long?> samples) |
| | | 504 | | { |
| | 2 | 505 | | double? sum = null; |
| | 2 | 506 | | foreach (long? l in samples) |
| | | 507 | | { |
| | 2 | 508 | | if (l != null) sum = (sum ?? 0) + l.Value; |
| | | 509 | | } |
| | 2 | 510 | | if (sum > long.MaxValue) return long.MaxValue; |
| | 2 | 511 | | return (long?)sum; |
| | | 512 | | } |
| | | 513 | | /// <summary> |
| | | 514 | | /// Gets the default temporal (over time) aggregation type for the specified statistic type. |
| | | 515 | | /// </summary> |
| | | 516 | | /// <param name="statisicType">The <see cref="AmbientStatisticType"/> for the statistic.</param> |
| | | 517 | | /// <returns>The default <see cref="AggregationTypes"/> for aggregating samples for specified statistic over time.</ |
| | | 518 | | public static AggregationTypes DefaultTemporalAggregation(this AmbientStatisticType statisicType) |
| | | 519 | | { |
| | 2 | 520 | | return statisicType switch |
| | 2 | 521 | | { |
| | 2 | 522 | | AmbientStatisticType.Raw => AggregationTypes.Average, |
| | 2 | 523 | | AmbientStatisticType.Cumulative => AggregationTypes.MostRecent, |
| | 2 | 524 | | AmbientStatisticType.Min => AggregationTypes.Min, |
| | 2 | 525 | | AmbientStatisticType.Max => AggregationTypes.Max, |
| | 2 | 526 | | _ => AggregationTypes.Average, |
| | 2 | 527 | | }; |
| | | 528 | | } |
| | | 529 | | /// <summary> |
| | | 530 | | /// Gets the default spatial (cross-system) aggregation type for the specified statistic type. |
| | | 531 | | /// </summary> |
| | | 532 | | /// <param name="statisicType">The <see cref="AmbientStatisticType"/> for the statistic.</param> |
| | | 533 | | /// <returns>The default <see cref="AggregationTypes"/> for aggregating samples for specified statistic across syste |
| | | 534 | | public static AggregationTypes DefaultSpatialAggregation(this AmbientStatisticType statisicType) |
| | | 535 | | { |
| | 2 | 536 | | return statisicType switch |
| | 2 | 537 | | { |
| | 2 | 538 | | AmbientStatisticType.Raw => AggregationTypes.Average, |
| | 2 | 539 | | AmbientStatisticType.Cumulative => AggregationTypes.Average, |
| | 2 | 540 | | AmbientStatisticType.Min => AggregationTypes.Min, |
| | 2 | 541 | | AmbientStatisticType.Max => AggregationTypes.Max, |
| | 2 | 542 | | _ => AggregationTypes.Average, |
| | 2 | 543 | | }; |
| | | 544 | | } |
| | | 545 | | } |